Временные ряды Python-панд с иерархической индексацией и переходом / сдвигом
Борьба с концепцией вращения и переключения панд. Есть много хороших предложений, в том числе и на этом форуме, но мне не удалось применить их к моему сценарию.
Теперь я использую традиционные циклы для временных рядов, но, тьфу, понадобилось около 8 часов, чтобы перебрать 150000 строк, что составляет около 3 дней данных для всех тикеров. У меня есть данные за 2 месяца, чтобы обработать их, вероятно, не закончится после того, как я вернусь из творческого отпуска, не говоря уже о риске отключения электричества, после которого мне придется начинать все заново, на этот раз без творческого отпуска во время ожидания.
У меня есть следующие 15-минутные временные ряды цены акций (иерархический индекс по дате и времени (отметка времени) и тикер, единственный исходный столбец - closePrice):
closePrice
datetime ticker
2014-02-04 09:15:00 AAPL xxx
EQIX xxx
FB xxx
GOOG xxx
MSFT xxx
2014-02-04 09:30:00 AAPL xxx
EQIX xxx
FB xxx
GOOG xxx
MSFT xxx
2014-02-04 09:45:00 AAPL xxx
EQIX xxx
FB xxx
GOOG xxx
MSFT xxx
Мне нужно добавить две колонки:
- 12 сма, скользящее среднее за 12 дней. После поисков в течение нескольких часов лучшим решением было бы использовать roll_mean, поэтому я попытался. Но это не сработало, учитывая мою структуру TS, т. Е. Она работает сверху вниз, первая МА рассчитывается на основе первых 12 строк независимо от разных значений тикера. Как мне сделать его средним на основе индекса, т.е. сначала дата-время, а затем тикер, чтобы я получил MA, скажем, AAPL? В настоящее время это делает (AAPL+EQIX+FB+GOOG+MSFT+AAPL... до 12-й строки) / 12
- Как только я получил столбец 12sma, мне нужен столбец 12ema, 12-дневная экспоненциальная скользящая средняя. Для расчета первое значение во временном ряду для каждого тикера просто скопирует значение 12sma из той же строки. Впоследствии мне понадобится closePrice из той же строки и 12ema из предыдущей строки, т.е. за последние 15 минут. Я провел длительное исследование, похоже, что решение было бы сочетанием сдвига и сдвига, но я не могу понять, как его соединить.
Любая помощь, я был бы благодарен.
Благодарю.
РЕДАКТИРОВАТЬ:
Благодаря советам Джеффа, после перестановки и сортировки уровня ix, я могу получить правильное значение 12sma с помощью roll_mean(), и с усилием удалось вставить первое значение 12ema, скопированное из 12sma в то же время:
close 12sma 12ema
sec_code datetime
AAPL 2014-02-05 11:45:00 113.0 NaN NaN
2014-02-05 12:00:00 113.2 NaN NaN
2014-02-05 13:15:00 112.9 NaN NaN
2014-02-05 13:30:00 113.2 NaN NaN
2014-02-05 13:45:00 113.0 NaN NaN
2014-02-05 14:00:00 113.1 NaN NaN
2014-02-05 14:15:00 113.3 NaN NaN
2014-02-05 14:30:00 113.3 NaN NaN
2014-02-05 14:45:00 113.3 NaN NaN
2014-02-05 15:00:00 113.2 NaN NaN
2014-02-05 15:15:00 113.2 NaN NaN
2014-02-05 15:30:00 113.3 113.16 113.16
2014-02-05 15:45:00 113.3 113.19 NaN
2014-02-05 16:00:00 113.2 113.19 NaN
2014-02-06 09:45:00 112.6 113.16 NaN
2014-02-06 10:00:00 113.5 113.19 NaN
2014-02-06 10:15:00 113.8 113.25 NaN
2014-02-06 10:30:00 113.5 113.29 NaN
2014-02-06 10:45:00 113.7 113.32 NaN
2014-02-06 11:00:00 113.5 113.34 Nan
Я понимаю, что у pandas есть pandas.stats.moments.ewma, но я предпочитаю использовать формулу, которую я получил из книги, для которой нужна цена закрытия "в данный момент" и 12 ema из предыдущего ряда.
Итак, я попытался заполнить колонку 12ema с 5 февраля 15:45 и далее. Я попытался применить () с функцией, но сдвиг дал ошибку:
def f12ema(x):
K = 2 / (12 + 1)
return x['price_nom'] * K + x['12ema'].shift(-1) * (1-K)
df1.apply(f12ema, axis=1)
AttributeError: ("'numpy.float64' object has no attribute 'shift'", u'occurred at index 2014-02-05 11:45:00')
Другая возможность, которая пришла мне в голову, - это roll_appy(), но она мне не известна.
1 ответ
Создайте диапазон дат, включающий желаемое время
In [47]: rng = date_range('20130102 09:30:00','20130105 16:00:00',freq='15T')
In [48]: rng = rng.take(rng.indexer_between_time('09:30:00','16:00:00'))
In [49]: rng
Out[49]:
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-01-02 09:30:00, ..., 2013-01-05 16:00:00]
Length: 108, Freq: None, Timezone: None
Создайте рамку, похожую на вашу (2000 тикеров х дат)
In [50]: df = DataFrame(np.random.randn(len(rng)*2000,1),columns=['close'],index=MultiIndex.from_product([rng,range(2000)],names=['date','ticker']))
Переупорядочить уровни так, чтобы его тикер х дата индекса, сортировать его!!!!
In [51]: df = df.swaplevel('ticker','date').sortlevel()
In [52]: df
Out[52]:
close
ticker date
0 2013-01-02 09:30:00 0.840767
2013-01-02 09:45:00 1.808101
2013-01-02 10:00:00 -0.718354
2013-01-02 10:15:00 -0.484502
2013-01-02 10:30:00 0.563363
2013-01-02 10:45:00 0.553920
2013-01-02 11:00:00 1.266992
2013-01-02 11:15:00 -0.641117
2013-01-02 11:30:00 -0.574673
2013-01-02 11:45:00 0.861825
2013-01-02 12:00:00 -1.562111
2013-01-02 12:15:00 -0.072966
2013-01-02 12:30:00 0.673079
2013-01-02 12:45:00 0.766105
2013-01-02 13:00:00 0.086202
2013-01-02 13:15:00 0.949205
2013-01-02 13:30:00 -0.381194
2013-01-02 13:45:00 0.316813
2013-01-02 14:00:00 -0.620176
2013-01-02 14:15:00 -0.193126
2013-01-02 14:30:00 -1.552111
2013-01-02 14:45:00 1.724429
2013-01-02 15:00:00 -0.092393
2013-01-02 15:15:00 0.197763
2013-01-02 15:30:00 0.064541
2013-01-02 15:45:00 -1.574853
2013-01-02 16:00:00 -1.023979
2013-01-03 09:30:00 -0.079349
2013-01-03 09:45:00 -0.749285
2013-01-03 10:00:00 0.652721
2013-01-03 10:15:00 -0.818152
2013-01-03 10:30:00 0.674068
2013-01-03 10:45:00 2.302714
2013-01-03 11:00:00 0.614686
...
[216000 rows x 1 columns]
Группировать по тикеру. Вернуть DataFrame для каждого тикера, который является приложением roll_mean и ewma. Обратите внимание, что есть много вариантов для управления этим, например, управление окнами, вы можете сделать так, чтобы оно не было перенесено на несколько дней и т.д.
In [53]: df.groupby(level='ticker')['close'].apply(lambda x: concat({ 'spma' : pd.rolling_mean(x,3), 'ema' : pd.ewma(x,3) }, axis=1))
Out[53]:
ema spma
ticker date
0 2013-01-02 09:30:00 0.840767 NaN
2013-01-02 09:45:00 1.393529 NaN
2013-01-02 10:00:00 0.480282 0.643504
2013-01-02 10:15:00 0.127447 0.201748
2013-01-02 10:30:00 0.270334 -0.213164
2013-01-02 10:45:00 0.356580 0.210927
2013-01-02 11:00:00 0.619245 0.794758
2013-01-02 11:15:00 0.269100 0.393265
2013-01-02 11:30:00 0.041032 0.017067
2013-01-02 11:45:00 0.258476 -0.117988
2013-01-02 12:00:00 -0.216742 -0.424986
2013-01-02 12:15:00 -0.179622 -0.257750
2013-01-02 12:30:00 0.038741 -0.320666
2013-01-02 12:45:00 0.223881 0.455406
2013-01-02 13:00:00 0.188995 0.508462
2013-01-02 13:15:00 0.380972 0.600504
2013-01-02 13:30:00 0.188987 0.218071
2013-01-02 13:45:00 0.221125 0.294942
2013-01-02 14:00:00 0.009907 -0.228185
2013-01-02 14:15:00 -0.041013 -0.165496
2013-01-02 14:30:00 -0.419688 -0.788471
2013-01-02 14:45:00 0.117299 -0.006936
2013-01-04 10:00:00 -0.060415 0.341013
2013-01-04 10:15:00 0.074068 0.604611
2013-01-04 10:30:00 -0.108502 0.440256
2013-01-04 10:45:00 -0.514229 -0.636702
... ...
[216000 rows x 2 columns]
Довольно хороший перфоманс, так как он по сути зацикливается на тикерах.
In [54]: %timeit df.groupby(level='ticker')['close'].apply(lambda x: concat({ 'spma' : pd.rolling_mean(x,3), 'ema' : pd.ewma(x,3) }, axis=1))
1 loops, best of 3: 2.1 s per loop