usar tablas recorrer promedio para notebook nombre leer graficar funciones filtrar datos data con como columna cambiar python pandas views chained-assignment

python - tablas - recorrer data frame pandas



Pandas: saber cuándo una operación afecta el marco de datos original (3)

Me encantan los pandas y lo he estado usando durante años y me siento bastante seguro de que tengo un buen manejo de cómo subordinar los marcos de datos y manejar las vistas frente a las copias de manera adecuada (aunque con seguridad hago muchas afirmaciones). También sé que ha habido toneladas de preguntas sobre SettingWithCopyWarning, por ejemplo, ¿Cómo lidiar con SettingWithCopyWarning en Pandas? y algunas excelentes guías recientes sobre cómo envolver la cabeza cuando sucede, por ejemplo, Entendiendo la configuración de la copia de seguridad en pandas .

Pero también sé que cosas específicas como la cita de esta respuesta ya no se encuentran en los documentos más recientes ( 0.22.0 ) y que muchas cosas han quedado en desuso a lo largo de los años (lo que lleva a algunas respuestas SO inapropiadas) y que las cosas continúan. para cambiar

Recientemente, después de enseñar a los pandas a los recién llegados con conocimientos generales básicos de Python sobre cosas como evitar la indexación encadenada (y usar .iloc / .loc ), todavía me cuesta proporcionar reglas generales para saber cuándo es importante prestar atención a el SettingWithCopyWarning (por ejemplo, cuando es seguro ignorarlo).

Personalmente, descubrí que el patrón específico de subconjunto de un marco de datos de acuerdo con algunas reglas (por ejemplo, operación de corte o booleano) y luego modificar ese subconjunto, independientemente del marco de datos original , es una operación mucho más común de lo que sugieren los documentos. En esta situación, queremos modificar la copia, no el original, y la advertencia confunde / asusta a los recién llegados.

Sé que no es trivial saber de antemano cuándo se devuelve una vista frente a una copia, por ejemplo,
¿Qué reglas utiliza Pandas para generar una vista frente a una copia?
Comprobando si el marco de datos es copiar o ver en Pandas

Así que, en lugar de eso, estoy buscando la respuesta a una pregunta más general (para principiantes): ¿ cuándo una operación en un marco de datos subcontratado afecta al marco de datos original a partir del cual se creó y cuándo son independientes? .

He creado algunos casos a continuación que creo que parecen razonables, pero no estoy seguro de si falta un "gotcha" o si hay alguna forma más fácil de pensar / verificar esto. Esperaba que alguien pudiera confirmar que mis intuiciones sobre los siguientes casos de uso son correctas en lo que respecta a mi pregunta anterior.

import pandas as pd df1 = pd.DataFrame({''A'':[2,4,6,8,10],''B'':[1,3,5,7,9],''C'':[10,20,30,40,50]})

1) Advertencia: No
Original cambiado: No

# df1 will be unaffected because we use .copy() method explicitly df2 = df1.copy() # # Reference: docs df2.iloc[0,1] = 100

2) Advertencia: Sí (realmente no entendí por qué)
Original cambiado: No

# df1 will be unaffected because .query() always returns a copy # # Reference: # https://stackoverflow.com/a/23296545/8022335 df2 = df1.query(''A < 10'') df2.iloc[0,1] = 100

3) Advertencia: si
Original cambiado: No

# df1 will be unaffected because boolean indexing with .loc # always returns a copy # # Reference: # https://stackoverflow.com/a/17961468/8022335 df2 = df1.loc[df1[''A''] < 10,:] df2.iloc[0,1] = 100

4) Advertencia: No
Original cambiado: No

# df1 will be unaffected because list indexing with .loc (or .iloc) # always returns a copy # # Reference: # Same as 4) df2 = df1.loc[[0,3,4],:] df2.iloc[0,1] = 100

5) Advertencia: No
Original cambiado: Sí (confuso para los recién llegados pero tiene sentido)

