Использование модуля scikit-learn для прогнозирования временных рядов

В модуле Python scikit-learn реализованы основные методы машинного обучения (machine learning).

Для установки модуля scikit-learn надо выполнить в командной строке:
pip install -U scikit-learn

Теперь попробуем использовать некоторые методы machine learning для прогнозирования временных рядов. Для работы с Python удобно использовать IPython Notebook.

Сначала импортируем стандартные модули:

import datetime
import numpy as np
import pandas as pd
import sklearn
from pandas.io.data import DataReader

Далее нам придётся выводить широкие таблицы, поэтому подправим опции, которые позволят увеличить ширину рабочего поля IPython Notebook:

pd.set_option('display.max_columns', 50)
pd.set_option('display.width', 500)

Теперь можно скачать с Yahoo Finance данные по какому-нибудь финансовому инструменту, например, фьючерсу на индекс S&P500. Для работы с датами используем стандартный класс datetime; для скачивания данных – функцию DataReader из модуля pandas.

symbol = "^GSPC"  # Код финансового инструмента на сайте Yahoo Finance
start_date = datetime.datetime(2013, 7, 1) # Начальная дата - 1 июля 2013
end_date = datetime.datetime(2014, 4, 30)  # Конечная дата - 30 апреля 2014
ts1 = DataReader(symbol, "yahoo", start_date, end_date)
print ts1.head(3) # Вывод первых трёх строк таблицы
print ts1.tail(2) # Вывод последних двух строк таблицы
               Open     High      Low    Close      Volume  Adj Close
Date
2013-07-01  1609.78  1626.61  1609.78  1614.96  3104690000    1614.96
2013-07-02  1614.29  1624.26  1606.77  1614.08  3317130000    1614.08
2013-07-03  1611.48  1618.97  1604.57  1615.41  1966050000    1615.41

[3 rows x 6 columns]
               Open    High      Low    Close      Volume  Adj Close
Date
2014-04-29  1870.78  1880.6  1870.78  1878.33  3647820000    1878.33
2014-04-30  1877.10  1885.2  1872.69  1883.95  3779230000    1883.95

[2 rows x 6 columns]

В каждой строке хранится дата (Date) в формате год-месяц-день; цена открытия (Open); максимальная (High) и минимальная цена (Low), достигнутая за данный день; цена закрытия (Close); биржевой объём (Volume) и скорректированная цена закрытия (Adjusted Close). Последний столбец учитывает разделение акций и выплату дивидендов; для индексов и валют (а также фьючерсов на них) значения в последнем столбце совпадают со значениями в столбце Close.

Для доступа к каждой строке таблицы можно использовать метод ix:

print ts1.ix['2014-03-10']
Open         1.877860e+03
High         1.877870e+03
Low          1.867040e+03
Close        1.877170e+03
Volume       3.021350e+09
Adj Close    1.877170e+03
Name: 2014-03-10 00:00:00, dtype: float64

Создадим новый набор данных, с тем же количеством строк, но нам нужны только цены закрытия и объёмы:

ts2 = pd.DataFrame(index=ts1.index)
ts2["Today"] = ts1["Adj Close"]
ts2["Volume"] = ts1["Volume"]
print ts2.head(3)
              Today      Volume
Date
2013-07-01  1614.96  3104690000
2013-07-02  1614.08  3317130000
2013-07-03  1615.41  1966050000

[3 rows x 2 columns]

Добавим к новому набору данных 2 столбца, которые будут хранить задержанные цены закрытия, сдвинутые на 1 день (столбец Lag1) и на 2 дня (Lag2):

count = 2
for i in xrange(0, count):
    ts2["Lag%s" % str(i+1)] = ts1["Adj Close"].shift(i+1)

print ts2.head(3)
              Today      Volume     Lag1     Lag2
Date
2013-07-01  1614.96  3104690000      NaN      NaN
2013-07-02  1614.08  3317130000  1614.96      NaN
2013-07-03  1615.41  1966050000  1614.08  1614.96

[3 rows x 4 columns]

Создадим ещё один набор данных, только в каждой \(i\)-й строке вместо цены закрытия \(p_i\) будем хранить изменение этой цены по сравнению с предыдущим днём, выраженное в процентах по отношению к предыдущему дню, по формуле

