Cómo crear una estructura de datos retrasada usando el dataframe pandas

Ejemplo

s=pd.Series([5,4,3,2,1], index=[1,2,3,4,5]) print s 1 5 2 4 3 3 4 2 5 1 

¿Hay una manera eficiente de crear una serie. por ejemplo, que contiene en cada fila los valores retrasados ​​(en este ejemplo hasta el retraso 2)

 3 [3, 4, 5] 4 [2, 3, 4] 5 [1, 2, 3] 

Esto corresponde a s = pd.Serie ([[3,4,5], [2,3,4], [1,2,3]], índice = [3,4,5])

¿Cómo se puede hacer esto de manera eficiente para los marcos de datos con muchas series temporales que son muy largas?

Gracias

Editado después de ver las respuestas.

ok, al final implementé esta función:

 def buildLaggedFeatures(s,lag=2,dropna=True): ''' Builds a new DataFrame to facilitate regressing over all possible lagged features ''' if type(s) is pd.DataFrame: new_dict={} for col_name in s: new_dict[col_name]=s[col_name] # create lagged Series for l in range(1,lag+1): new_dict['%s_lag%d' %(col_name,l)]=s[col_name].shift(l) res=pd.DataFrame(new_dict,index=s.index) elif type(s) is pd.Series: the_range=range(lag+1) res=pd.concat([s.shift(i) for i in the_range],axis=1) res.columns=['lag_%d' %i for i in the_range] else: print 'Only works for DataFrame or Series' return None if dropna: return res.dropna() else: return res 

produce los resultados deseados y gestiona la denominación de las columnas en el DataFrame resultante.

Para una serie como entrada:

 s=pd.Series([5,4,3,2,1], index=[1,2,3,4,5]) res=buildLaggedFeatures(s,lag=2,dropna=False) lag_0 lag_1 lag_2 1 5 NaN NaN 2 4 5 NaN 3 3 4 5 4 2 3 4 5 1 2 3 

y para un DataFrame como entrada:

 s2=s=pd.DataFrame({'a':[5,4,3,2,1], 'b':[50,40,30,20,10]},index=[1,2,3,4,5]) res2=buildLaggedFeatures(s2,lag=2,dropna=True) a a_lag1 a_lag2 b b_lag1 b_lag2 3 3 4 5 30 40 50 4 2 3 4 20 30 40 5 1 2 3 10 20 30 

Como se mencionó, podría valer la pena ver las funciones de rolling_ , lo que significará que no tendrá tantas copias alrededor.

Una solución es concatear la serie cambiada para hacer un DataFrame:

 In [11]: pd.concat([s, s.shift(), s.shift(2)], axis=1) Out[11]: 0 1 2 1 5 NaN NaN 2 4 5 NaN 3 3 4 5 4 2 3 4 5 1 2 3 In [12]: pd.concat([s, s.shift(), s.shift(2)], axis=1).dropna() Out[12]: 0 1 2 3 3 4 5 4 2 3 4 5 1 2 3 

Trabajar en esto será más eficiente que en las listas …

Solución muy simple usando pandas DataFrame:

 number_lags = 3 df = pd.DataFrame(data={'vals':[5,4,3,2,1]}) for lag in xrange(1, number_lags + 1): df['lag_' + str(lag)] = df.vals.shift(lag) #if you want numpy arrays with no null values: df.dropna().values for numpy arrays 

Puedes hacer lo siguiente:

 s=pd.Series([5,4,3,2,1], index=[1,2,3,4,5]) res = pd.DataFrame(index = s.index) for l in range(3): res[l] = s.shift(l) print res.ix[3:,:].as_matrix() 

Produce:

 array([[ 3., 4., 5.], [ 2., 3., 4.], [ 1., 2., 3.]]) 

que espero que sea muy cercano a lo que realmente quieres.

Me gusta poner los números de retraso en las columnas haciendo que las columnas sean un MultiIndex . De esta manera, se conservan los nombres de las columnas.

Aquí hay un ejemplo del resultado:

 # Setup indx = pd.Index([1, 2, 3, 4, 5], name='time') s=pd.Series( [5, 4, 3, 2, 1], index=indx, name='population') shift_timeseries_by_lags(pd.DataFrame(s), [0, 1, 2]) 

