Fondo

Acabo de actualizar mi Pandas de 0.11 a 0.13.0rc1. Ahora, la aplicación muestra 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 que significa exactamente ¿Necesito cambiar algo?

¿Cómo debo suspender la advertencia si insisto en usar 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])
respuesta

se creó para marcar asignaciones "encadenadas" potencialmente confusas, como las SettingWithCopyWarningsiguientes, que no siempre funcionan como se esperaba, especialmente cuando la primera selección devuelve una copia . [ver GH5390 y GH5597 para una discusión de fondo.]

df[df['A'] > 2]['B'] = new_val  # new_val not set in df

La advertencia ofrece una sugerencia para reescribir de la siguiente manera:

df.loc[df['A'] > 2, 'B'] = new_val

Sin embargo, esto no se ajusta a su uso, que es equivalente a:

df = df[df['A'] > 2]
df['B'] = new_val

Si bien está claro que no le importa que las escrituras regresen al marco original (ya que está sobrescribiendo la referencia), desafortunadamente, este patrón no se puede diferenciar 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 desactivar de forma segura esta nueva advertencia con la siguiente tarea.

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

Otros recursos

How to deal with SettingWithCopyWarning in Pandas?

Esta publicación está destinada a lectores que,

  1. Me gustaría entender qué significa esta advertencia.
  2. Me gustaría entender diferentes formas de suprimir esta advertencia.
  3. Me gustaría saber cómo mejorar su código y seguir buenas prácticas para evitar esta advertencia en el futuro.

Configuración

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
   A  B  C  D  E
0  5  0  3  3  7
1  9  3  5  2  4
2  7  6  8  8  1

¿Cuál es el SettingWithCopyWarning?

Para saber cómo lidiar con esta advertencia, es importante entender qué 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 , según el 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 réplica de los datos del original y la modificación de la copia no tiene ningún efecto sobre el original.

Como se mencionó en otras respuestas, SettingWithCopyWarningse creó para marcar las operaciones de "asignación encadenada". Considere dfla 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,

df[df.A > 5]['B']
 
1    3
2    6
Name: B, dtype: int64

Y,

df.loc[df.A > 5, 'B']

1    3
2    6
Name: B, dtype: int64

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, por lo general, es difícil predecir si se devuelve una vista o una copia, por lo que esto se convierte en gran medida en un problema cuando se intenta volver a asignar valores. Para construir sobre el ejemplo anterior, considere cómo el intérprete ejecuta este código:

df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)

Con una sola __setitem__llamada a df. OTOH, considere este código:

df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B', 4)

Ahora bien, según se __getitem__devuelva una vista o una copia, es posible que la __setitem__operación no funcione .

En general, debe utilizar locpara la asignación basada en etiquetas y ilocpara la asignación basada en enteros/posicionales, ya que la especificación garantiza que siempre funcionan en el original. Además, para configurar una sola celda, debe usar aty iat.

Se puede encontrar más en la documentación .

Note
All boolean indexing operations done with loc can also be done with iloc. The only difference is that iloc expects either integers/positions for index or a numpy array of boolean values, and integer/position indexes for the columns.

For example,

df.loc[df.A > 5, 'B'] = 4

Can be written nas

df.iloc[(df.A > 5).values, 1] = 4

And,

df.loc[1, 'A'] = 100

Can be written as

df.iloc[1, 0] = 100

And so on.


¡Solo dime cómo suprimir la advertencia!

Considere una operación simple en la columna "A" de df. Seleccionar "A" y dividir por 2 generará la advertencia, pero la operación funcionará.

df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

df2
     A
0  2.5
1  4.5
2  3.5

Hay un par de formas de silenciar directamente esta advertencia:

  1. (recomendado) Use locpara dividir subconjuntos :

     df2 = df.loc[:, ['A']]
     df2['A'] /= 2     # Does not raise 
    
  2. Cambiarpd.options.mode.chained_assignment
    Puede establecerse en None, "warn"o "raise". "warn"es el predeterminado. Nonesuprimirá la advertencia por completo y "raise"lanzará un SettingWithCopyError, evitando que la operación se lleve a cabo.

     pd.options.mode.chained_assignment = None
     df2['A'] /= 2
    
  3. Hacer unadeepcopy

     df2 = df[['A']].copy(deep=True)
     df2['A'] /= 2
    