\[d_i = \frac{p_i - p_{i-1}}{p_{i-1}} \cdot 100\%.\]
ts = pd.DataFrame(index=ts2.index)
ts["Volume"] = ts2["Volume"]
ts["Today"] = ts2["Today"].pct_change()*100.0

Заменим нулевые значения малыми числами (например, 0.0001), т.к. нулевые значения будут мешать работе некоторых алгоритмов обучения (см. ниже).

for i,x in enumerate(ts["Today"]):
        if (abs(x) < 0.0001):
            ts["Today"][i] = 0.0001

print ts.head(3)
                Volume     Today
Date
2013-07-01  3104690000       NaN
2013-07-02  3317130000 -0.054491
2013-07-03  1966050000  0.082400

[3 rows x 2 columns]

Добавим к новому набору данных те же 2 столбца для задержанных данных, но вместо цены закрытия будем хранить процентное изменение. Кроме того, нам потребуется ещё один столбец Direction (Направление): если цена закрытия выросла по сравнению с предыдущим днём, то в этот столбец запишем 1, а если цена закрытия уменьшилась, то значение -1.

for i in xrange(0, count):
    ts["Lag%s" % str(i+1)] = ts2["Lag%s" % str(i+1)].pct_change()*100.0

ts["Direction"] = np.sign(ts["Today"])
ts = ts[count+1:]  # Пропустили первые count дней, т.к. для них некоторые данные не определены (NaN)
print ts.head(7)
                Volume     Today      Lag1      Lag2  Direction
Date
2013-07-05  2634140000  1.020174  0.082400 -0.054491          1
2013-07-08  3514590000  0.525158  1.020174  0.082400          1
2013-07-09  3155360000  0.722968  0.525158  1.020174          1
2013-07-10  3011010000  0.018156  0.722968  0.525158          1
2013-07-11  3446340000  1.355424  0.018156  0.722968          1
2013-07-12  3039070000  0.308653  1.355424  0.018156          1
2013-07-15  2623200000  0.137484  0.308653  1.355424          1

[7 rows x 5 columns]

Разделим наш набор данных на две части: обучающую и тестовую. Первую будем использовать для обучения модели, а вторую – для тестирования результатов.

start_test = datetime.datetime(2014,1,1) # Начало тестового набора данных - 1 января 2014

Для обучения модели будем использовать значения из столбцов Lag1 и Lag2:

cols = []
for i in xrange(0, count):
    cols.extend(["Lag%s" % str(i+1)])

print cols
['Lag1', 'Lag2']

Целевые значения – направления изменения цены закрытия, т.е. значения из столбца Direction.

x = ts[cols] # Входные данные для обучения
y = ts["Direction"]     # Целевые значения

x_train = x[x.index < start_test] # Входные данные для обучения (март)
print x_train[0:5] # Вывели для проверки первые 5 строк обучающей выборки
x_test = x[x.index >= start_test] # Входные данные для теста (апрель)
print x_test[0:5] # Вывели для проверки первые 5 строк тестовой выборки

y_train = y[y.index < start_test] # Целевые значения для обучения (март)
print y_train[0:5]
y_test = y[y.index >= start_test] # Целевые значения для теста (апрель)
print y_test[0:5]

#from sklearn import preprocessing
#scaler = preprocessing.StandardScaler().fit(x_train)
#x_train = scaler.transform(x_train)
#x_test = scaler.transform(x_test)
#print x_train[0:5] # Вывели для проверки первые 5 строк обучающей выборки после масштабирования

d = pd.DataFrame(index=y_test.index) # Набор данных для проверки модели
d["Actual"] = y_test                 # Реальные изменения цен закрытия
                Lag1      Lag2
Date
2013-07-05  0.082400 -0.054491
2013-07-08  1.020174  0.082400
2013-07-09  0.525158  1.020174
2013-07-10  0.722968  0.525158
2013-07-11  0.018156  0.722968

[5 rows x 2 columns]
                Lag1      Lag2
Date
2014-01-02  0.395965 -0.017921
2014-01-03 -0.886191  0.395965
2014-01-06 -0.033297 -0.886191
2014-01-07 -0.251178 -0.033297
2014-01-08  0.608177 -0.251178

