2016-03-29 8 views
3

Ich habe einen sehr großen Pandas-Datensatz, in dem die Daten aussehenWie beschleunigt man das Ersetzen von fehlenden Werten für jede Gruppe durch Gruppen in Pandas?

df = pd.DataFrame({'group1' : ['A', 'A', 'A', 'A', 
         'B', 'B', 'B', 'B'], 
        'group2' : ['C', 'C', 'C', 'D', 
         'E', 'E', 'F', 'F'], 
        'B' : ['one', np.NaN, np.NaN, np.NaN, 
         np.NaN, 'two', np.NaN, np.NaN], 
        'C' : [np.NaN, 1, np.NaN, np.NaN, 
         np.NaN, np.NaN, np.NaN, 4]})  




df 
Out[64]: 
    B C group1 group2 
0 one NaN  A  C 
1 NaN 1  A  C 
2 NaN NaN  A  C 
3 NaN NaN  A  D 
4 NaN NaN  B  E 
5 two NaN  B  E 
6 NaN NaN  B  F 
7 NaN 4  B  F 

Hier können Sie sehen, dass, für jede einzelne Kombination von group1 und group2, die Spalten B und C enthält höchstens eine nicht-fehlende Variable .

Innerhalb jeder groupby(['group1','group2']) Gruppe ersetze ich alle fehlenden Werte, indem ich diesen eindeutigen, nicht fehlenden Wert (in dieser Gruppe) verwende, wenn dieser Wert existiert.

dies zu tun, verwende ich die first Funktion verfügbar, nachdem ein groupby, die den ersten nichtfehlender Wert für B oder C in jeder Gruppe der fehlenden Werte in der Gruppe an den Rest ausbreitet:

df[['B','C']]=df.groupby(['group1','group2']).transform('first')  



df 
Out[62]: 
    B C group1 group2 
0 one 1  A  C 
1 one 1  A  C 
2 one 1  A  C 
3 NaN NaN  A  D 
4 two NaN  B  E 
5 two NaN  B  E 
6 NaN 4  B  F 
7 NaN 4  B  F 

Leider ist dies schmerzhaft langsam auf meinem sehr großen Datensatz. Siehst du irgendeinen Weg, die Geschwindigkeit hier zu verbessern? Ich dachte an fillna, aber es scheint, ich würde es zweimal anwenden müssen (ffill und bfill) ... Irgendwelche Ideen?

UPDATE Würde die von ajcr vorgeschlagene sehr effiziente Lösung mit Gruppen arbeiten, die durch mehrere Spalten definiert sind? map funktioniert in diesem Fall nicht. Vielleicht merge?

Antwort

3

Auf meinem Rechner, kann es fast 100-mal schneller groupby und dann map zu verwenden:

g = df.groupby('group', sort=False).first() 

df['B'] = df['group'].map(g['B']) 
df['C'] = df['group'].map(g['C']) 

Hier ist ein Test Datenrahmen mit 1000 Gruppen und 10000 Zeilen:

df = pd.DataFrame({'group': np.repeat(np.arange(1000), 10), 
        'B': np.nan, 
        'C': np.nan}) 

df.ix[4::10, 'B':'C'] = 5 # every 4th row of a group is non-null 

und die Zeitpunkte:

%%timeit 

df2 = df.copy() 

g = df2.groupby('group', sort=False).first() 

df2['B'] = df2['group'].map(g['B']) 
df2['C'] = df2['group'].map(g['C']) 

Dies gibt 100 loops, best of 3: 2.29 ms per loop zurück .

Die transform Methode ist fast 100-mal langsamer:

%%timeit 

df3 = df.copy() 

df3[['B','C']] = df3.groupby('group').transform('first') 

Das gibt 1 loops, best of 3: 205 ms per loop.


Im Hinblick auf Sie Frage aktualisiert mehrere Gruppen über Verwendung @ Jeffs Vorschlag in den Kommentaren unten verwenden

df['B'] = df.groupby(['group1','group2']).B.transform('first') 
df['C'] = df.groupby(['group1','group2']).C.transform('first') 

ist etwa 50-mal schneller als beide Spalten in einem Rutsch zu verwandeln. Dies liegt daran, dass transform derzeit für die Serie viel leistungsfähiger ist, obwohl es auch eine newly-created issue gibt, um die Geschwindigkeit der Operation auf DataFrames zu erhöhen.

+0

Genie finden. Lemme versuchen Sie diese Lösung –

+0

können Sie erklären, was ist die 'map' hier tun? –

+1

Ich spielte mit etwas ähnlich wie a [['B', 'C']] = a.apply (lambda x: g.loc [x.gruppe, ['B', 'C']], axis = 1) ', aber es war viel langsamer. So +1 für Ihre Lösung – MaxU

1

Lassen Sie uns die Geschwindigkeit sie ein wenig nach oben:

In [130]: a = df.copy() 

In [131]: %timeit a['B'],a['C'] = a.B.fillna(method='ffill'), a.C.fillna(method='bfill') 
1000 loops, best of 3: 538 µs per loop 

In [132]: a = df.copy() 

In [133]: %timeit a[['B','C']]=a.groupby('A').transform('first') 
100 loops, best of 3: 3 ms per loop 
+0

danke maxU aber wir wissen nicht genau, wo ist der nicht fehlende Wert in jeder Gruppe. Mit anderen Worten, funktioniert nur ein 'ffill' auf B nicht. Deshalb habe ich auf jeder Spalte ein doppeltes "ffill" und "bfill" erwähnt ... –

+1

@Noobie, könnten Sie bitte Ihre Probe DF entsprechend aktualisieren? – MaxU

+0

sicher eine Sekunde –

1

Was passiert, wenn Sie dies ging über unterschiedlich und nicht versuchen, und zu füllen, sondern wieder aufgebaut?

unique_df = df.drop_duplicates() 

bVal = unique_df.drop(['B'],axis = 1).dropna().set_index(['A']) 
cVal = unique_df.drop(['C'],axis = 1).dropna().set_index(['A']) 

colVals = pd.merge(bVal,cVal, how = 'outer',left_index = True, right_index = True) 
output = pd.merge(df[['A']],colVals, how = 'left',left_on = 'A',right_index = True) 

Shrink die df nach unten auf die einzigartigen Teile finden die einzigartigen Reihen auf dem kleineren Datenrahmen, dann verschmelzen wieder auf größeren Rahmen neu aufzubauen - schneller?

Verwandte Themen