web-dev-qa-db-fra.com

Corrélation croisée (corrélation temporelle) avec les pandas?

J'ai différentes séries chronologiques, que je veux corréler - ou plutôt intercorréler - entre elles, pour savoir à quel décalage temporel le facteur de corrélation est le plus important.

J'ai trouvé diversquestions et des réponses/liens expliquant comment le faire avec numpy, mais cela signifierait que je dois transformer mes trames de données en tableaux numpy. Et comme mes séries chronologiques couvrent souvent différentes périodes, j'ai peur de tomber dans le chaos.

Modifier

Le problème que j'ai avec toutes les méthodes numpy/scipy, c'est qu'elles semblent ne pas être conscientes de la nature de la série temporelle de mes données. Lorsque je corrèle une série chronologique qui commence en 1940 par exemple avec une série qui commence en 1970, pandas corr le sait, tandis que np.correlate produit juste un tableau de 1020 entrées (longueur de la série la plus longue) plein de nan.

Les différents Q sur ce sujet indiquent qu'il devrait y avoir un moyen de résoudre le problème de longueur différente, mais jusqu'à présent, je n'ai vu aucune indication sur la façon de l'utiliser pour des périodes spécifiques. J'ai juste besoin de décaler de 12 mois par incréments de 1, pour voir le temps de corrélation maximale dans un an.

Edit2

Quelques exemples de données minimales:

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)

En raison de diverses étapes de traitement, ces dfs finissent par être transformés en df indexés de 1940 à 2015. Cela devrait reproduire ceci:

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)

Voici ce que j'obtiens lorsque je corrèle avec pandas et que je déplace un jeu de données:

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

Et essayant scipy:

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

qui selon whos est

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

Mais je voudrais juste avoir 12 entrées. /Edit2

L'idée que j'ai trouvée est d'implémenter moi-même une corrélation temporelle, comme ceci:

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

Mais c'est probablement lent, et j'essaie probablement de réinventer la roue ici. Edit L'approche ci-dessus semble fonctionner, et je l'ai mise en boucle, pour parcourir les 12 mois de l'année, mais je préférerais quand même une méthode intégrée.

28
JC_CL

Pour autant que je sache, il n'y a pas de méthode intégrée qui fait exactement ce que vous demandez. Mais si vous regardez le code source de la méthode de la série pandas autocorr, vous pouvez voir que vous avez la bonne idée:

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

Donc, une simple fonction de covariance croisée temporelle serait

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

Ensuite, si vous vouliez regarder les corrélations croisées à chaque mois, vous pourriez faire

 xcov_monthly = [crosscorr(datax, datay, lag=i) for i in range(12)]
33
Daniel Watkins

Il y a une meilleure approche: Vous pouvez créer une fonction qui décalée votre dataframe d'abord avant d'appeler le corr ().

Obtenez cette trame de données comme un exemple:

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

Votre fonction pour déplacer les autres colonnes (sauf la cible):

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)

Supposons que votre cible compare le prcp (variable de précipitation) au stp (pression atmosphérique)

Si vous le faites actuellement, ce sera:

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

Mais si vous décalé 1(one) période toutes les autres colonnes et gardez le cible (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

Notez que maintenant la colonne stp est décalée d'une position vers le haut à la période, donc si vous appelez corr (), ce sera:

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

Donc, vous pouvez faire avec lag -1, -2, -n !!

2
Andre Araujo

Pour s'appuyer sur la réponse d'André - si vous ne vous souciez que de la corrélation (décalée) avec la cible, mais souhaitez tester divers décalages (par exemple pour voir quel décalage donne les corrélations les plus élevées), vous pouvez faire quelque chose comme ceci:

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

De cette façon, chaque ligne correspond à une valeur de décalage différente, et chaque colonne correspond à une variable différente (l'une d'entre elles est la cible elle-même, donnant l'autocorrélation).

0
Itamar Mushkin