[5 rows x 2 columns]
Date
2013-07-05    1
2013-07-08    1
2013-07-09    1
2013-07-10    1
2013-07-11    1
Name: Direction, dtype: float64
Date
2014-01-02   -1
2014-01-03   -1
2014-01-06   -1
2014-01-07    1
2014-01-08   -1
Name: Direction, dtype: float64

Отобразим получившиеся значения на графике: красные точки соответствуют дням, когда цена снижалась, зелёные - когда повышалась.

import matplotlib.pyplot as plt
x_tr = x_train.values[:]
y_tr = y_train.values[:]
print x_tr[0:5]
print y_tr[0:5]

xs = x_tr[:, 0][y_tr == 1]
ys = x_tr[:, 1][y_tr == 1]
plt.scatter(xs, ys, c="green")

xs = x_tr[:, 0][y_tr == -1]
ys = x_tr[:, 1][y_tr == -1]
plt.scatter(xs, ys, c="red")

plt.legend(["Up", "Down"])
plt.xlabel('Lag1')
plt.ylabel('Lag2')
[[ 0.08239988 -0.05449051]
 [ 1.02017444  0.08239988]
 [ 0.52515795  1.02017444]
 [ 0.72296795  0.52515795]
 [ 0.01815629  0.72296795]]
[ 1.  1.  1.  1.  1.]
<matplotlib.text.Text at 0xa08f430>
../_images/scikit-learn-01_25_2.png

Сначала испробуем модель логистической регрессии LogisticRegression:

from sklearn.linear_model import LogisticRegression
model1 = LogisticRegression()
model1.fit(x_train, y_train)     # Обучение (подбор параметров модели)
d['Predict_LR'] = model1.predict(x_test) # Тест

# Считаем процент правильно предсказанных направлений изменения цены:
d["Correct_LR"] = (1.0+d['Predict_LR']*d["Actual"])/2.0
print d
hit_rate1 = np.mean(d["Correct_LR"])
print "Процент верных предсказаний: %.1f%%" % (hit_rate1*100)
            Actual  Predict_LR  Correct_LR
Date
2014-01-02      -1           1           0
2014-01-03      -1          -1           1
2014-01-06      -1           1           0
2014-01-07       1           1           1
2014-01-08      -1           1           0
2014-01-09       1           1           1
2014-01-10       1           1           1
2014-01-13      -1           1           0
2014-01-14       1          -1           0
2014-01-15       1           1           1
2014-01-16      -1           1           0
2014-01-17      -1           1           0
2014-01-21       1           1           1
2014-01-22       1           1           1
2014-01-23      -1           1           0
2014-01-24      -1          -1           1
2014-01-27      -1          -1           1
2014-01-28       1           1           1
2014-01-29      -1           1           0
2014-01-30       1          -1           0
2014-01-31      -1           1           0
2014-02-03      -1          -1           1
2014-02-04       1          -1           0
2014-02-05      -1           1           0
2014-02-06       1           1           1
2014-02-07       1           1           1
2014-02-10       1           1           1
2014-02-11       1           1           1
2014-02-12      -1           1           0
2014-02-13       1           1           1
2014-02-14       1           1           1
2014-02-18       1           1           1
2014-02-19      -1           1           0
2014-02-20       1           1           1
2014-02-21      -1           1           0
2014-02-24       1           1           1
2014-02-25      -1           1           0
2014-02-26       1           1           1
2014-02-27       1           1           1
2014-02-28       1           1           1
2014-03-03      -1           1           0
2014-03-04       1          -1           0
2014-03-05      -1           1           0
2014-03-06       1           1           1
2014-03-07       1           1           1
2014-03-10      -1           1           0
2014-03-11      -1           1           0
2014-03-12       1           1           1
2014-03-13      -1           1           0
2014-03-14      -1          -1           1
2014-03-17       1           1           1
2014-03-18       1           1           1
2014-03-19      -1           1           0
2014-03-20       1          -1           0
2014-03-21      -1           1           0
2014-03-24      -1           1           0
2014-03-25       1           1           1
2014-03-26      -1           1           0
2014-03-27      -1          -1           1
2014-03-28       1           1           1
               ...         ...         ...

[82 rows x 3 columns]
Процент верных предсказаний: 56.1%

Итак, нам удалось в 56 случаях из 100 правильно предсказывать направление движения цены за день.

Теперь попробуем использовать модель LDA (Linear Discriminant Analysis - линейный дискриминантный анализ).

