Временные ряды 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

Мне нужно добавить две колонки:

  1. 12 сма, скользящее среднее за 12 дней. После поисков в течение нескольких часов лучшим решением было бы использовать roll_mean, поэтому я попытался. Но это не сработало, учитывая мою структуру TS, т. Е. Она работает сверху вниз, первая МА рассчитывается на основе первых 12 строк независимо от разных значений тикера. Как мне сделать его средним на основе индекса, т.е. сначала дата-время, а затем тикер, чтобы я получил MA, скажем, AAPL? В настоящее время это делает (AAPL+EQIX+FB+GOOG+MSFT+AAPL... до 12-й строки) / 12
  2. Как только я получил столбец 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
Другие вопросы по тегам