# df1 will be affected because scalar/slice indexing with .iloc/.loc # always references the original dataframe, but may sometimes # provide a view and sometimes provide a copy # # Reference: docs df2 = df1.loc[:10,:] df2.iloc[0,1] = 100

tl; dr Al crear un nuevo marco de datos desde el original, cambiando el nuevo marco de datos:
Cambiará el original cuando se utilice la indexación escalar / división con .loc / .iloc para crear el nuevo marco de datos .
No cambiará el original cuando se use la indexación booleana con .loc, .query() o .copy() para crear el nuevo marco de datos


Esta es una parte algo confusa e incluso frustrante de los pandas, pero en su mayor parte no debería preocuparse por esto si sigue algunas reglas simples de flujo de trabajo. En particular, tenga en cuenta que solo hay dos casos generales aquí cuando tiene dos marcos de datos, uno de los cuales es un subconjunto del otro.

Este es un caso en el que la regla Zen de Python "explícito es mejor que implícito" es una gran guía a seguir.

Caso A: los cambios en df2 NO deben afectar a df1

Esto es trivial, por supuesto. Quieres dos marcos de datos completamente independientes, así que simplemente haces una copia explícita:

df2 = df1.copy()

Después de esto, cualquier cosa que haga en df2 afecta solo a df2 y no a df1 y viceversa.

Caso B: los cambios en df2 TAMBIÉN deberían afectar a df1

En este caso, no creo que haya una manera general de resolver el problema porque depende de lo que está intentando hacer exactamente. Sin embargo, hay un par de enfoques estándar que son bastante directos y no deberían tener ninguna ambigüedad sobre cómo están funcionando.

Método 1: copie df1 en df2, luego use df2 para actualizar df1

En este caso, básicamente puede hacer una conversión uno a uno de los ejemplos anteriores. Aquí está el ejemplo # 2:

df2 = df1.copy() df2 = df1.query(''A < 10'') df2.iloc[0,1] = 100 df1 = df2.append(df1).reset_index().drop_duplicates(subset=''index'').drop(columns=''index'')

Desafortunadamente, la re-fusión a través de append es un poco detallado allí. Puede hacerlo de forma más limpia con lo siguiente, aunque tiene el efecto secundario de convertir enteros en flotantes.

df1.update(df2) # note that this is an inplace operation

Método 2: Use una máscara (no cree df2 en absoluto)

Creo que el mejor enfoque general aquí no es crear df2 en absoluto, sino que sea una versión enmascarada de df1 . Desafortunadamente, no se puede hacer una traducción directa del código anterior debido a su mezcla de loc e iloc que está bien para este ejemplo, aunque probablemente no sea realista para el uso real.

La ventaja es que puedes escribir código muy simple y legible. Aquí hay una versión alternativa del ejemplo # 2 anterior donde df2 es en realidad una versión enmascarada de df1 . Pero en lugar de cambiar a través de iloc , cambiaré si la columna "C" == 10.

df2_mask = df1[''A''] < 10 df1.loc[ df2_mask & (df1[''C''] == 10), ''B''] = 100

Ahora, si imprime df1 o df1[df2_mask] verá que la columna "B" = 100 para la primera fila de cada marco de datos. Obviamente, esto no es muy sorprendente aquí, pero esa es la ventaja inherente de seguir "explícitamente es mejor que implícito".


Solo necesita reemplazar .iloc[0,1] con .iat[0,1] .

Más en general, si desea modificar solo un elemento, debe usar el método .iat o .at . En cambio, cuando modifica más elementos a la vez, debe usar los métodos .loc o .iloc .

Haciendo de esta manera los pandas no deberían lanzar ninguna advertencia.


Tengo la misma duda, busqué esta respuesta en el pasado sin éxito. Así que ahora, solo certifico que el original no está cambiando y uso este código de paz para el programa al principio para eliminar advertencias:

import pandas as pd pd.options.mode.chained_assignment = None # default=''warn''