使用pandas进行交叉相关性(时滞相关性)分析?

64

我有多个时间序列,想要互相关或者说交叉相关,以找出在哪个时间滞后下相关系数最大。

我找到了各种问题和回答/链接,讨论如何使用numpy进行计算,但这意味着我必须将我的数据框转换为numpy数组。由于我的时间序列经常涵盖不同的时间段,我担心会遇到混乱。

编辑

我在使用所有numpy/scipy方法时遇到的问题是,它们似乎缺乏对我的数据时序性的意识。当我将一个从1940年开始的时间序列与一个从1970年开始的时间序列相关联时,pandas的corr知道这一点,而np.correlate只会生成一个1020个条目(较长序列的长度)的数组,填充满nan。

该主题上的各种问题表明应该有一种解决不同长度问题的方法,但到目前为止,我还没有看到如何针对特定时间段使用它的迹象。我只需要以1个月的增量移动12个月,以查看一年内最大相关时间。

编辑2

一些最小样本数据:

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)

由于各种处理步骤,这些数据框最终被更改为从1940年到2015年进行索引的数据框。这应该能够重新生成如下内容:

由于各种处理步骤,这些数据框最终被更改为从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)

当我使用pandas和shift一个数据集进行相关性分析时,这就是我得到的结果:

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个月,但我仍然希望有一个内置方法。


2
如果你还没有看过这些,请考虑使用scipy.signal.correlatescipy.signal.correlate2d。我认为转换为numpy数组可能是最好的选择。 - wgwz
我看过那些,但我想避免使用numpy,因为在这一步之后,我还需要将其转换回数据框以进行进一步的计算。我想我会尝试重新发明轮子... - JC_CL
据我所知,将数据转换为numpy格式再转回来是一种相当常见的工作流程。我认为没有必要犹豫。我建议将你的数组写入硬盘,这样你就不必在代码中重复进行转换了。可以查看pd.HDFStoreh5py。如果你感觉自己有能力重新发明轮子,那就去做吧。 - wgwz
1
顺便查一下pandas apply/ufunc object。你可能已经发现了。实际上,你可以将numpy函数放入pandas apply对象中。这样就可以解决问题了。 - wgwz
不知道series.apply,谢谢,这可能会在以后派上用场。我遇到的问题是所有numpy/scipy方法似乎都没有意识到我的数据是时间序列的。当我将开始于1940年的时间序列与一个从1970年开始的时间序列进行相关时,pandas corr 知道这一点,而np.correlate只是产生一个1020个条目的数组,其中全部是nan。我只需要移位以查看一年内的最大相关性。 - JC_CL
显示剩余5条评论
3个回答

73

据我所知,没有内置的方法可以完全满足您的要求。但是,如果您查看pandas Series方法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)]

谢谢,这非常有帮助!我完全忘记了内置的自相关实际上是一个时间滞后相关。我会看看能否利用它来产生比仅仅列表更有用的输出。 - JC_CL
1
刚发现这个——非常好的答案! - Andrew Williams
1
当我将这个解决方案应用到我的熊猫系列时,尽管这两个系列不同,但它会产生nan。 - AlketCecaj

14

在Andre的回答基础上,如果您只关心与目标的(滞后)相关性,但想测试各种滞后(例如查看哪个滞后给出最高的相关性),可以像这样操作:

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

这样,每一行对应不同的滞后值,每一列对应不同的变量(其中一个是目标本身,给出自相关性)。


12

有一个更好的方法: 您可以创建一个函数,先将数据框进行平移,然后再调用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!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接