from sklearn.lda import LDA
model2 = LDA()
model2.fit(x_train, y_train)     # Обучение (подбор параметров модели)
d['Predict_LDA'] = model2.predict(x_test) # Тест

# Считаем процент правильно предсказанных направлений изменения цены:
d["Correct_LDA"] = (1.0+d['Predict_LDA']*d["Actual"])/2.0
print d
hit_rate2 = np.mean(d["Correct_LDA"])
print "Процент верных предсказаний: %.1f%%" % (hit_rate2*100)
            Actual  Predict_LR  Correct_LR  Predict_LDA  Correct_LDA
Date
2014-01-02      -1           1           0            1            0
2014-01-03      -1          -1           1           -1            1
2014-01-06      -1           1           0            1            0
2014-01-07       1           1           1            1            1
2014-01-08      -1           1           0            1            0
2014-01-09       1           1           1            1            1
2014-01-10       1           1           1            1            1
2014-01-13      -1           1           0            1            0
2014-01-14       1          -1           0           -1            0
2014-01-15       1           1           1            1            1
2014-01-16      -1           1           0            1            0
2014-01-17      -1           1           0            1            0
2014-01-21       1           1           1            1            1
2014-01-22       1           1           1            1            1
2014-01-23      -1           1           0            1            0
2014-01-24      -1          -1           1           -1            1
2014-01-27      -1          -1           1           -1            1
2014-01-28       1           1           1            1            1
2014-01-29      -1           1           0            1            0
2014-01-30       1          -1           0           -1            0
2014-01-31      -1           1           0            1            0
2014-02-03      -1          -1           1           -1            1
2014-02-04       1          -1           0           -1            0
2014-02-05      -1           1           0            1            0
2014-02-06       1           1           1            1            1
2014-02-07       1           1           1            1            1
2014-02-10       1           1           1            1            1
2014-02-11       1           1           1            1            1
2014-02-12      -1           1           0            1            0
2014-02-13       1           1           1            1            1
2014-02-14       1           1           1            1            1
2014-02-18       1           1           1            1            1
2014-02-19      -1           1           0            1            0
2014-02-20       1           1           1           -1            0
2014-02-21      -1           1           0            1            0
2014-02-24       1           1           1            1            1
2014-02-25      -1           1           0            1            0
2014-02-26       1           1           1            1            1
2014-02-27       1           1           1            1            1
2014-02-28       1           1           1            1            1
2014-03-03      -1           1           0            1            0
2014-03-04       1          -1           0           -1            0
2014-03-05      -1           1           0            1            0
2014-03-06       1           1           1            1            1
2014-03-07       1           1           1            1            1
2014-03-10      -1           1           0            1            0
2014-03-11      -1           1           0            1            0
2014-03-12       1           1           1            1            1
2014-03-13      -1           1           0            1            0
2014-03-14      -1          -1           1           -1            1
2014-03-17       1           1           1            1            1
2014-03-18       1           1           1            1            1
2014-03-19      -1           1           0            1            0
2014-03-20       1          -1           0           -1            0
2014-03-21      -1           1           0            1            0
2014-03-24      -1           1           0            1            0
2014-03-25       1           1           1            1            1
2014-03-26      -1           1           0            1            0
2014-03-27      -1          -1           1           -1            1
2014-03-28       1           1           1            1            1
               ...         ...         ...          ...          ...

[82 rows x 5 columns]
Процент верных предсказаний: 54.9%

Как видим, результат чуть хуже.

Теперь проверим модель QDA (Quadratic discriminant analysis - квадратичный дискриминантный анализ):

from sklearn.qda import QDA
model3 = QDA()
model3.fit(x_train, y_train)     # Обучение (подбор параметров модели)
d['Predict_QDA'] = model3.predict(x_test) # Тест

# Считаем процент правильно предсказанных направлений изменения цены:
d["Correct_QDA"] = (1.0+d['Predict_QDA']*d["Actual"])/2.0
print d
hit_rate3 = np.mean(d["Correct_QDA"])
print "Процент верных предсказаний: %.1f%%" % (hit_rate3*100)
            Actual  Predict_LR  Correct_LR  Predict_LDA  Correct_LDA  Predict_QDA  Correct_QDA