Resultado: un dataframe MultiIndex con dos tags de columna: el original (“población”) y uno nuevo (“retraso”):

Marco de datos con retrasos 0, 1 y 2


Solución : Al igual que en la solución aceptada, usamos DataFrame.shift y luego pandas.concat .

 def shift_timeseries_by_lags(df, lags, lag_label='lag'): return pd.concat([ shift_timeseries_and_create_multiindex_column(df, lag, lag_label=lag_label) for lag in lags], axis=1) def shift_timeseries_and_create_multiindex_column( dataframe, lag, lag_label='lag'): return (dataframe.shift(lag) .pipe(append_level_to_columns_of_dataframe, lag, lag_label)) 

Me gustaría que hubiera una manera fácil de agregar una lista de tags a las columnas existentes. Aquí está mi solución.

 def append_level_to_columns_of_dataframe( dataframe, new_level, name_of_new_level, inplace=False): """Given a (possibly MultiIndex) DataFrame, append labels to the column labels and assign this new level a name. Parameters ---------- dataframe : a pandas DataFrame with an Index or MultiIndex columns new_level : scalar, or arraylike of length equal to the number of columns in `dataframe` The labels to put on the columns. If scalar, it is broadcast into a list of length equal to the number of columns in `dataframe`. name_of_new_level : str The label to give the new level. inplace : bool, optional, default: False Whether to modify `dataframe` in place or to return a copy that is modified. Returns ------- dataframe_with_new_columns : pandas DataFrame with MultiIndex columns The original `dataframe` with new columns that have the given `level` appended to each column label. """ old_columns = dataframe.columns if not hasattr(new_level, '__len__') or isinstance(new_level, str): new_level = [new_level] * dataframe.shape[1] if isinstance(dataframe.columns, pd.MultiIndex): new_columns = pd.MultiIndex.from_arrays( old_columns.levels + [new_level], names=(old_columns.names + [name_of_new_level])) elif isinstance(dataframe.columns, pd.Index): new_columns = pd.MultiIndex.from_arrays( [old_columns] + [new_level], names=([old_columns.name] + [name_of_new_level])) if inplace: dataframe.columns = new_columns return dataframe else: copy_dataframe = dataframe.copy() copy_dataframe.columns = new_columns return copy_dataframe 

Actualización : aprendí de esta solución otra forma de poner un nuevo nivel en una columna, lo que hace que no sea necesario usar append_level_to_columns_of_dataframe :

 def shift_timeseries_by_lags_v2(df, lags, lag_label='lag'): return pd.concat({ '{lag_label}_{lag_number}'.format(lag_label=lag_label, lag_number=lag): df.shift(lag) for lag in lags}, axis=1) 

Aquí está el resultado de shift_timeseries_by_lags_v2(pd.DataFrame(s), [0, 1, 2]) :

resultado de <code/> shift_timeseries_by_lags_2 “> </p>
</div>
</li><!-- #comment-## -->
<div class=

Para un df de dataframe con el retraso que se aplicará en ‘nombre de columna’, puede utilizar la función de cambio.

 df['lag1']=df['col name'].shift(1) df['lag2']=df['col name'].shift(2) 

Para varios retrasos (muchos de ellos), esto podría ser más compacto:

 df=pd.DataFrame({'year': range(2000, 2010), 'gdp': [234, 253, 256, 267, 272, 273, 271, 275, 280, 282]}) df.join(pd.DataFrame({'gdp_' + str(lag): df['gdp'].shift(lag) for lag in range(1,4)})) 

Suponiendo que se está enfocando en una sola columna en su dataframe, guardado en s. Este código abreviado generará instancias de la columna con 7 retrasos.

 s=pd.Series([5,4,3,2,1], index=[1,2,3,4,5], name='test') shiftdf=pd.DataFrame() for i in range(3): shiftdf = pd.concat([shiftdf , s.shift(i).rename(s.name+'_'+str(i))], axis=1) shiftdf >> test_0 test_1 test_2 1 5 NaN NaN 2 4 5.0 NaN 3 3 4.0 5.0 4 2 3.0 4.0 5 1 2.0 3.0