¿Por qué debería hacer una copia de un marco de datos en pandas?

Resuelto Elizabeth Susan Joseph asked hace 9 años • 8 respuestas

Al seleccionar un marco de datos secundario de un marco de datos principal, noté que algunos programadores hacen una copia del marco de datos usando el .copy()método. Por ejemplo,

X = my_dataframe[features_list].copy()

...en lugar de sólo

X = my_dataframe[features_list]

¿Por qué están haciendo una copia del marco de datos? ¿Qué pasará si no hago una copia?

Elizabeth Susan Joseph avatar Dec 28 '14 09:12 Elizabeth Susan Joseph
Aceptado

Esta respuesta ha quedado obsoleta en las versiones más recientes de pandas. Ver documentos


Esto amplía la respuesta de Paul. En Pandas, indexar un DataFrame devuelve una referencia al DataFrame inicial. Por lo tanto, cambiar el subconjunto cambiará el DataFrame inicial. Por lo tanto, querrás usar la copia si quieres asegurarte de que el DataFrame inicial no cambie. Considere el siguiente código:

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

Obtendrás:

   x
0 -1
1  2

Por el contrario, lo siguiente deja df sin cambios:

df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1
cgold avatar Dec 28 '2014 20:12 cgold

Porque si no hace una copia, los índices aún se pueden manipular en otros lugares incluso si asigna el marco de datos a un nombre diferente.

Por ejemplo:

df2 = df
func1(df2)
func2(df)

func1 puede modificar df modificando df2, así que para evitar eso:

df2 = df.copy()
func1(df2)
func2(df)
sparrow avatar Sep 22 '2016 01:09 sparrow

Es necesario mencionar que devolver una copia o vista depende del tipo de indexación.

La documentación de pandas dice:

Devolver una vista versus una copia

Las reglas sobre cuándo se devuelve una vista de los datos dependen completamente de NumPy. Siempre que una matriz de etiquetas o un vector booleano estén involucrados en la operación de indexación, el resultado será una copia. Con indexación y división de etiqueta única/escalar, por ejemplo, df.ix[3:6] o df.ix[:, 'A'], se devolverá una vista.

Gusev Slava avatar Jan 20 '2017 13:01 Gusev Slava

El objetivo principal es evitar la indexación encadenada y eliminar el archivo SettingWithCopyWarning.

Aquí la indexación encadenada es algo así comodfc['A'][0] = 111

El documento decía que se debe evitar la indexación encadenada al devolver una vista frente a una copia . Aquí hay un ejemplo ligeramente modificado de ese 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

Aquí aColumnhay una vista y no una copia del DataFrame original, por lo que la modificación aColumnhará que el original dfctambién se modifique. A continuación, si indexamos la fila primero:

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

Esta vez zero_rowes una copia, por lo que el original dfcno se modifica.

A partir de estos dos ejemplos anteriores, vemos que es ambiguo si desea o no cambiar el DataFrame original. Esto es especialmente peligroso si escribe algo como lo siguiente:

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

Esta vez no funcionó en absoluto. Aquí quisimos cambiar dfc, pero en realidad modificamos un valor intermedio dfc.loc[0]que es una copia y se descarta inmediatamente. Es muy difícil predecir si el valor intermedio dfc.loc[0]es dfc['A']una vista o una copia, por lo que no se garantiza si el DataFrame original se actualizará o no. Es por eso que se debe evitar la indexación encadenada, y pandas genera la SettingWithCopyWarningactualización para este tipo de indexación encadenada.

Ahora es el uso de .copy(). Para eliminar la advertencia, haga una copia para expresar su intención de forma explícita:

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

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

Dado que está modificando una copia, sabe que el original dfcnunca cambiará y no espera que cambie. Su expectativa coincide con el comportamiento, luego SettingWithCopyWarningdesaparece.

Nota: si desea modificar el DataFrame original, el documento sugiere que utilice loc:

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

In [15]: dfc
Out[15]:
    A   B
0   555 1
1   bbb 2
2   ccc 3
Cosyn avatar Oct 22 '2018 09:10 Cosyn

Se supone que tiene un marco de datos como el siguiente

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

Cuando quieras crear otro df2que sea idéntico a df1, sincopy

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

Y me gustaría modificar el valor df2 solo como se muestra a continuación

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

Al mismo tiempo también se cambia el df1.

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

Dado que dos df son iguales object, podemos verificarlo usando elid

id(df1)
140367679979600
id(df2)
140367679979600

Entonces, son el mismo objeto y uno cambia, otro también pasará el mismo valor.


Si sumamos el copy, y ahora df1y df2se consideran diferentes object, si le hacemos el mismo cambio a uno de ellos el otro no cambiará.

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

Es bueno mencionar que cuando crea un subconjunto del marco de datos original, también es seguro agregar la copia para evitar elSettingWithCopyWarning

BENY avatar Jun 17 '2020 01:06 BENY