Antecedentes
Acabo de actualizar mis Pandas de 0.11 a 0.13.0rc1. Ahora, la aplicación está apareciendo muchas advertencias nuevas. Uno de ellos así:
E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
¿Quiero saber qué significa exactamente? ¿Necesito cambiar algo?
¿Cómo debo suspender la advertencia si insisto en usarla quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE?
La función que da errores
def _decode_stock_quote(list_of_150_stk_str):
"""decode the webpage and return dataframe"""
from cStringIO import StringIO
str_of_all = "".join(list_of_150_stk_str)
quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
quote_df['TClose'] = quote_df['TPrice']
quote_df['RT'] = 100 * (quote_df['TPrice']/quote_df['TPCLOSE'] - 1)
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
quote_df['TAmt'] = quote_df['TAmt']/TAMT_SCALE
quote_df['STK_ID'] = quote_df['STK'].str.slice(13,19)
quote_df['STK_Name'] = quote_df['STK'].str.slice(21,30)#.decode('gb2312')
quote_df['TDate'] = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])
return quote_df
Más mensajes de error
E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
E:\FinReporter\FM_EXT.py:450: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TAmt'] = quote_df['TAmt']/TAMT_SCALE
E:\FinReporter\FM_EXT.py:453: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
quote_df['TDate'] = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])
python
pandas
dataframe
chained-assignment
bicho grande
fuente
fuente

