Кросс-корреляция (корреляция с задержкой) с пандами?

У меня есть различные временные ряды, которые я хочу коррелировать - или, скорее, взаимно коррелировать - друг с другом, чтобы выяснить, при каком временном лаге коэффициент корреляции является наибольшим.

Я нашел различных вопросы и ответы / ссылки, в которых обсуждается, как это сделать с помощью numpy, но это будет означать, что мне нужно превратить свои фреймы данных в массивы numpy. А поскольку мои временные ряды часто охватывают разные периоды, я боюсь столкнуться с хаосом.

Изменить

Проблема, с которой я сталкиваюсь со всеми методами numpy / scipy, заключается в том, что они, похоже, не осведомлены о характере временных рядов моих данных. Когда я сопоставляю временной ряд, который начинается, скажем, в 1940 году, с тем, который начинается в 1970 году, pandas corr знает это, тогда как np.correlate просто создает массив из 1020 записей (длина более длинного ряда), полный нан.

Различные вопросы по этому поводу указывают на то, что должен быть способ решить проблему разной длины, но до сих пор я не видел никаких указаний о том, как использовать его в определенные периоды времени. Мне просто нужно сдвинуть на 12 месяцев с шагом 1, чтобы увидеть время максимальной корреляции в пределах одного года.

Edit2

Некоторые минимальные образцы данных:

import pandas as pd
import numpy as np
dfdates1 = pd.date_range('01/01/1980', '01/01/2000', freq = 'MS')
dfdata1 = (np.random.random_integers(-30,30,(len(dfdates1)))/10.0) #My real data is from measurements, but random between -3 and 3 is fitting
df1 = pd.DataFrame(dfdata1, index = dfdates1)
dfdates2 = pd.date_range('03/01/1990', '02/01/2013', freq = 'MS')
dfdata2 = (np.random.random_integers(-30,30,(len(dfdates2)))/10.0)
df2 = pd.DataFrame(dfdata2, index = dfdates2)

Из-за различных этапов обработки эти dfs в конечном итоге превращаются в df, которые проиндексированы с 1940 по 2015 год. Это должно воспроизвести следующее:

bigdates = pd.date_range('01/01/1940', '01/01/2015', freq = 'MS')
big1 = pd.DataFrame(index = bigdates)
big2 = pd.DataFrame(index = bigdates)
big1 = pd.concat([big1, df1],axis = 1)
big2 = pd.concat([big2, df2],axis = 1)

Вот что я получаю, когда коррелирую с пандами и сдвигаю один набор данных:

In [451]: corr_coeff_0 = big1[0].corr(big2[0])
In [452]: corr_coeff_0
Out[452]: 0.030543266378853299
In [453]: big2_shift = big2.shift(1)
In [454]: corr_coeff_1 = big1[0].corr(big2_shift[0])
In [455]: corr_coeff_1
Out[455]: 0.020788314779320523

И пробуя scipy:

In [456]: scicorr = scipy.signal.correlate(big1,big2,mode="full")
In [457]: scicorr
Out[457]: 
array([[ nan],
       [ nan],
       [ nan],
       ..., 
       [ nan],
       [ nan],
       [ nan]])

который согласно whos

scicorr               ndarray                       1801x1: 1801 elems, type `float64`, 14408 bytes

Но я бы хотел иметь только 12 записей. / Edit2

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

corr_coeff_0 = df1['Data'].corr(df2['Data'])
df1_1month = df1.shift(1)
corr_coeff_1 = df1_1month['Data'].corr(df2['Data'])
df1_6month = df1.shift(6)
corr_coeff_6 = df1_6month['Data'].corr(df2['Data'])
...and so on

Но это, вероятно, медленно, и я, вероятно, пытаюсь здесь изобрести велосипед. Изменить. Похоже, что описанный выше подход работает, и я ввел его в цикл, чтобы использовать все 12 месяцев в году, но я все же предпочел бы встроенный метод.


person JC_CL    schedule 16.10.2015    source источник
comment
Если вы их еще не видели, подумайте об использовании scipy.signal.correlate и _2 _. Я бы сказал, что преобразование в массивы numpy, вероятно, ваш лучший выбор.   -  person wgwz    schedule 16.10.2015
comment
Я видел их, но я хочу избежать перехода к numpy, потому что после этого шага мне пришлось бы преобразовать обратно в фрейм данных для дальнейших вычислений. Думаю, я попробую изобрести велосипед, тогда…   -  person JC_CL    schedule 19.10.2015
comment
Насколько я знаю, это довольно распространенный рабочий процесс - преобразование в numpy и обратно. Я не вижу в этом необходимости колебаться. Я бы рекомендовал записывать ваши массивы на диск, чтобы вы не повторяли преобразования в своем коде. Касса pd.HDFStore и h5py. Если вы хотите изобретать велосипед, дерзайте.   -  person wgwz    schedule 19.10.2015
comment
Кстати, проверьте объект pandas apply / ufunc . Вы, наверное, уже нашли это. Фактически вы можете поместить функцию numpy в объект применения pandas. Так что это может помочь   -  person wgwz    schedule 20.10.2015
comment
Не знал series.apply, спасибо, это может пригодиться позже. Проблема, с которой я сталкиваюсь со всеми методами numpy / scipy, заключается в том, что они, похоже, не осведомлены о характере временных рядов моих данных. Когда я сопоставляю временной ряд, который начинается, скажем, в 1940 году, с временным рядом, который начинается в 1970 году, pandas corr знает это, тогда как np.correlate просто создает массив из 1020 записей, полный nan. Мне просто нужно переключиться, чтобы увидеть максимальную корреляцию в течение одного года.   -  person JC_CL    schedule 20.10.2015
comment
Не могли бы вы поделиться некоторыми данными в pastebin или gist или любом другом месте?   -  person wgwz    schedule 20.10.2015
comment
Я отредактировал некоторые поддельные образцы данных, а также то, как / почему они имеют такую ​​форму, чтобы они вели себя и выглядели так же, как мои настоящие данные.   -  person JC_CL    schedule 21.10.2015
comment
@JC_CL, ты чего-нибудь добился с этим? Также интересует решение на основе панд. Заранее спасибо!   -  person Steve Lorimer    schedule 31.05.2016
comment
Забавно, что вы спрашиваете ... Когда я задал этот вопрос, я просто сделал очень грубую вещь, которую в любом случае обрисовал в своем вопросе, а затем двинулся дальше, но на сегодняшний день мне приходится делать довольно много из них, поэтому я работаю над что-то. Ядро - это симпатичный маленький фрагмент кода @DanielWatkins ниже. Если доберусь до чего-то полезного, добавлю сюда.   -  person JC_CL    schedule 02.06.2016
comment
@JC_CL, ты смотрел этот пакет? statsmodels.org/devel/examples/index.html (если вы не не возражаю оставить панд для этих вычислений)   -  person jjrr    schedule 30.11.2017


