2017-05-27 2 views
0

Ich versuche (in ok) alle Gruppen in einem Pandas DataFrame zu markieren, die kleiner als 'N' sind. Ich habe eine funktionierende Lösung, aber es ist langsam, gibt es eine Möglichkeit, dies zu beschleunigen?Markieren aller Gruppen in Datenrahmen kleiner als N

import pandas as pd 

df = pd.DataFrame([ 
    [1, 2, 1], 
    [1, 2, 2], 
    [1, 2, 3], 
    [2, 3, 1], 
    [2, 3, 2], 
    [4, 5, 1], 
    [4, 5, 2], 
    [4, 5, 3], 
], columns=['x', 'y', 'z']) 


keys = ['x', 'y'] 
N = 3 

df['ok'] = True 
c = df.groupby(keys)['ok'].count() 
for vals in c[c < N].index: 
    local_dict = dict(zip(keys, vals)) 
    query = ' & '.join(f'{key}[email protected]{key}' for key in keys) 
    idx = df.query(query, local_dict=local_dict).index 
    df.loc[idx, 'ok'] = False 
print(df) 

Antwort

4

Statt groupby/count zu verwenden, verwenden groupby/transform/count eine Serie zu bilden, die die gleiche Länge wie das ursprüngliche Datenrahmen ist df:

c = df.groupby(keys)['z'].transform('count') 

Dann können Sie eine boolean Maske bilden, die die gleiche Länge hat als df:

In [35]: c<N 
Out[35]: 
0 False 
1 False 
2 False 
3  True 
4  True 
5 False 
6 False 
7 False 
Name: ok, dtype: bool 

Zuordnung zu 01.230.geht viel glatter nun, ohne eine Schleife, Abfrage oder Unterindexierungs:

df['ok'] = c >= N 

import pandas as pd 

df = pd.DataFrame([ 
    [1, 2, 1], 
    [1, 2, 2], 
    [1, 2, 3], 
    [2, 3, 1], 
    [2, 3, 2], 
    [4, 5, 1], 
    [4, 5, 2], 
    [4, 5, 3], 
], columns=['x', 'y', 'z']) 


keys = ['x', 'y'] 
N = 3 

c = df.groupby(keys)['z'].transform('count') 
df['ok'] = c >= N 
print(df) 

Ausbeuten

x y z  ok 
0 1 2 1 True 
1 1 2 2 True 
2 1 2 3 True 
3 2 3 1 False 
4 2 3 2 False 
5 4 5 1 True 
6 4 5 2 True 
7 4 5 3 True 

Da die builtin groupby/transform methods (wie transform('count')) sind Cythonized sie sind im Allgemeinen schneller als Aufruf groupby/transform mit einer benutzerdefinierten Lambda-Funktion. So , Berechnen der ok Spalte in zwei Schritten

c = df.groupby(keys)['z'].transform('count') 
df['ok'] = c >= N 

Verwendung schneller als

df.assign(ok=df.groupby(keys)['z'].transform(lambda x: x.size >= N)) 

Zusätzlich vektorisierten Operationen über eine gesamte Spalte (wie c >= N) sind schneller als mehrere Operationen über Untergruppen. transform(lambda x: x.size >= N)) führt den Vergleich x.size >= N einmal für jede Gruppe durch. Wenn viele Gruppen vorhanden sind, führt die Berechnung c >= N zu einer Leistungsverbesserung.


Zum Beispiel mit diesem 1000-Reihe Datenrahmen:

import numpy as np 
import pandas as pd 
np.random.seed(2017) 
df = pd.DataFrame(np.random.randint(10, size=(1000, 3)), columns=['x', 'y', 'z']) 
keys = ['x', 'y'] 
N = 3 

transform('count') verwenden, sind etwa 12-fach schneller:

In [37]: %%timeit 
    ....: c = df.groupby(keys)['z'].transform('count') 
    ....: df['ok'] = c >= N 
1000 loops, best of 3: 1.69 ms per loop 

In [38]: %timeit df.assign(ok=df.groupby(keys)['z'].transform(lambda x: x.size >= N)) 
1 loop, best of 3: 20.2 ms per loop 

In [39]: 20.2/1.69 
Out[39]: 11.95266272189349 

Im Beispiel oben gab es 100 Gruppen:

In [47]: df.groupby(keys).ngroups 
Out[47]: 100        

Der Geschwindigkeitsvorteil der Verwendung von transform('count') nimmt zu, wenn die Anzahl der Gruppen zunimmt.Zum Beispiel mit 955 Gruppen:

In [48]: np.random.seed(2017); df = pd.DataFrame(np.random.randint(100, size=(1000, 3)), columns=['x', 'y', 'z']) 

In [51]: df.groupby(keys).ngroups 
Out[51]: 955 

die transform('count') Methode führt zu 92x schneller:

In [49]: %%timeit 
    ....: c = df.groupby(keys)['z'].transform('count') 
    ....: df['ok'] = c >= N 
1000 loops, best of 3: 1.88 ms per loop 

In [50]: %timeit df.assign(ok=df.groupby(keys)['z'].transform(lambda x: x.size >= N)) 
10 loops, best of 3: 173 ms per loop 

In [52]: 173/1.88 
Out[52]: 92.02127659574468 
+0

Großen und Details beantworten - danke! – lazy1

1

Eingangsgrößen:

keys = ['x','y'] 
N = 3 

berechnen in Ordnung oder nicht mit groupby, transform und size:

df.assign(ok=df.groupby(keys)['z'].transform(lambda x: x.size >= N)) 

Ausgang:

x y z  ok 
0 1 2 1 True 
1 1 2 2 True 
2 1 2 3 True 
3 2 3 1 False 
4 2 3 2 False 
5 4 5 1 True 
6 4 5 2 True 
7 4 5 3 True 
Verwandte Themen