df.set_valuedocumentos aquí - pandas.pydata.org/pandas-docs/stable/generated/…df.set_valueha quedado en desuso. Pandas ahora recomienda usar.at[]o en su.iat[]lugar. documentos aquí pandas.pydata.org/pandas-docs/stable/generated/…option_contextaquí: pandas.pydata.org/pandas-docs/stable/user_guide/options.html , use comowith pd.option_context("mode.chained_assignment", None): [...]Respuestas:
Se
SettingWithCopyWarningcreó para marcar asignaciones "encadenadas" potencialmente confusas, como las siguientes, que no siempre funcionan como se esperaba, particularmente cuando la primera selección devuelve una copia . [Ver GH5390 y GH5597 para una discusión de antecedentes.]La advertencia ofrece una sugerencia para reescribir de la siguiente manera:
Sin embargo, esto no se ajusta a su uso, lo que equivale a:
Si bien está claro que no le importa que las escrituras vuelvan al marco original (ya que está sobrescribiendo la referencia), desafortunadamente este patrón no puede diferenciarse del primer ejemplo de asignación encadenada. De ahí la advertencia (falso positivo). El potencial de falsos positivos se aborda en los documentos sobre indexación , si desea leer más. Puede deshabilitar esta nueva advertencia de forma segura con la siguiente asignación.
fuente
.ix, mejorados.iloc, etc.) definitivamente pueden ser vistos como "la forma principal" sin advertir a todos incesantemente sobre otras formas. En cambio, déjenlos ser adultos y si quieren hacer una tarea encadenada, que así sea. Mis dos centavos de todos modos. Uno ve comentarios descontentos de los desarrolladores de Pandas aquí a menudo cuando las asignaciones encadenadas funcionarán para resolver un problema, pero no se considerarían la forma "principal" de hacerlo.pd.options.mode.chained_assignment = Noneha resultado en que mi código se ejecute aproximadamente 6 veces más rápido. ¿Alguien más experimentó resultados similares?Esta publicación está destinada a lectores que,
Preparar
¿Cuál es el
SettingWithCopyWarning?Para saber cómo lidiar con esta advertencia, es importante comprender lo que significa y por qué se plantea en primer lugar.
Al filtrar DataFrames, es posible dividir / indexar un marco para devolver una vista o una copia , dependiendo del diseño interno y varios detalles de implementación. Una "vista" es, como sugiere el término, una vista de los datos originales, por lo que modificar la vista puede modificar el objeto original. Por otro lado, una "copia" es una replicación de datos del original, y la modificación de la copia no tiene ningún efecto sobre el original.
Como se menciona en otras respuestas,
SettingWithCopyWarningse creó para marcar las operaciones de "asignación encadenada". Consideredfen la configuración anterior. Suponga que desea seleccionar todos los valores en la columna "B" donde los valores en la columna "A" son> 5. Pandas le permite hacer esto de diferentes maneras, algunas más correctas que otras. Por ejemplo,Y,
Estos devuelven el mismo resultado, por lo que si solo está leyendo estos valores, no hay diferencia. Entonces, ¿cuál es el problema? El problema con la asignación encadenada es que, en general, es difícil predecir si se devuelve una vista o una copia, por lo que esto se convierte en un problema en gran medida cuando intenta asignar valores de nuevo. Para construir sobre el ejemplo anterior, considere cómo el intérprete ejecuta este código:
Con una sola
__setitem__llamada adf. OTOH, considere este código:Ahora, dependiendo de si
__getitem__devolvió una vista o una copia, la__setitem__operación puede no funcionar .En general, debe usarlo
locpara la asignación basada en etiquetas yilocpara la asignación basada en enteros / posiciones, ya que la especificación garantiza que siempre operan en el original. Además, para configurar una sola celda, debe usaratyiat.Más se puede encontrar en la documentación .
¡Solo dime cómo suprimir la advertencia!
Considere una operación simple en la columna "A" de
df. Seleccionar "A" y dividir entre 2 generará la advertencia, pero la operación funcionará.Hay un par de formas de silenciar directamente esta advertencia:
Hacer una
deepcopyCambio
pd.options.mode.chained_assignmentPuede ajustarse a
None,"warn"o"raise"."warn"es el predeterminadoNonesuprimirá la advertencia por completo y"raise"arrojará unSettingWithCopyError, evitando que la operación se realice.@Peter Cotton en los comentarios, ideó una forma agradable de cambiar el modo de manera no intrusiva (modificado desde esta esencia ) usando un administrador de contexto, para configurar el modo solo el tiempo que sea necesario y restablecerlo de nuevo a estado original cuando termine.
El uso es el siguiente:
O, para plantear la excepción
El "problema XY": ¿Qué estoy haciendo mal?
La mayoría de las veces, los usuarios intentan buscar formas de suprimir esta excepción sin comprender completamente por qué se planteó en primer lugar. Este es un buen ejemplo de un problema XY , donde los usuarios intentan resolver un problema "Y" que en realidad es un síntoma de un problema más profundo "X". Se formularán preguntas basadas en problemas comunes que se encuentran con esta advertencia, y luego se presentarán soluciones.
Manera incorrecta de hacer esto:
Manera correcta usando
loc:Puede usar cualquiera de los siguientes métodos para hacer esto.
En realidad, esto probablemente se deba a un código más arriba en su tubería. ¿Creaste a
df2partir de algo más grande, como? En este caso, la indexación booleana devolverá una vista, por lo que
df2hará referencia al original. Lo que debe hacer es asignardf2una copia :Esto se
df2debe a que debe haberse creado como una vista desde alguna otra operación de corte, comoLa solución aquí es hacer un uso
copy()dedf, o usarloc, como antes.fuente
En general, el objetivo
SettingWithCopyWarninges mostrar a los usuarios (y especialmente a los nuevos usuarios) que pueden estar operando en una copia y no en el original como piensan. No son falsos positivos (OIA si usted sabe lo que está haciendo podría ser aceptable ). Una posibilidad es simplemente apagar la (por defecto advertir advertencia) como sugieren @Garrett.Aquí hay otra opción:
Puede establecer el
is_copyindicador enFalse, que desactivará efectivamente la comprobación, para ese objeto :Si copia explícitamente, no se producirán más advertencias:
El código que el OP muestra arriba, aunque es legítimo, y probablemente algo que yo también hago, es técnicamente un caso para esta advertencia, y no un falso positivo. Otra forma de no tener la advertencia sería realizar la operación de selección a través de
reindex, p. Ej.O,
fuente
0.16, veo muchos más falsos positivos, el problema con los falsos positivos es que uno aprende a ignorarlo, aunque a veces es legítimo.undefinedcomportamiento. En todo caso, debería arrojar un error (para evitar errores vistosC), ya queapiestá congelado, el comportamiento actual de advertencia tiene sentido para la compatibilidad con versiones anteriores. Y los haré lanzar para atraparlos como errores en mi código de producción (warnings.filterwarnings('error', r'SettingWithCopyWarning). Además, la sugerencia de uso a.locveces tampoco ayuda (si está en un grupo).Advertencia de copia de marco de datos de pandas
Cuando vas y haces algo como esto:
pandas.ixen este caso devuelve un nuevo marco de datos independiente.Cualquier valor que decida cambiar en este marco de datos no cambiará el marco de datos original.
Esto es de lo que los pandas intentan advertirte.
Por qué
.ixes una mala idea?El
.ixobjeto intenta hacer más de una cosa, y para cualquiera que haya leído algo sobre código limpio, este es un olor fuerte.Dado este marco de datos:
Dos comportamientos:
Comportamiento uno:
dfcopyahora es un marco de datos independiente. Cambiarlo no cambiarádfComportamiento dos: esto cambia el marco de datos original.
Usar en su
.loclugarLos desarrolladores de pandas reconocieron que el
.ixobjeto era bastante maloliente [especulativamente] y, por lo tanto, crearon dos nuevos objetos que ayudan a acceder y asignar datos. (El otro ser.iloc).loces más rápido porque no intenta crear una copia de los datos..locestá destinado a modificar su marco de datos existente en el lugar, que es más eficiente en memoria..locEs predecible, tiene un comportamiento.La solución
Lo que está haciendo en su ejemplo de código es cargar un archivo grande con muchas columnas y luego modificarlo para que sea más pequeño.
La
pd.read_csvfunción puede ayudarlo con mucho de esto y también hace que la carga del archivo sea mucho más rápida.Entonces, en lugar de hacer esto
Hacer esto
Esto solo leerá las columnas que le interesen y las nombrará correctamente. No es necesario usar el
.ixobjeto maligno para hacer cosas mágicas.fuente
.iloc. Estos son los dos métodos principales para indexar estructuras de datos de pandas. Lea más en la documentación.Aquí respondo la pregunta directamente. ¿Como lidiar con?
Haga un
.copy(deep=False)después de cortar. Ver pandas.DataFrame.copy .Espera, ¿una rebanada no devuelve una copia? Después de todo, ¿esto es lo que el mensaje de advertencia intenta decir? Lee la respuesta larga:
Esto da una advertencia:
Esto no lo hace:
Ambos
df0ydf1sonDataFrameobjetos, pero algo en ellos es diferente que permite a los pandas imprimir la advertencia. Averigüemos qué es.Usando su herramienta de elección, verá que más allá de un par de direcciones, la única diferencia material es esta:
El método que decide si advertir es
DataFrame._check_setitem_copyqué controles_is_copy. Así que aquí tienes. Haga uncopypara que su DataFrame no sea_is_copy.La advertencia sugiere su uso
.loc, pero si la usa.locen un marco_is_copy, seguirá recibiendo la misma advertencia. ¿Engañoso? Si. ¿Molesto? Usted apuesta. ¿Servicial? Potencialmente, cuando se utiliza la asignación encadenada. Pero no puede detectar correctamente la asignación de la cadena e imprime la advertencia indiscriminadamente.fuente
Este tema es realmente confuso con los pandas. Afortunadamente, tiene una solución relativamente simple.
El problema es que no siempre está claro si las operaciones de filtrado de datos (por ejemplo, loc) devuelven una copia o una vista del DataFrame. El uso adicional de dicho DataFrame filtrado podría ser confuso.
La solución simple es (a menos que necesite trabajar con conjuntos de datos muy grandes):
Siempre que necesite actualizar cualquier valor, asegúrese siempre de copiar implícitamente el DataFrame antes de la asignación.
fuente
Para eliminar cualquier duda, mi solución fue hacer una copia profunda del segmento en lugar de una copia normal. Esto puede no ser aplicable dependiendo de su contexto (restricciones de memoria / tamaño del segmento, potencial de degradación del rendimiento, especialmente si la copia se produce en un bucle como lo hizo para mí, etc.)
Para ser claros, aquí está la advertencia que recibí:
Ilustración
Tenía dudas de que la advertencia fuera lanzada debido a una columna que estaba cayendo en una copia de la rebanada. Aunque técnicamente no estaba tratando de establecer un valor en la copia del segmento, todavía era una modificación de la copia del segmento. A continuación se detallan los pasos (simplificados) que he tomado para confirmar la sospecha, espero que ayude a aquellos de nosotros que estamos tratando de entender la advertencia.
Ejemplo 1: colocar una columna en el original afecta la copia
Ya lo sabíamos, pero este es un recordatorio saludable. De esto NO se trata la advertencia.
Es posible evitar que los cambios realizados en df1 afecten a df2
Ejemplo 2: colocar una columna en la copia puede afectar el original
Esto realmente ilustra la advertencia.
Es posible evitar que los cambios realizados en df2 afecten a df1
¡Salud!
fuente
Esto debería funcionar:
fuente
Algunos pueden querer simplemente suprimir la advertencia:
fuente
Si ha asignado el segmento a una variable y desea establecerlo usando la variable como se muestra a continuación:
Y no desea utilizar la solución de Jeff porque su computación de condición
df2es demasiado larga o por alguna otra razón, entonces puede usar lo siguiente:df2.index.tolist()devuelve los índices de todas las entradas en df2, que luego se utilizarán para establecer la columna B en el marco de datos original.fuente
Para mí, este problema ocurrió en un siguiente> ejemplo <simplificado. Y también pude resolverlo (con suerte con una solución correcta):
código antiguo con advertencia:
Esto imprimió la advertencia para la línea
old_row[field] = new_row[field]Dado que las filas en el método update_row son realmente de tipo
Series, reemplacé la línea con:es decir, método para acceder / búsquedas para a
Series. Si bien ambos funcionan bien y el resultado es el mismo, de esta manera no tengo que desactivar las advertencias (= guardarlas para otros problemas de indexación de la cadena en otro lugar).Espero que esto pueda ayudar a alguien.
fuente
Podría evitar todo el problema como este, creo:
Usando Asignar. De la documentación : Asigne nuevas columnas a un DataFrame, devolviendo un nuevo objeto (una copia) con todas las columnas originales además de las nuevas.
Consulte el artículo de Tom Augspurger sobre el método de encadenamiento en pandas: https://tomaugspurger.github.io/method-chaining
fuente
Seguimiento principiante pregunta / comentario
Tal vez una aclaración para otros principiantes como yo (vengo de R, que parece funcionar de manera un poco diferente bajo el capó). El siguiente código funcional y de aspecto inofensivo siguió produciendo la advertencia SettingWithCopy, y no pude entender por qué. Había leído y entendido el emitido con "indexación encadenada", pero mi código no contiene ninguno:
Pero luego, más tarde, demasiado tarde, miré dónde se llama la función plot ():
Entonces, "df" no es un marco de datos, sino un objeto que de alguna manera recuerda que fue creado al indexar un marco de datos (entonces, ¿es eso una vista?) Que haría la línea en plot ()
equivalente a
que es una indexación encadenada ¿Lo entendí bien?
De todas formas,
arreglado.
fuente
Como esta pregunta ya está completamente explicada y discutida en las respuestas existentes, solo proporcionaré un
pandasenfoque ordenado para el administrador de contexto usandopandas.option_context(enlaces a documentos y ejemplos ): no hay absolutamente ninguna necesidad de crear una clase personalizada con todos los métodos dunder y otras campanas y silbidos.Primero el código del administrador de contexto en sí:
Entonces un ejemplo:
Vale la pena notar que ambos enfoques no se modifican
a, lo cual es un poco sorprendente para mí, e incluso una copia de df superficial.copy(deep=False)evitaría que se genere esta advertencia (por lo que entiendo, la copia superficial al menos también debería modificarsea, pero no lo hace). 't.pandasmagia.).fuente
Había tenido este problema
.apply()al asignar un nuevo marco de datos desde un marco de datos preexistente en el que he usado el.query()método. Por ejemplo:Volvería este error. La solución que parece resolver el error en este caso es cambiar esto a:
Sin embargo, esto NO es eficiente, especialmente cuando se utilizan grandes marcos de datos, debido a que tiene que hacer una nueva copia.
Si está utilizando el
.apply()método para generar una nueva columna y sus valores, una solución que resuelve el error y es más eficiente es agregar.reset_index(drop=True):fuente