Ответы (3)


Насколько я могу судить, не существует встроенного метода, который в точности выполняет то, о чем вы просите. Но если вы посмотрите исходный код метода autocorr серии pandas, вы увидите, что у вас есть правильная идея:

def autocorr(self, lag=1):
    """
    Lag-N autocorrelation

    Parameters
    ----------
    lag : int, default 1
        Number of lags to apply before performing autocorrelation.

    Returns
    -------
    autocorr : float
    """
    return self.corr(self.shift(lag))

Таким образом, простая функция кросс-ковариации с запаздыванием будет выглядеть так:

def crosscorr(datax, datay, lag=0):
    """ Lag-N cross correlation. 
    Parameters
    ----------
    lag : int, default 0
    datax, datay : pandas.Series objects of equal length

    Returns
    ----------
    crosscorr : float
    """
    return datax.corr(datay.shift(lag))

Затем, если вы хотите смотреть на кросс-корреляции каждый месяц, вы можете сделать

 xcov_monthly = [crosscorr(datax, datay, lag=i) for i in range(12)]
person Daniel Watkins    schedule 13.05.2016
comment
Спасибо, это очень помогает! Совершенно забыл, что встроенная автокорреляция - это, по сути, корреляция с запаздыванием. Я посмотрю, смогу ли я поработать с этим, чтобы получить более полезный результат, чем просто список. - person JC_CL; 02.06.2016
comment
Только что нашел - отличный ответ! - person AWilliams3142; 30.03.2020
comment
Когда я применяю это решение к моей серии panda, она дает nan, несмотря на то, что две серии разные. - person AlketCecaj; 19.08.2020

Существует лучший подход: вы можете создать функцию, которая сначала сдвигала ваш фрейм данных перед вызовом corr ().

Получите этот фрейм данных как пример:

d = {'prcp': [0.1,0.2,0.3,0.0], 'stp': [0.0,0.1,0.2,0.3]}
df = pd.DataFrame(data=d)

>>> df
   prcp  stp
0   0.1  0.0
1   0.2  0.1
2   0.3  0.2
3   0.0  0.3

Ваша функция для перемещения других столбцов (кроме целевого):

def df_shifted(df, target=None, lag=0):
    if not lag and not target:
        return df       
    new = {}
    for c in df.columns:
        if c == target:
            new[c] = df[target]
        else:
            new[c] = df[c].shift(periods=lag)
    return  pd.DataFrame(data=new)

Предположим, что ваша цель сравнивает prcp (переменная осадков) с stp (атмосферное давление).

Если вы это сделаете, в настоящее время будут:

>>> df.corr()
      prcp  stp
prcp   1.0 -0.2
stp   -0.2  1.0

Но если вы сдвинули все остальные столбцы на 1 (один) период и сохранили цель (prcp):

df_new = df_shifted(df, 'prcp', lag=-1)

>>> print df_new
   prcp  stp
0   0.1  0.1
1   0.2  0.2
2   0.3  0.3
3   0.0  NaN

Обратите внимание, что теперь столбец stp сдвигается на одну позицию вверх в периоде, поэтому, если вы вызовете corr (), будет:

>>> df_new.corr()
      prcp  stp
prcp   1.0  1.0
stp    1.0  1.0

Итак, вы можете обойтись лагом -1, -2, -n !!

person Andre Araujo    schedule 13.02.2018

Чтобы основываться на ответе Андре - если вас интересует только (запаздывающая) корреляция с целью, но вы хотите протестировать различные запаздывания (например, чтобы увидеть, какая задержка дает самые высокие корреляции), вы можете сделать что-то вроде этого:

lagged_correlation = pd.DataFrame.from_dict(
    {x: [df[target].corr(df[x].shift(-t)) for t in range(max_lag)] for x in df.columns})

Таким образом, каждая строка соответствует разному значению задержки, а каждый столбец соответствует разной переменной (одна из них - это сама цель, дающая автокорреляцию).

person Itamar Mushkin    schedule 03.04.2019