Date
2014-01-02      -1           1           0            1            0            1            0
2014-01-03      -1          -1           1           -1            1            1            0
2014-01-06      -1           1           0            1            0            1            0
2014-01-07       1           1           1            1            1           -1            0
2014-01-08      -1           1           0            1            0            1            0
2014-01-09       1           1           1            1            1           -1            0
2014-01-10       1           1           1            1            1           -1            0
2014-01-13      -1           1           0            1            0            1            0
2014-01-14       1          -1           0           -1            0            1            1
2014-01-15       1           1           1            1            1            1            1
2014-01-16      -1           1           0            1            0            1            0
2014-01-17      -1           1           0            1            0           -1            1
2014-01-21       1           1           1            1            1           -1            0
2014-01-22       1           1           1            1            1            1            1
2014-01-23      -1           1           0            1            0           -1            1
2014-01-24      -1          -1           1           -1            1            1            0
2014-01-27      -1          -1           1           -1            1            1            0
2014-01-28       1           1           1            1            1            1            1
2014-01-29      -1           1           0            1            0            1            0
2014-01-30       1          -1           0           -1            0            1            1
2014-01-31      -1           1           0            1            0            1            0
2014-02-03      -1          -1           1           -1            1            1            0
2014-02-04       1          -1           0           -1            0            1            1
2014-02-05      -1           1           0            1            0            1            0
2014-02-06       1           1           1            1            1            1            1
2014-02-07       1           1           1            1            1            1            1
2014-02-10       1           1           1            1            1            1            1
2014-02-11       1           1           1            1            1            1            1
2014-02-12      -1           1           0            1            0            1            0
2014-02-13       1           1           1            1            1            1            1
2014-02-14       1           1           1            1            1            1            1
2014-02-18       1           1           1            1            1            1            1
2014-02-19      -1           1           0            1            0           -1            1
2014-02-20       1           1           1           -1            0            1            1
2014-02-21      -1           1           0            1            0            1            0
2014-02-24       1           1           1            1            1           -1            0
2014-02-25      -1           1           0            1            0            1            0
2014-02-26       1           1           1            1            1           -1            0
2014-02-27       1           1           1            1            1           -1            0
2014-02-28       1           1           1            1            1            1            1
2014-03-03      -1           1           0            1            0            1            0
2014-03-04       1          -1           0           -1            0            1            1
2014-03-05      -1           1           0            1            0            1            0
2014-03-06       1           1           1            1            1            1            1
2014-03-07       1           1           1            1            1            1            1
2014-03-10      -1           1           0            1            0           -1            1
2014-03-11      -1           1           0            1            0           -1            1
2014-03-12       1           1           1            1            1           -1            0
2014-03-13      -1           1           0            1            0            1            0
2014-03-14      -1          -1           1           -1            1            1            0
2014-03-17       1           1           1            1            1            1            1
2014-03-18       1           1           1            1            1            1            1
2014-03-19      -1           1           0            1            0            1            0
2014-03-20       1          -1           0           -1            0            1            1
2014-03-21      -1           1           0            1            0            1            0
2014-03-24      -1           1           0            1            0           -1            1
2014-03-25       1           1           1            1            1           -1            0
2014-03-26      -1           1           0            1            0            1            0
2014-03-27      -1          -1           1           -1            1            1            0
2014-03-28       1           1           1            1            1            1            1
               ...         ...         ...          ...          ...          ...          ...

[82 rows x 7 columns]
Процент верных предсказаний: 52.4%

Получилось несколько хуже, чем первыми двумя методами.

До сих пор мы искали закономерности на плоскости: пытались прогнозировать направление изменения цены по изменениям за два предыдущих дня. Теперь можно попытаться увеличить размерность задачи – попробуйте сделать это самостоятельно.



Комментарии

Комментариев пока нет.

* Обязательные поля
(Не публикуется)
 
Жирный Курсив Подчеркнутый Перечеркнутый Степень Индекс Код PHP Код Кавычки Вставить линию Вставить маркированный список Вставить нумерованный список Вставить ссылку Вставить e-mail Вставить изображение Вставить видео
 
Улыбка Печаль Удивление Смех Злость Язык Возмущение Ухмылка Подмигнуть Испуг Круто Скука Смущение Несерьёзно Шокирован
 
1000
Captcha
Refresh
 
Введите код:
 
Запомнить информацию введенную в поля формы.