@Peter Cotton en los comentarios, se le ocurrió una buena manera de cambiar el modo de manera no intrusiva (modificado a partir de esta esencia ) usando un administrador de contexto, para configurar el modo solo mientras sea necesario, y luego restablecerlo a la Estado original al terminar.

class ChainedAssignent:
    def __init__(self, chained=None):
        acceptable = [None, 'warn', 'raise']
        assert chained in acceptable, "chained must be in " + str(acceptable)
        self.swcw = chained

    def __enter__(self):
        self.saved_swcw = pd.options.mode.chained_assignment
        pd.options.mode.chained_assignment = self.swcw
        return self

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = self.saved_swcw

El uso es el siguiente:

# some code here
with ChainedAssignent():
    df2['A'] /= 2
# more code follows

O, para plantear la excepción

with ChainedAssignent(chained='raise'):
    df2['A'] /= 2

SettingWithCopyError: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

El "Problema XY": ¿Qué estoy haciendo mal?

Muchas veces, los usuarios intentan buscar formas de suprimir esta excepción sin comprender completamente por qué se generó 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 "X" con raíces más profundas. Se plantearán preguntas basadas en problemas comunes que se encuentran con esta advertencia, y luego se presentarán soluciones.

Question 1
I have a DataFrame

df
       A  B  C  D  E
    0  5  0  3  3  7
    1  9  3  5  2  4
    2  7  6  8  8  1

I want to assign values in col "A" > 5 to 1000. My expected output is

      A  B  C  D  E
0     5  0  3  3  7
1  1000  3  5  2  4
2  1000  6  8  8  1

Manera incorrecta de hacer esto:

df.A[df.A > 5] = 1000         # works, because df.A returns a view
df[df.A > 5]['A'] = 1000      # does not work
df.loc[df.A > 5]['A'] = 1000   # does not work

Manera correcta usando loc:

df.loc[df.A > 5, 'A'] = 1000

Question 21
I am trying to set the value in cell (1, 'D') to 12345. My expected output is

   A  B  C      D  E
0  5  0  3      3  7
1  9  3  5  12345  4
2  7  6  8      8  1

I have tried different ways of accessing this cell, such as df['D'][1]. What is the best way to do this?

1. This question isn't specifically related to the warning, but it is good to understand how to do this particular operation correctly so as to avoid situations where the warning could potentially arise in future.

Puede usar cualquiera de los siguientes métodos para hacer esto.

df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345

Question 3
I am trying to subset values based on some condition. I have a DataFrame

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

I would like to assign values in "D" to 123 such that "C" == 5. I tried

df2.loc[df2.C == 5, 'D'] = 123

Which seems fine but I am still getting the SettingWithCopyWarning! How do I fix this?

En realidad, esto probablemente se deba a un código que se encuentra más arriba en su canalización. ¿Creaste a df2partir de algo más grande, como

df2 = df[df.A > 5]

? En este caso, la indexación booleana devolverá una vista, por lo que df2hará referencia al original. Lo que tendría que hacer es asignar df2a una copia :

df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]

Question 4
I'm trying to drop column "C" in-place from

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

But using

df2.drop('C', axis=1, inplace=True)

Throws SettingWithCopyWarning. Why is this happening?

Esto se debe a df2que debe haberse creado como una vista de alguna otra operación de corte, como

df2 = df[df.A > 5]

La solución aquí es hacer o usar copy(), como antes.dfloc

En general, el objetivo del SettingWithCopyWarninges mostrar a los usuarios (y especialmente a los nuevos) que pueden estar operando en una copia y no en el original como creen. Hay falsos positivos ( IOW , si sabes lo que estás haciendo, podría estar bien ). Una posibilidad es simplemente desactivar la advertencia (por defecto advertir ) como sugiere @Garrett.

Aquí hay otra opción:

In [1]: df = DataFrame(np.random.randn(5, 2), columns=list('AB'))

In [2]: dfa = df.ix[:, [1, 0]]

In [3]: dfa.is_copy
Out[3]: True

In [4]: dfa['A'] /= 2
/usr/local/bin/ipython:1: 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
  #!/usr/local/bin/python

Puede establecer el is_copyindicador en False, lo que desactivará efectivamente la verificación para ese objeto :

