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

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

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

редактировать

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

Различные вопросы по этому вопросу указывают на то, что должен быть способ решить проблему различной длины, но до сих пор я не видел указаний на то, как использовать его для определенных периодов времени. Мне просто нужно сдвинуться на 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

И пытается скупиться

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 месяцев года, но я все еще предпочел бы встроенный метод.

2 ответа

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

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)]

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

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})

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

Есть лучший подход: вы можете создать функцию, которая сместит ваш фрейм данных перед вызовом 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!!

Другие вопросы по тегам