python - drop - ¿Por qué debería hacer una copia de un marco de datos en pandas?
pandas plot (5)
El objetivo principal es evitar la indexación encadenada y eliminar la
SettingWithCopyWarning
.
Aquí la indexación encadenada es algo así como
dfc[''A''][0] = 111
El documento dice que se debe evitar la indexación encadenada al devolver una vista versus 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í
aColumn
es una vista y no una copia del DataFrame original, por lo que modificar
aColumn
también modificará el
aColumn
original.
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_row
es una copia, por lo que el
dfc
original no se modifica.
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í queríamos cambiar
dfc
, pero en realidad modificamos un valor intermedio
dfc.loc[0]
que es una copia y se descarta de inmediato.
Es muy difícil predecir si el valor intermedio como
dfc.loc[0]
o
dfc[''A'']
es una vista o una copia, por lo que no se garantiza si DataFrame original se actualizará o no.
Es por eso que se debe evitar la indexación encadenada, y los pandas generan el
SettingWithCopyWarning
para este tipo de actualización de indexación encadenada.
Ahora es el uso de
.copy()
.
Para eliminar la advertencia, haga una copia para expresar su intención explícitamente:
In [12]: zero_row_copy = dfc.loc[0].copy()
In [13]: zero_row_copy[''A''] = 444 # This time no warning
Como está modificando una copia, sabe que el
dfc
original nunca cambiará y no espera que cambie.
Su expectativa coincide con el comportamiento, luego desaparece
SettingWithCopyWarning
.
Tenga en cuenta que si desea modificar el DataFrame original, el documento sugiere que use
loc
:
In [14]: dfc.loc[0,''A''] = 555
In [15]: dfc
Out[15]:
A B
0 555 1
1 bbb 2
2 ccc 3
Al seleccionar un subtrama de datos de un marco de datos primario, noté que algunos programadores hacen una copia del marco de datos utilizando el método
.copy()
.
¿Por qué están haciendo una copia del marco de datos? ¿Qué pasará si no hago una copia?
En general, es más seguro trabajar en copias que en marcos de datos originales, excepto cuando sabe que ya no necesitará el original y desea continuar con la versión manipulada. Normalmente, aún tendría algún uso para que el marco de datos original se compare con la versión manipulada, etc. Por lo tanto, la mayoría de las personas trabajan en copias y se fusionan al final.
Es necesario mencionar que devolver la copia o la vista depende del tipo de indexación.
La documentación de los 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 una sola etiqueta / indexación y corte escalar, por ejemplo, df.ix [3: 6] o df.ix [:, ''A''], se devolverá una vista.
Esto amplía la respuesta de Pablo. En Pandas, la indexación de un DataFrame devuelve una referencia al DataFrame inicial. Por lo tanto, cambiar el subconjunto cambiará el DataFrame inicial. Por lo tanto, querrá usar la copia si desea asegurarse 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
En contraste, lo siguiente deja df sin cambios:
df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1
Porque si no hace una copia, los índices aún pueden manipularse en otro lugar, incluso si asigna el dataFrame a un nombre diferente.
Por ejemplo:
df2 = df
func1(df2)
func2(df)
func1 puede modificar df modificando df2, para evitar eso:
df2 = df.copy()
func1(df2)
func2(df)