perché dovrei fare una copia di un frame di dati in Panda


190

Durante la selezione di un sub frame di dati da un frame di dati principale, ho notato che alcuni programmatori eseguono una copia del frame di dati utilizzando il .copy()metodo. Per esempio,

X = my_dataframe[features_list].copy()

... invece di solo

X = my_dataframe[features_list]

Perché stanno facendo una copia del frame di dati? Cosa succederà se non ne faccio una copia?


6
La mia ipotesi è che stiano prendendo ulteriori precauzioni per non modificare il frame di dati di origine. Probabilmente non necessario, ma quando si mette insieme qualcosa in modo interattivo, è meglio prevenire che curare.
Paolo H,

8
Presumo che questa non sia una domanda stupida per averne una negativa.
Elizabeth Susan Joseph,

Risposte:


208

Questo si espande sulla risposta di Paul. In Panda, l'indicizzazione di un DataFrame restituisce un riferimento al DataFrame iniziale. Pertanto, la modifica del sottoinsieme modificherà il DataFrame iniziale. Pertanto, si desidera utilizzare la copia se si desidera assicurarsi che il DataFrame iniziale non debba cambiare. Considera il seguente codice:

df = DataFrame({'x': [1,2]})
df_sub = df[0:1]
df_sub.x = -1
print(df)

Otterrai:

x
0 -1
1  2

Al contrario, le seguenti foglie rimangono invariate:

df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1

6
è una copia profonda?
bikashg,

6
Sì. La modalità predefinita è la copia "profonda"! pandas.pydata.org/pandas-docs/stable/reference/api/…
Ambareesh

45

Perché se non ne fai una copia, allora gli indici possono ancora essere manipolati altrove anche se assegni dataFrame a un nome diverso.

Per esempio:

df2 = df
func1(df2)
func2(df)

func1 può modificare df modificando df2, in modo da evitare che:

df2 = df.copy()
func1(df2)
func2(df)

Aspetta, aspetta, puoi spiegare PERCHÉ questo accade? Non ha senso.
NoName

2
è perché nel primo esempio, `df2 = df , both variables reference the same DataFrame instance. So any changes made to df` o df2verrà creato nella stessa istanza di oggetto. Mentre nella df2 = df.copy()seconda istanza di un oggetto viene creata, una copia della prima, ma ora dfe df2riferimento a diverse istanze di oggetti e qualsiasi modifica verrà apportata alla rispettiva istanza DataFrame.
Pedro,

17

È necessario menzionare che la copia o la visualizzazione di ritorno dipende dal tipo di indicizzazione.

La documentazione dei panda dice:

Restituzione di una vista rispetto a una copia

Le regole su quando viene restituita una vista sui dati dipendono interamente da NumPy. Ogni volta che una matrice di etichette o un vettore booleano sono coinvolti nell'operazione di indicizzazione, il risultato sarà una copia. Con l'indicizzazione e lo slicing di etichette singole / scalari, ad esempio df.ix [3: 6] o df.ix [:, 'A'], verrà restituita una vista.



12

Lo scopo principale è evitare l'indicizzazione concatenata ed eliminare il file SettingWithCopyWarning.

Qui l'indicizzazione concatenata è qualcosa di simile dfc['A'][0] = 111

Il documento afferma che l'indicizzazione concatenata dovrebbe essere evitata in Restituzione di una vista rispetto a una copia . Ecco un esempio leggermente modificato da quel documento:

In [1]: import pandas as pd

In [2]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})

In [3]: dfc
Out[3]:
    A   B
0   aaa 1
1   bbb 2
2   ccc 3

In [4]: aColumn = dfc['A']

In [5]: aColumn[0] = 111
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [6]: dfc
Out[6]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

Qui aColumnè una vista e non una copia del DataFrame originale, quindi la modifica aColumncomporterà anche la dfcmodifica dell'originale . Quindi, se prima indicizziamo la riga:

In [7]: zero_row = dfc.loc[0]

In [8]: zero_row['A'] = 222
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [9]: dfc
Out[9]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

Questa volta zero_rowè una copia, quindi l'originale dfcnon viene modificato.

Da questi due esempi precedenti, vediamo ambiguo se si desidera modificare il DataFrame originale. Ciò è particolarmente pericoloso se scrivi qualcosa di simile al seguente:

In [10]: dfc.loc[0]['A'] = 333
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [11]: dfc
Out[11]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

Questa volta non ha funzionato affatto. Qui volevamo cambiaredfc , ma in realtà abbiamo modificato un valore intermedio dfc.loc[0]che è una copia e viene immediatamente scartato. È molto difficile prevedere se il valore intermedio gradisce dfc.loc[0]o dfc['A']è una vista o una copia, quindi non è garantito se DataFrame originale verrà aggiornato. Ecco perché è necessario evitare l'indicizzazione concatenata e Panda genera SettingWithCopyWarningquesto tipo di aggiornamento dell'indicizzazione concatenata.

Ora è l'uso di .copy(). Per eliminare l'avviso, creane una copia per esprimere esplicitamente la tua intenzione:

In [12]: zero_row_copy = dfc.loc[0].copy()

In [13]: zero_row_copy['A'] = 444 # This time no warning

Poiché stai modificando una copia, sai che l'originale dfcnon cambierà mai e non ti aspetti che cambi. Le tue aspettative corrispondono al comportamento, quindi SettingWithCopyWarningscompare.

Nota, se si desidera modificare il DataFrame originale, il documento suggerisce di utilizzare loc:

In [14]: dfc.loc[0,'A'] = 555

In [15]: dfc
Out[15]:
    A   B
0   555 1
1   bbb 2
2   ccc 3

2

In generale, è più sicuro lavorare sulle copie che sui frame di dati originali, tranne quando si sa che non sarà più necessario l'originale e si desidera procedere con la versione manipolata. Normalmente, avresti ancora qualche utilità per il frame di dati originale da confrontare con la versione manipolata, ecc. Pertanto, la maggior parte delle persone lavora sulle copie e si fonde alla fine.


0

Supponiamo di avere un frame di dati come di seguito

df1
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0

Quando desideri crearne un altro df2identico df1, senzacopy

df2=df1
df2
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0

E vorrei modificare il valore di df2 solo come di seguito

df2.iloc[0,0]='changed'

df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

Allo stesso tempo, anche il df1 viene modificato

df1
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

Dato che due df uguali object, possiamo controllarlo usando ilid

id(df1)
140367679979600
id(df2)
140367679979600

Quindi sono lo stesso oggetto e uno cambia un altro passerà anche lo stesso valore.


Se aggiungiamo il copy, e ora df1e df2siamo considerati diversi object, se facciamo la stessa modifica a uno di essi, l'altro non cambierà.

df2=df1.copy()
id(df1)
140367679979600
id(df2)
140367674641232

df1.iloc[0,0]='changedback'
df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

Buono a dirsi, quando si imposta un sottoinsieme del frame di dati originale, è sicuro aggiungere anche la copia per evitare il file SettingWithCopyWarning

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.