2016-05-27 19 views
12

Byłem zmotywowany do korzystania z funkcji pandy rolling do wykonywania toczącej się wieloczynnikowej regresji (to pytanie jest NOT o toczącej się regresji wieloczynnikowej). Spodziewałem się, że będę mógł użyć apply po df.rolling(2) i pobrać wynikowy pd.DataFrame wyodrębnić ndarray z .values i wykonać wymagane mnożenie macierzy. To nie działało w ten sposób.Dlaczego pandy toczące się używają pojedynczego wymiaru ndarray

Oto co znalazłem:

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) 

Co zrobić obiekty wyglądać następująco:

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) 

Mnożenie macierzy zachowuje się normalnie:

df.values.dot(X) 

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

Korzystanie zastosować, aby wykonać kolejny wiersz kropki produkt zachowuje się zgodnie z oczekiwaniami:

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 -> Zastosuj zachowuje się jak bym się spodziewać:

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 

Ale kiedy biegnę:

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

uzyskać:

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

Ok, więc jest pand za pomocą prostej ndarray w ramach implementacji rolling. Mogę sobie z tym poradzić. Zamiast używać .values uzyskać ndarray, spróbujmy:

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

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

Czekaj! Co?!

Stworzyłem więc niestandardową funkcję, aby sprawdzić, co się dzieje.

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

Potem pobiegł:

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 

My wynikające pd.DataFrame jest taka sama, to jest dobre. Ale wydrukowano 10 pojedynczych wymiarów obiektów ndarray. Co o 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 

samo, ale oczekiwać wyjście drukowane 8 ndarray obiektów. rolling produkuje jednowymiarowy ndarray o długości window dla każdej kolumny, w przeciwieństwie do tego, co oczekiwałem, który był ndarray o kształcie (window, len(df.columns)).

Pytanie brzmi, dlaczego?

Nie mam teraz sposobu na łatwe uruchomienie toczącej się regresji wieloczynnikowej.

+1

Jest to [znany problem] (http: // stackoverflow.com/a/21026837/5276797). Niedawno zapytałam o to Jeffa, możesz przeczytać jego odpowiedź w komentarzach! – IanS

+0

Co to jest najnowocześniejsze rozwiązanie od Pandy 0.20? Wygląda na to, że wprowadzono wiele ulepszeń. Czy cel w OP możliwy do osiągnięcia za pomocą rolling(). Apply() bezpośrednio? – Zhang18

Odpowiedz

4

Używanie strides views concept on dataframe, oto wektorowy podejście -

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

Runtime testu -

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 

Weryfikuj wyniki -

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

ogromny postęp tam, co ja nadzieję, że pozostaną zauważalne na większych tablicach!

+0

pojawia się nierozwiązany błąd odniesienia podczas próby użycia get_sliding_window – RaduS

+0

@RaduS Co dokładnie mówi ten błąd? – Divakar

6

Chciałem podzielić się tym, co zrobiłem, aby obejść ten problem.

Biorąc pod uwagę pd.DataFrame i okno, generuję ułożone ndarray przy użyciu np.dstack (see answer). Następnie przekonwertuję go na pd.Panel i za pomocą pd.Panel.to_frame zamieniam go na pd.DataFrame. W tym momencie mam pd.DataFrame, który ma dodatkowy poziom w swoim indeksie w stosunku do oryginalnego pd.DataFrame, a nowy poziom zawiera informacje o każdym zwiniętym okresie. Na przykład, jeśli okno rolki ma wartość 3, nowy poziom indeksu będzie wynosił [0, 1, 2]. Przedmiot dla każdego okresu. Mogę teraz groupby i zwrócić obiekt groupby. To daje mi teraz obiekt, który mogę intuicyjnie manipulować.

Funkcja Rolka

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) 

Demonstracja

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 

Chodźmy 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 

zajrzeć pod maskę, możemy zobaczyć stucture:

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 

Ale co z celem, dla którego go zbudowałem, toczącą się regresją wieloczynnikową. Ale na razie ustalę rozmnożenie macierzy.

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

To było bardzo pomocne, dzięki. Wpadłem na małe problemy z wartościami nan, ale uaktualnienie ostatniej linii funkcji 'roll' w celu użycia' .to_frame (filter_observations = False) 'naprawiło mój problem. – user338714

+0

Jest to pomocne. Ale czy istnieje sposób, aby kolumna "roll" zachowała oryginalny indeks? na przykład 0,46 0,47 jest zawsze związane z "1". Dziękuję Ci. – iwbabn

1

złożył następujące modyfikacje powyższej odpowiedzi, ponieważ musiałem powrócić całe okno toczenia jak to się dzieje w pd.DataFrame.rolling()

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)