2017-01-11 48 views
7

Rozważmy przykład, w którym to pandy mam kolumnę obliczeniową C przez pomnożenie A z B i float gdy pewien warunek jest spełniony przy użyciu apply z lambda funkcję:Pandy: jak szybciej wnioskować o ramkę danych?

import pandas as pd 
df = pd.DataFrame({'A':[1,2,3,4,5,6,7,8,9],'B':[9,8,7,6,5,4,3,2,1]}) 

df['C'] = df.apply(lambda x: x.A if x.B > 5 else 0.1*x.A*x.B, axis=1) 

Oczekiwany wynik będzie:

A B C 
0 1 9 1.0 
1 2 8 2.0 
2 3 7 3.0 
3 4 6 4.0 
4 5 5 2.5 
5 6 4 2.4 
6 7 3 2.1 
7 8 2 1.6 
8 9 1 0.9 

Problem polega na tym, że ten kod jest powolny i muszę wykonać tę operację na ramie danych z około 56 milionami wierszy.

%timeit -result powyższej operacji lambda jest:

1000 loops, best of 3: 1.63 ms per loop 

Idąc od czasu obliczeniowego, a także wykorzystanie pamięci, gdy robi to na moim dużym dataframe Przypuszczam, operacja ta wykorzystuje pośrednika serię robiąc obliczenia .

Próbowałem sformułować go na różne sposoby, w tym za pomocą kolumn tymczasowych, ale każde alternatywne rozwiązanie, które wymyśliłem, jest jeszcze wolniejsze.

Czy istnieje sposób na uzyskanie pożądanego wyniku w inny i szybszy sposób, np. za pomocą numpy?

+0

Należy spojrzeć na 'numpy.where'. – IanS

Odpowiedz

6

na wydajność, może być lepiej pracuje z numpy tablicy i korzystania np.where -

a = df.values # Assuming you have two columns A and B 
df['C'] = np.where(a[:,1]>5,a[:,0],0.1*a[:,0]*a[:,1]) 

Runtime testowych

def numpy_based(df): 
    a = df.values # Assuming you have two columns A and B 
    df['C'] = np.where(a[:,1]>5,a[:,0],0.1*a[:,0]*a[:,1]) 

taktowanie -

In [271]: df = pd.DataFrame(np.random.randint(0,9,(10000,2)),columns=[['A','B']]) 

In [272]: %timeit numpy_based(df) 
1000 loops, best of 3: 380 µs per loop 

In [273]: df = pd.DataFrame(np.random.randint(0,9,(10000,2)),columns=[['A','B']]) 

In [274]: %timeit df['C'] = df.A.where(df.B.gt(5), df[['A', 'B']].prod(1).mul(.1)) 
100 loops, best of 3: 3.39 ms per loop 

In [275]: df = pd.DataFrame(np.random.randint(0,9,(10000,2)),columns=[['A','B']]) 

In [276]: %timeit df['C'] = np.where(df['B'] > 5, df['A'], 0.1 * df['A'] * df['B']) 
1000 loops, best of 3: 1.12 ms per loop 

In [277]: df = pd.DataFrame(np.random.randint(0,9,(10000,2)),columns=[['A','B']]) 

In [278]: %timeit df['C'] = np.where(df.B > 5, df.A, df.A.mul(df.B).mul(.1)) 
1000 loops, best of 3: 1.19 ms per loop 

Bliżej

Weźmy bliżej przyjrzeć się liczbie NumPy za pożerające możliwości i porównać z pand język mix -

# Extract out as array (its a view, so not really expensive 
# .. as compared to the later computations themselves) 

In [291]: a = df.values 

In [296]: %timeit df.values 
10000 loops, best of 3: 107 µs per loop 

Case # 1: Praca z numpy tablicy i używać numpy.gdzie:

In [292]: %timeit np.where(a[:,1]>5,a[:,0],0.1*a[:,0]*a[:,1]) 
10000 loops, best of 3: 86.5 µs per loop 

Ponownie, przypisanie do nowej kolumny: df['C'] nie byłoby bardzo drogie -

In [300]: %timeit df['C'] = np.where(a[:,1]>5,a[:,0],0.1*a[:,0]*a[:,1]) 
1000 loops, best of 3: 323 µs per loop 

Case # 2: Praca z pandy dataframe i używać jej .where metody (nie NumPy)

In [293]: %timeit df.A.where(df.B.gt(5), df[['A', 'B']].prod(1).mul(.1)) 
100 loops, best of 3: 3.4 ms per loop 

Case # 3: Praca z dataframe pandy (bez tablicy NumPy), ale użyć numpy.where -

In [294]: %timeit np.where(df['B'] > 5, df['A'], 0.1 * df['A'] * df['B']) 
1000 loops, best of 3: 764 µs per loop 

Case # 4: Praca z pandy dataframe ponownie (bez tablicy NumPy), ale użyć numpy.where -

In [295]: %timeit np.where(df.B > 5, df.A, df.A.mul(df.B).mul(.1)) 
1000 loops, best of 3: 830 µs per loop 
+0

Uderzyłeś mnie, ale "numpy.where" gra dobrze z serią pand i wierzę, że moja wersja jest bardziej czytelna;) – IanS

+1

@IanS OP poprosił o więcej. Dzięki temu wszystko staje się numpy, co zwiększa wydajność. – piRSquared

+0

@IanS NumPy gra dobrze z chrupaniem numerów iz pewnością z ramkami danych, przynajmniej to wiem! ;) – Divakar

3

Korzystanie numpy.where:

df['C'] = numpy.where(df['B'] > 5, df['A'], 0.1 * df['A'] * df['B']) 
2

Zastosowanie:

df['C'] = np.where(df.B > 5, df.A, df.A.mul(df.B).mul(.1)) 
print (df) 
    A B C 
0 1 9 1.0 
1 2 8 2.0 
2 3 7 3.0 
3 4 6 4.0 
4 5 5 2.5 
5 6 4 2.4 
6 7 3 2.1 
7 8 2 1.6 
8 9 1 0.9 
+0

'mul' nie różni się zbytnio od' * ', czyż nie? ;) – IanS

+1

Przeprowadzam badania i wydaje mi się, że jeśli użyjesz 'df.A * df.B' i' df.A.mul (df.B) 'to' mul' jest szybsze. Ale jeśli wielokrotność przez stałą, jest taka sama. – jezrael

+1

@IanS również, jest wygodny do łączenia – piRSquared

4

pure pandas
użyciu pd.Series.where

df['C'] = df.A.where(df.B.gt(5), df[['A', 'B']].prod(1).mul(.1)) 

    A B C 
0 1 9 1.0 
1 2 8 2.0 
2 3 7 3.0 
3 4 6 4.0 
4 5 5 2.5 
5 6 4 2.4 
6 7 3 2.1 
7 8 2 1.6 
8 9 1 0.9 
+0

nice, prod jest szybszy jak mul? – jezrael

+0

może ... ledwie – piRSquared

+0

Zastanawiam się, dlaczego jest 3 razy wolniejsza niż inne odpowiedzi ... – IanS