In [5]: dfa.is_copy = False

In [6]: dfa['A'] /= 2

Si copia explícitamente, no habrá más advertencias:

In [7]: dfa = df.ix[:, [1, 0]].copy()

In [8]: dfa['A'] /= 2

El código que muestra el OP 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 hacer la operación de selección a través reindexde, por ejemplo

quote_df = quote_df.reindex(columns=['STK', ...])

O,

quote_df = quote_df.reindex(['STK', ...], axis=1)  # v.0.21

Advertencia de copia de marco de datos de Pandas

Cuando vas y haces algo como esto:

quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

pandas.ix en este caso , devuelve un marco de datos nuevo e independiente.

Cualquier valor que decida cambiar en este marco de datos, no cambiará el marco de datos original.

Esto es 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:

df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})

Dos comportamientos:

dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2

Comportamiento uno: dfcopyahora es un marco de datos independiente. Cambiarlo no cambiarádf

df.ix[0, "a"] = 3

Comportamiento dos: Esto cambia el marco de datos original.


Usar .locen su lugar

Los desarrolladores de pandas reconocieron que el .ixobjeto olía bastante [especulativamente] y, por lo tanto, crearon dos nuevos objetos que ayudan en el acceso y la asignación de 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 su lugar, lo que es más eficiente en la 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 hacer que la carga del archivo sea mucho más rápida.

Así que en lugar de hacer esto

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]]

Hacer esto

columns = ['STK', 'TPrice', 'TPCLOSE', 'TOpen', 'THigh', 'TLow', 'TVol', 'TAmt', 'TDate', 'TTime']
df = pd.read_csv(StringIO(str_of_all), sep=',', usecols=[0,3,2,1,4,5,8,9,30,31])
df.columns = columns

Esto solo leerá las columnas que le interesan y las nombrará correctamente. No hay necesidad de usar el objeto malvado .ixpara hacer cosas mágicas.

Aquí respondo la pregunta directamente. ¿Cómo lidiar con ello?

Haz un .copy(deep=False)después de cortar. Consulte pandas.DataFrame.copy .

Espera, ¿un segmento no devuelve una copia? Después de todo, ¿esto es lo que intenta decir el mensaje de advertencia? Lea la respuesta larga:

import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})

Esto da una advertencia:

df0 = df[df.x>2]
df0['foo'] = 'bar'

Esto no lo hace:

df1 = df[df.x>2].copy(deep=False)
df1['foo'] = 'bar'

Ambos df0y df1son DataFrameobjetos, pero algo en ellos es diferente que permite que los pandas impriman la advertencia. Averigüemos qué es.

import inspect
slice= df[df.x>2]
slice_copy = df[df.x>2].copy(deep=False)
inspect.getmembers(slice)
inspect.getmembers(slice_copy)

Usando la herramienta de comparación de su elección, verá que más allá de un par de direcciones, la única diferencia material es esta:

|          | slice   | slice_copy |
| _is_copy | weakref | None       |

El método que decide si avisar es DataFrame._check_setitem_copyel que comprueba _is_copy. Así que aquí tienes. Haga una copypara que su DataFrame no sea _is_copy.

La advertencia sugiere usar .loc, pero si usa .locen un marco que _is_copy, seguirá recibiendo la misma advertencia. ¿Engañoso? Sí. ¿Irritante? tu 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 de forma indiscriminada.

Este tema es realmente confuso con Pandas. Por suerte, tiene una solución relativamente sencilla.

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. Por lo tanto, el uso posterior 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 algún valor, asegúrese siempre de copiar explícitamente el DataFrame antes de la asignación.

df  # Some DataFrame
df = df.loc[:, 0:2]  # Some filtering (unsure whether a view or copy is returned)
df = df.copy()  # Ensuring a copy is made
df[df["Name"] == "John"] = "Johny"  # Assignment can be done now (no warning)

Tuve este problema .apply()al asignar un nuevo marco de datos de un marco de datos preexistente en el que usé el .query()método. Por ejemplo:

prop_df = df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

Devolvería este error. La solución que parece resolver el error en este caso es cambiar esto a:

prop_df = df.copy(deep=True)
prop_df = prop_df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

Sin embargo, esto NO es eficiente, especialmente cuando se usan tramas de datos grandes, debido a que se debe 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):

