2016-05-27 14 views
12

Ich wurde motiviert, Pandas rolling Feature zu verwenden, um eine rollende Multifaktor-Regression (Diese Frage ist NICHT über rollende Multi-Faktor-Regression). Ich erwartete, dass ich apply nach einem df.rolling(2) verwenden und das resultierende pd.DataFrame das NDarray mit .values extrahieren und die erforderliche Matrixmultiplikation durchführen kann. So hat es nicht geklappt. Hierwarum pandas rolling single-dimension verwenden ndarray

ist das, was ich gefunden habe:

import pandas as pd 
import numpy as np 

np.random.seed([3,1415]) 
df = pd.DataFrame(np.random.rand(5, 2).round(2), columns=['A', 'B']) 
X = np.random.rand(2, 1).round(2) 

Was Objekte tun wie folgt aussehen:

print "\ndf = \n", df 
print "\nX = \n", X 
print "\ndf.shape =", df.shape, ", X.shape =", X.shape 

df = 
     A  B 
0 0.44 0.41 
1 0.46 0.47 
2 0.46 0.02 
3 0.85 0.82 
4 0.78 0.76 

X = 
[[ 0.93] 
[ 0.83]] 

df.shape = (5, 2) , X.shape = (2L, 1L) 

Matrixmultiplikation normalerweise verhält:

df.values.dot(X) 

array([[ 0.7495], 
     [ 0.8179], 
     [ 0.4444], 
     [ 1.4711], 
     [ 1.3562]]) 

Verwendung gelten Zeile für Zeile Punkt auszuführen Produkt verhält sich wie erwartet:

df.apply(lambda x: x.values.dot(X)[0], axis=1) 

0 0.7495 
1 0.8179 
2 0.4444 
3 1.4711 
4 1.3562 
dtype: float64 

GROUPBY -> benimmt sich bewerben, wie ich erwarten würde:

df.groupby(level=0).apply(lambda x: x.values.dot(X)[0, 0]) 

0 0.7495 
1 0.8179 
2 0.4444 
3 1.4711 
4 1.3562 
dtype: float64 

Aber wenn ich laufen:

df.rolling(1).apply(lambda x: x.values.dot(X)) 

ich:

AttributeError: 'numpy.ndarray' object has no attribute 'values'

Ok, so Pandas ist Verwendung von gerade ndarray innerhalb seiner rolling Implementierung. Ich komme damit klar. Statt .values der Verwendung der ndarray zu bekommen, lassen Sie uns versuchen:

df.rolling(1).apply(lambda x: x.dot(X)) 

shapes (1,) and (2,1) not aligned: 1 (dim 0) != 2 (dim 0)

Warten Sie! Was?!

Also habe ich eine benutzerdefinierte Funktion erstellt, um zu sehen, was Rolling tut.

def print_type_sum(x): 
    print type(x), x.shape 
    return x.sum() 

Dann lief:

print df.rolling(1).apply(print_type_sum) 

<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
<type 'numpy.ndarray'> (1L,) 
     A  B 
0 0.44 0.41 
1 0.46 0.47 
2 0.46 0.02 
3 0.85 0.82 
4 0.78 0.76 

Mein resultierende pd.DataFrame das gleiche ist, das ist gut. Aber es hat 10 eindimensionale ndarray Objekte ausgedruckt. Was ist rolling(2)

print df.rolling(2).apply(print_type_sum) 

<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
<type 'numpy.ndarray'> (2L,) 
     A  B 
0 NaN NaN 
1 0.90 0.88 
2 0.92 0.49 
3 1.31 0.84 
4 1.63 1.58 

Das Gleiche gilt, erwarten Ausgang aber es gedruckt 8 ndarray Objekte. rolling produziert eine einzelne Dimension ndarray der Länge window für jede Spalte im Gegensatz zu dem, was ich erwartet, die eine ndarray der Form (window, len(df.columns)) war.

Frage ist warum?

Ich habe jetzt keine Möglichkeit, eine rollende Multi-Faktor-Regression leicht zu führen.

+1

