2017-07-14 22 views
2

Lassen Sie uns sagen, dass ich die folgende Pandas Datenrahmen haben:Pandas: Operationen mit groupby Ausbeute SettingWithCopyWarning

df = pd.DataFrame({ 
    'team': ['Warriors', 'Warriors', 'Warriors', 'Rockets', 'Rockets'], 
    'player': ['Stephen Curry', 'Klay Thompson', 'Kevin Durant', 'Chris Paul', 'James Harden']}) 

Wenn ich zu einer Gruppe auf der team Spalte versuchen, eine Operation durchführen erhalte ich eine SettingWithCopyWarning:

for team, team_df in df.groupby(by='team'): 
    # team_df = team_df.copy() # produces no warning 
    team_df['rank'] = 10 # produces warning 
    team_df.loc[:, 'rank'] = 10 # produces warning 

SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. 
Try using .loc[row_index,col_indexer] = value instead 
df_team['rank'] = 10 

Wenn ich die Zeile auskommentiere, die eine Kopie des Sub-DataFrame erzeugt, bekomme ich den Fehler nicht. Ist das im Allgemeinen die beste Vorgehensweise, um diese Warnung zu vermeiden, oder mache ich etwas falsch?

Hinweis Ich möchte den ursprünglichen Dataframe df nicht bearbeiten. Ich weiß auch, dass dieses Beispiel besser gemacht werden kann, aber mein Anwendungsfall ist viel komplexer und erfordert die Gruppierung eines originalen DataFrame und das Ausführen einer Reihe von Operationen basierend auf einem anderen DataFrame und den Spezifikationen dieser einzigartigen Gruppe.

Antwort

4

Sobald Sie this article grok und sind sicher, dass Sie wissen, wie verkettete Indizierung zu vermeiden (durch Verwendung von .loc oder iloc), dann können Sie die SettingWithCopyWarning mit pd.options.mode.chained_assignment = None und nie durch diese Warnung auszuschalten immer wieder gestört werden.

Da Sie

Hinweis schrieben ich will nicht den ursprünglichen Datenrahmen bearbeiten df

und Sie richtig .loc verwenden zu team_df zuweisen, ist es klar, dass man bereits weiß, dass Ändern der Kopie (team_df) wird das Original (df) nicht ändern, so dass die SettingWithCopyWarning emittiert hier nur ein Ärgernis ist.

Die SettingWithCopyWarning kommt in allen möglichen Situationen, wo Sie richtig Codierung sind, auch mit .loc oder .iloc. Es gibt keinen "richtigen" Weg, um zu kodieren, der vermeidet, manchmal SettingWithCopyWarning s auszulösen.

Deshalb würde ich einfach abschalten diese Warnung global mit

pd.options.mode.chained_assignment = None 

würde ich in der Regel nicht nur mit team_df = team_df.copy() empfehlen zu vermeiden SettingWithCopyWarning s - ein Datenrahmen Kopieren kann eine Belastung für Leistung sein insbesondere wenn der Datenrahmen groß ist oder wenn er viele Male in einer Schleife ausgeführt wird.

Wenn Sie turn off the warning in just one location möchten, können Sie

team_df.is_copy = False 

verwenden Es dient dem gleichen Zweck, aber nicht eine Leistung Drain sein. Beachten Sie jedoch, , dass is_copy ist nicht in der offiziellen Pandas API erwähnt, so kann es nicht garantiert zu existieren oder für diesen Zweck in allen zukünftigen Versionen von Pandas nützlich sein. Wenn Robustheit eine Priorität ist, aber die Leistung nicht ist, dann verwenden Sie vielleicht team_df = team_df.copy().Aber ich denke, der solidere Weg für einen erfahrenen Pandas Programmierer zu gehen ist, entweder die Warnung global auszuschalten oder - wenn Sie sehr vorsichtig sein wollen - halten Sie die Warnungen, überprüfen Sie sie manuell, aber akzeptieren Sie , dass es manchmal durch richtigen Code ausgelöst werden.

+0

Great Link zum Artikel! – piRSquared

+0

@piRSquared: Ich habe von [dem Artikel] (https://www.dataquest.io/blog/settingwithcopywarning/) von [Alexander] (https://stackoverflow.com/users/2411802/alexander) [hier] erfahren (https://stackoverflow.com/questions/38809796/pandas-still-getting-settingwithcopywarning-even-after-using-loc/38810015#comment76884959_38809796). – unutbu

0

Die pandas split apply combine docs sind nicht gut darin. Dies sollte Sie in die richtige Richtung zeigen

def apply_fun(team_df): 
    team_df['rank'] = 10 
    return team_df 

df.groupby('team').apply(apply_fun) 
df['column_rank'] = df.groupby('team')['column'].transform(lambda x: x.rank())