2016-01-19 7 views
9

Sono passato da R a Panda. Io abitualmente ottengo SettingWithCopyWarnings, quando faccio qualcosa comeQual è il punto di vista nei panda se non è definito se un'operazione di indicizzazione restituisce una vista o una copia?

df_a = pd.DataFrame({'col1': [1,2,3,4]})  

# Filtering step, which may or may not return a view 
df_b = df_a[df_a['col1'] > 1] 

# Add a new column to df_b 
df_b['new_col'] = 2 * df_b['col1'] 

# SettingWithCopyWarning!! 

credo di aver capito il problema, anche se imparerò volentieri quello che ho ottenuto sbagliato. Nell'esempio indicato, non è definito se df_b è una vista su df_a oppure no. Pertanto, l'effetto dell'assegnazione a df_b non è chiaro: ha effetto su df_a? Il problema può essere risolto facendo in modo esplicito una copia durante il filtraggio:

df_a = pd.DataFrame({'col1': [1,2,3,4]})  

# Filtering step, definitely a copy now 
df_b = df_a[df_a['col1'] > 1].copy() 

# Add a new column to df_b 
df_b['new_col'] = 2 * df_b['col1'] 

# No Warning now 

penso che ci sia qualcosa che mi manca: se non possiamo mai essere veramente sicuri se creiamo una vista o no, che cosa sono viste buone per ? Dalla documentazione panda (http://pandas-docs.github.io/pandas-docs-travis/indexing.html?highlight=view#indexing-view-versus-copy)

Al di fuori dei casi semplici, è molto difficile prevedere se esso [getitem] restituirà una vista o una copia (dipende dal layout di memoria della matrice, di cui panda non ha garanzie)

Avvertenze simili possono essere trovate per diversi metodi di indicizzazione.

Trovo molto ingombrante e errorprone di cospargere .copy() chiama attraverso il mio codice. Sto usando lo stile sbagliato per manipolare i miei DataFrames? O il guadagno di prestazioni è così alto da giustificare l'apparente imbarazzo?

+0

È possibile disattivare questo nuovo avviso con la seguente assegnazione. 'pd.options.mode.chained_assignment = None' –

+0

Hmmm, forse aiuta a ripristinare l'indice' df_b = df_a [df_a ['col1']> 1] .reset_index (drop = True) '. – jezrael

+2

@GeorgePetrov Vorrei fortemente suggerire di disabilitare questo! L'avviso si presenta per una buona ragione: se mai, in realtà ti suggerirei di promuoverlo su un'eccezione anziché un avvertimento. –

risposta

10

Ottima domanda!

La risposta breve è: questo è un difetto nei panda a cui viene posto rimedio.

È possibile trovare una discussione più lunga sulla natura di the problem here, ma il principale take-away è che ora stiamo passando a un comportamento "copia su scrittura" in cui ogni volta che si divide, si ottiene un nuovo copia e non devi mai pensare alle visualizzazioni. La correzione arriverà presto attraverso questo refactoring project. In realtà ho provato a correggerlo direttamente (see here), ma non era fattibile nell'architettura corrente.

In realtà, manterremo le visualizzazioni in background - rendono la memoria SUPER Panda efficiente e veloce quando possono essere fornite - ma finiremo per nasconderle dagli utenti, quindi, dal punto di vista dell'utente, se taglia, indicizza o taglia un DataFrame, ciò che ottieni tornerà a essere una nuova copia.

(Questo si ottiene creando viste quando l'utente sta solo leggendo i dati, ma ogni volta che viene utilizzato un'operazione di assegnazione, la visualizzazione verrà convertito in una copia prima della cessione avvenga.)

ipotesi migliore è il la correzione sarà tra un anno - nel frattempo, ho paura che alcuni .copy() possano essere necessari, mi dispiace!

2

Sono d'accordo che è un po 'strano. La mia pratica attuale è quella di cercare un metodo "funzionale" per qualsiasi cosa io voglia fare (nella mia esperienza questi esistono quasi sempre ad eccezione della ridenominazione di colonne e serie). A volte rende il codice più elegante, a volte peggiora (non mi piace lo assign con lambda), ma almeno non devo preoccuparmi della mutabilità.

Così per l'indicizzazione, invece di usare la notazione fetta, è possibile utilizzare query che restituirà una copia di default:

In [5]: df_a.query('col1 > 1') 
Out[5]: 
    col1 
1  2 
2  3 
3  4 

ho espandere su di esso un po 'in this blog post.

Edit: Come indicato nei commenti, mi sembra che mi stia sbagliando su query restituendo una copia per impostazione predefinita, tuttavia se si utilizza lo stile assign, quindi assegnare eseguirà una copia prima di restituire il risultato, e tutto va bene:

df_b = (df_a.query('col1 > 1') 
      .assign(newcol = 2*df_a['col1'])) 
+0

Perché la sequenza: 'df_b = df_a.query ('col1> 1')' seguita da 'df_b ['new_col'] = 2 * df_b ['col1']' restituisce ancora SettingWithCopyWarning? – screenpaver

+0

@maxymoo: questo risponde alla seconda parte della mia domanda: uno stile di programmazione per evitare problemi di SettingWithCopy. Grazie! Mi è piaciuto molto il tuo post sul blog! Puoi rispondere alla domanda da screenpaver? Penso che la maggior parte dei suggerimenti nel post del tuo blog funzionino molto bene, ma .query() non sembra restituire una copia in tutti i casi! Quindi posso filtrare in una catena di metodi? – sjk

+0

@screenpaver e skj, la mia risposta aggiornata è di aiuto? – maxymoo

Problemi correlati