Dies ist ein [bekanntes Problem] (http: // stackoverflow.com/a/21026837/5276797). Ich habe Jeff kürzlich gefragt, du kannst seine Antwort in den Kommentaren lesen! – IanS

+0

Was ist die aktuelle Lösung von Pandas 0.20? Scheint wie viele Verbesserungen gemacht worden sind. Ist das Ziel in OP erreichbar mit rolling(). Apply() direkt? – Zhang18

Antwort

4

die strides views concept on dataframe Verwendung, hier ist ein vektorisiert Ansatz -

get_sliding_window(df, 2).dot(X) # window size = 2 

Runtime Test -

In [101]: df = pd.DataFrame(np.random.rand(5, 2).round(2), columns=['A', 'B']) 

In [102]: X = np.array([2, 3]) 

In [103]: rolled_df = roll(df, 2) 

In [104]: %timeit rolled_df.apply(lambda df: pd.Series(df.values.dot(X))) 
100 loops, best of 3: 5.51 ms per loop 

In [105]: %timeit get_sliding_window(df, 2).dot(X) 
10000 loops, best of 3: 43.7 µs per loop 

Ergebnisse überprüfen -

In [106]: rolled_df.apply(lambda df: pd.Series(df.values.dot(X))) 
Out[106]: 
     0  1 
1 2.70 4.09 
2 4.09 2.52 
3 2.52 1.78 
4 1.78 3.50 

In [107]: get_sliding_window(df, 2).dot(X) 
Out[107]: 
array([[ 2.7 , 4.09], 
     [ 4.09, 2.52], 
     [ 2.52, 1.78], 
     [ 1.78, 3.5 ]]) 

Huge es Verbesserung, die ich bin Die Hoffnung würde auf größeren Arrays spürbar bleiben!

+0

Ich bekomme einen ungelösten Referenzfehler beim Versuch, get_sliding_window zu verwenden – RaduS

+0

@RaduS Was genau sagt der Fehler? – Divakar

6

Ich wollte teilen, was ich getan habe, um dieses Problem zu umgehen.

ein pd.DataFrame und ein Fenster gegeben, I erzeugen eine gestapelte ndarray Verwendung np.dstack (see answer). Ich konvertiere es dann in eine pd.Panel und mit pd.Panel.to_frame wandeln Sie es in eine pd.DataFrame um. An dieser Stelle habe ich eine pd.DataFrame, die eine zusätzliche Ebene in ihrem Index relativ zu der ursprünglichen pd.DataFrame hat und die neue Ebene enthält Informationen über jede gerollte Periode. Wenn das Roll-Fenster beispielsweise 3 ist, enthält die neue Indexstufe [0, 1, 2]. Ein Artikel für jede Periode. Ich kann jetzt groupbylevel=0 und das Objekt groupby zurückgeben. Dies gibt mir jetzt ein Objekt, das ich viel intuitiver manipulieren kann.

Rollfunktion

import pandas as pd 
import numpy as np 

def roll(df, w): 
    roll_array = np.dstack([df.values[i:i+w, :] for i in range(len(df.index) - w + 1)]).T 
    panel = pd.Panel(roll_array, 
        items=df.index[w-1:], 
        major_axis=df.columns, 
        minor_axis=pd.Index(range(w), name='roll')) 
    return panel.to_frame().unstack().T.groupby(level=0) 

Demonstration

np.random.seed([3,1415]) 
df = pd.DataFrame(np.random.rand(5, 2).round(2), columns=['A', 'B']) 

print df 

     A  B 
0 0.44 0.41 
1 0.46 0.47 
2 0.46 0.02 
3 0.85 0.82 
4 0.78 0.76 

Lassen Sie uns sum

rolled_df = roll(df, 2) 

print rolled_df.sum() 

major  A  B 
1  0.90 0.88 
2  0.92 0.49 
3  1.31 0.84 
4  1.63 1.58 

unter der Haube spähen wir die stucture sehen:

print rolled_df.apply(lambda x: x) 

major  A  B 
    roll    
1 0  0.44 0.41 
    1  0.46 0.47 
2 0  0.46 0.47 
    1  0.46 0.02 
3 0  0.46 0.02 
    1  0.85 0.82 
4 0  0.85 0.82 
    1  0.78 0.76 

Aber was ist mit dem Zweck, für den ich das gebaut habe, Rolling Multi-Faktor-Regression. Aber ich werde mich jetzt für die Matrix-Multiplikation entscheiden.

X = np.array([2, 3]) 

print rolled_df.apply(lambda df: pd.Series(df.values.dot(X))) 

     0  1 
1 2.11 2.33 
2 2.33 0.98 
3 0.98 4.16 
4 4.16 3.84 
+0

Das war sehr hilfreich, danke. Ich hatte ein kleines Problem mit Nan-Werten, aber das Aktualisieren der letzten Zeile der 'Roll'-Funktion zur Verwendung von' .to_frame (filter_observations = False) 'behob mein Problem. – user338714

+0

Dies ist hilfreich. Aber gibt es einen Weg, die 'Roll'-Spalte den ursprünglichen Index beizubehalten? z.B. 0.46 0.47 ist immer mit "1" verknüpft. Vielen Dank. – iwbabn

1

die folgenden Änderungen an der obigen Antwort gemacht, da ich das ganze Rollfenster zurückzukehren benötigt wie in pd.DataFrame.rolling getan wird()

def roll(df, w): 
    roll_array = np.dstack([df.values[i:i+w, :] for i in range(len(df.index) - w + 1)]).T 
    roll_array_full_window = np.vstack((np.empty((w-1 ,len(df.columns), w)), roll_array)) 
    panel = pd.Panel(roll_array_full_window, 
       items=df.index, 
       major_axis=df.columns, 
       minor_axis=pd.Index(range(w), name='roll')) 
    return panel.to_frame().unstack().T.groupby(level=0)