prop_df = df.query('column == "value"').reset_index(drop=True)
prop_df['new_column'] = prop_df.apply(function, axis=1)

Para despejar cualquier duda, mi solución fue hacer una copia profunda del segmento en lugar de una copia normal. Es posible que esto no sea aplicable según 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 me sucedió a mí, etc.)

Para ser claros, aquí está la advertencia que recibí:

/opt/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:54:
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

Ilustración

Tenía dudas de que la advertencia se emitiera debido a una columna que estaba colocando en una copia del segmento. Aunque técnicamente no intentaba establecer un valor en la copia de la porción, seguía siendo una modificación de la copia de la porción. A continuación se muestran 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. Esto NO es de lo que se trata la advertencia.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123


>> df2 = df1
>> df2

A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 affects df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    B
0   121
1   122
2   123

Es posible evitar que los cambios realizados en df1 afecten a df2. Nota: puede evitar la importación copy.deepcopyhaciendo df.copy()en su lugar.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2
A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 does not affect df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    A   B
0   111 121
1   112 122
2   113 123

Ejemplo 2: colocar una columna en la copia puede afectar el original

Esto realmente ilustra la advertencia.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> df2 = df1
>> df2

    A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df2 can affect df1
# No slice involved here, but I believe the principle remains the same?
# Let me know if not
>> df2.drop('A', axis=1, inplace=True)
>> df1

B
0   121
1   122
2   123

Es posible evitar que los cambios realizados en df2 afecten a df1

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2

A   B
0   111 121
1   112 122
2   113 123

>> df2.drop('A', axis=1, inplace=True)
>> df1

A   B
0   111 121
1   112 122
2   113 123

¡Salud!

Esto debería funcionar:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE

Algunos pueden querer simplemente suprimir la advertencia:

class SupressSettingWithCopyWarning:
    def __enter__(self):
        pd.options.mode.chained_assignment = None

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = 'warn'

with SupressSettingWithCopyWarning():
    #code that produces warning

Para mí funcionó:

import pandas as pd
# ...
pd.set_option('mode.chained_assignment', None)

Seguimiento de pregunta/comentario de principiante

Tal vez una aclaración para otros principiantes como yo (vengo de R, que parece funcionar de manera un poco diferente debajo del capó). El siguiente código funcional y de apariencia inofensiva seguía produciendo la advertencia SettingWithCopy, y no podía entender por qué. Había leído y entendido el problema con la "indexación encadenada", pero mi código no contiene ninguno:

def plot(pdb, df, title, **kw):
    df['target'] = (df['ogg'] + df['ugg']) / 2
    # ...

Pero luego, más tarde, demasiado tarde, miré dónde se llama a la función plot():

    df = data[data['anz_emw'] > 0]
    pixbuf = plot(pdb, df, title)

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 una vista?) Que haría la línea en plot()

 df['target'] = ...

equivalente a

 data[data['anz_emw'] > 0]['target'] = ...

que es una indexación encadenada. ¿Entendí bien?

De todas formas,

def plot(pdb, df, title, **kw):
    df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2

arreglado.

Como esta pregunta ya está completamente explicada y discutida en las respuestas existentes, solo proporcionaré un pandasenfoque ordenado para el uso del administrador de contexto pandas.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 silbatos.

Primero el propio código del administrador de contexto:

from contextlib import contextmanager

@contextmanager
def SuppressPandasWarning():
    with pd.option_context("mode.chained_assignment", None):
        yield

Luego un ejemplo:

import pandas as pd
from string import ascii_letters

a = pd.DataFrame({"A": list(ascii_letters[0:4]), "B": range(0,4)})

mask = a["A"].isin(["c", "d"])
# Even shallow copy below is enough to not raise the warning, but why is a mystery to me.
b = a.loc[mask]  # .copy(deep=False)

# Raises the `SettingWithCopyWarning`
b["B"] = b["B"] * 2

# Does not!
with SuppressPandasWarning():
    b["B"] = b["B"] * 2

