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_value
documentos aquí - pandas.pydata.org/pandas-docs/stable/generated/…df.set_value
ha quedado en desuso. Pandas ahora recomienda usar.at[]
o en su.iat[]
lugar. documentos aquí pandas.pydata.org/pandas-docs/stable/generated/…option_context
aquí: pandas.pydata.org/pandas-docs/stable/user_guide/options.html , use comowith pd.option_context("mode.chained_assignment", None): [...]
Respuestas:
Se
SettingWithCopyWarning
creó 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 = None
ha 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,
SettingWithCopyWarning
se creó para marcar las operaciones de "asignación encadenada". Consideredf
en 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
loc
para la asignación basada en etiquetas yiloc
para 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 usarat
yiat
.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
deepcopy
Cambio
pd.options.mode.chained_assignment
Puede ajustarse a
None
,"warn"
o"raise"
."warn"
es el predeterminadoNone
suprimirá 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
df2
partir de algo más grande, como? En este caso, la indexación booleana devolverá una vista, por lo que
df2
hará referencia al original. Lo que debe hacer es asignardf2
una copia :Esto se
df2
debe 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
SettingWithCopyWarning
es 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_copy
indicador 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.undefined
comportamiento. En todo caso, debería arrojar un error (para evitar errores vistosC
), ya queapi
está 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.loc
veces tampoco ayuda (si está en un grupo).Advertencia de copia de marco de datos de pandas
Cuando vas y haces algo como esto:
pandas.ix
en 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é
.ix
es una mala idea?El
.ix
objeto 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:
dfcopy
ahora es un marco de datos independiente. Cambiarlo no cambiarádf
Comportamiento dos: esto cambia el marco de datos original.
Usar en su
.loc
lugarLos desarrolladores de pandas reconocieron que el
.ix
objeto era bastante maloliente [especulativamente] y, por lo tanto, crearon dos nuevos objetos que ayudan a acceder y asignar datos. (El otro ser.iloc
).loc
es más rápido porque no intenta crear una copia de los datos..loc
está destinado a modificar su marco de datos existente en el lugar, que es más eficiente en memoria..loc
Es 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_csv
funció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
.ix
objeto 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
df0
ydf1
sonDataFrame
objetos, 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_copy
qué controles_is_copy
. Así que aquí tienes. Haga uncopy
para que su DataFrame no sea_is_copy
.La advertencia sugiere su uso
.loc
, pero si la usa.loc
en 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
df2
es 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
pandas
enfoque 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.pandas
magia.).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