Vale la pena notar que ambos enfoques no se modifican a, lo cual es un poco sorprendente para mí, e incluso una copia superficial de df .copy(deep=False)evitaría que se genere esta advertencia (por lo que entiendo, la copia superficial al menos debería modificarse atambién, pero no 't. pandasmagia.).

Si ha asignado el segmento a una variable y desea establecer el uso de la variable como se muestra a continuación:

df2 = df[df['A'] > 2]
df2['B'] = value

Y no quiere usar la solución de Jeff porque su condición df2de computación es demasiado larga o por alguna otra razón, entonces puede usar lo siguiente:

df.loc[df2.index.tolist(), 'B'] = value

df2.index.tolist()devuelve los índices de todas las entradas en df2, que luego se usarán para establecer la columna B en el marco de datos original.

Podrías evitar todo el problema así, creo:

return (
    pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    .rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    .ix[:,[0,3,2,1,4,5,8,9,30,31]]
    .assign(
        TClose=lambda df: df['TPrice'],
        RT=lambda df: 100 * (df['TPrice']/quote_df['TPCLOSE'] - 1),
        TVol=lambda df: df['TVol']/TVOL_SCALE,
        TAmt=lambda df: df['TAmt']/TAMT_SCALE,
        STK_ID=lambda df: df['STK'].str.slice(13,19),
        STK_Name=lambda df: df['STK'].str.slice(21,30)#.decode('gb2312'),
        TDate=lambda df: df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10]),
    )
)

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 encadenamiento de métodos en pandas: https://tomaugspurger.github.io/method-chaining

Para mí, este problema ocurrió en un siguiente ejemplo >simplificado<. Y también pude resolverlo (esperemos que con una solución correcta):

código antiguo con advertencia:

def update_old_dataframe(old_dataframe, new_dataframe):
    for new_index, new_row in new_dataframe.iterrorws():
        old_dataframe.loc[new_index] = update_row(old_dataframe.loc[new_index], new_row)

def update_row(old_row, new_row):
    for field in [list_of_columns]:
        # line with warning because of chain indexing old_dataframe[new_index][field]
        old_row[field] = new_row[field]  
    return old_row

Esto imprimió la advertencia para la línea.old_row[field] = new_row[field]

Dado que las filas en el método update_row en realidad son de tipo Series, reemplacé la línea con:

old_row.at[field] = new_row.at[field]

es decir , método para acceder/buscar un archivo Series. Aunque ambos funcionan bien y el resultado es el mismo, de esta manera no tengo que deshabilitar las advertencias (= guardarlas para otros problemas de indexación de cadenas en otro lugar).

Espero que esto pueda ayudar a alguien.

Me enfrentaba a la misma advertencia, mientras ejecutaba esta parte de mi código:

    def scaler(self, numericals):
        scaler = MinMaxScaler()
        self.data.loc[:, numericals[0]] = scaler.fit_transform(self.data.loc[:, numericals[0]])
        self.data.loc[:, numericals[1]] = scaler.fit_transform(self.data.loc[:, numericals[1]])

que scaleres un MinMaxScaler y numericals[0] contiene nombres de 3 de mis columnas numéricas. la advertencia se eliminó cuando cambié el código a:

    def scaler(self, numericals):
        scaler = MinMaxScaler()
        self.data.loc[:][numericals[0]] = scaler.fit_transform(self.data.loc[:][numericals[0]])
        self.data.loc[:][numericals[1]] = scaler.fit_transform(self.data.loc[:][numericals[1]])

Entonces, solo cambia [:, ~]a[:][~]

esto podría aplicarse solo a numpy, lo que significa que es posible que deba importarlo, pero los datos que utilicé para mis ejemplos numpy no eran esenciales con los cálculos, pero simplemente puede detener esta configuración con el mensaje de advertencia de copia, utilizando esta 1 línea de código a continuación ,

np.warnings.filterwarnings('ignore')

Simplemente cree una copia de su(s) marco(s) de datos usando .copy()el método antes de que aparezca la advertencia, para eliminar todas sus advertencias. Esto sucede porque no queremos realizar cambios en el quote_df original. En otras palabras, no queremos jugar con la referencia del objeto del quote_df que hemos creado para quote_df.

quote_df = quote_df.copy()

En mi caso, crearía una nueva columna basada en el índice, pero recibí esta advertencia como tú:

df_temp["Quarter"] = df_temp.index.quarter

Uso insert() en lugar de asignación directa y funciona para mí:

df_temp.insert(loc=0, column='Quarter', value=df_temp.index.quarter)