Saltar al contenido principal

SettingWithCopyWarning in Pandas: Cómo solucionar esta advertencia

Aprende a arreglar la advertencia SettingWithCopyWarning de Pandas: Se intenta establecer un valor en una copia de un trozo de un Marco de datos.
Actualizado 17 nov 2024  · 8 min de lectura

SettingWithCopyWarning es una advertencia que Pandas puede lanzar cuando hacemos una asignación a un DataFrame. Esto puede ocurrir cuando utilizamos asignaciones encadenadas o cuando utilizamos un DataFrame creado a partir de una rebanada. Es una fuente común de errores en el código Pandas a la que todos nos hemos enfrentado alguna vez. Puede ser difícil de depurar porque la advertencia puede aparecer en código que parece que debería funcionar bien.

Comprender la SettingWithCopyWarning es importante porque señala posibles problemas de manipulación de datos. Esta advertencia sugiere que es posible que tu código no esté alterando los datos según lo previsto, lo que puede provocar consecuencias no deseadas y fallos oscuros difíciles de rastrear.

En este artículo, exploraremos el SettingWithCopyWarning en pandas y cómo evitarlo. También hablaremos del futuro de Pandas y de cómo la opción copy_on_write cambiará nuestra forma de trabajar con DataFrames.

Vistas y copias de marcos de datos

Cuando seleccionamos un trozo de un Marco de Datos y lo asignamos a una variable, podemos obtener una vista o una copia nueva del Marco de Datos.

Con una vista, la memoria entre ambos DataFrames es compartida. Esto significa que modificar un valor de una celda que esté presente en ambos DataFrames modificará los dos.

Ejemplo de una vista Pandas DataFrame

Con una copia, se asigna nueva memoria y se crea un DataFrame independiente con los mismos valores que el original. En este caso, ambos DataFrames son entidades distintas, por lo que modificar un valor en uno de ellos no afecta al otro.

Ejemplo de copia de un DataFrame

Pandas intenta evitar crear una copia siempre que puede para optimizar el rendimiento. Sin embargo, es imposible predecir de antemano si obtendremos una vista o una copia. El SettingWithCopyWarning aparece siempre que asignamos un valor a un DataFrame para el que no está claro si es una copia o una vista de otro DataFrame.

Comprender la SettingWithCopyWarning con datos reales

Utilizaremos este conjunto de datos Kaggle de Datos Inmobiliarios Londres 2024 para aprender cómo se produce el SettingWithCopyWarning y cómo solucionarlo. 

Este conjunto de datos contiene datos inmobiliarios recientes de Londres. Aquí tienes un resumen de las columnas presentes en el conjunto de datos:

  • addedOn: La fecha en la que se añadió el anuncio.
  • title: El título del anuncio.
  • descriptionHtml: Una descripción HTML del anuncio.
  • propertyType: El tipo de propiedad. El valor será "Not Specified" si no se especificó el tipo.
  • sizeSqFeetMax: El tamaño máximo en metros cuadrados.
  • bedrooms: El número de habitaciones.
  • listingUpdatedReason: Motivo de la actualización del anuncio (por ejemplo, nuevo anuncio, reducción de precio).
  • price: El precio del anuncio en libras.

Ejemplo con una variable temporal explícita

Digamos que nos dicen que las propiedades con un tipo de propiedad no especificado son casas. Por tanto, queremos actualizar todas las filas con propertyType igual a "Not Specified" a "House". Una forma de hacerlo es filtrar las filas con un tipo de propiedad no especificado en una variable temporal DataFrame y actualizar los valores de la columna propertyType de esta forma:

import pandas as pd
dataset_name = "realestate_data_london_2024_nov.csv"
df = pd.read_csv(dataset_name)

# Obtain all rows with unspecified property type
no_property_type = df[df["propertyType"] == "Not Specified"]

# Update the property type to “House” on those rows
no_property_type["propertyType"] = "House"

La ejecución de este código hará que pandas produzca el SettingWithCopyWarning:

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
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  no_property_type["propertyType"] = "House"

El motivo es que pandas no puede saber si el no_property_type DataFrame es una vista o una copia de df

Cuando asignamos parte de un marco de datos a una variable, podemos obtener una vista o una copia, pero no podemos saber cuál hemos obtenido

Esto es un problema porque el comportamiento del código siguiente puede ser muy diferente según se trate de una vista o de una copia. 

En este ejemplo, nuestro objetivo es modificar el DataFrame original. Esto sólo ocurrirá si no_property_type es una vista. Si el resto de nuestro código asume que df se ha modificado, puede equivocarse porque no hay forma de garantizar que sea así. Debido a este comportamiento incierto, Pandas lanza la advertencia para avisarnos de ese hecho.

Aunque nuestro código se ejecute correctamente porque hemos obtenido una vista, podríamos obtener una copia en ejecuciones posteriores, y el código no funcionaría como estaba previsto. Por tanto, es importante no ignorar esta advertencia y asegurarnos de que nuestro código siempre hará lo que queremos que haga.

Ejemplo con una variable temporal oculta

En el ejemplo anterior, está claro que se está utilizando una variable temporal porque estamos asignando explícitamente parte del DataFrame a una variable llamada no_property_type.

Sin embargo, en algunos casos, esto no es tan explícito. El ejemplo más común en el que se produce SettingWithCopyWarning es con la indexación encadenada. Supongamos que sustituimos las dos últimas líneas por una sola:

df[df["propertyType"] == "Not Specified"]["propertyType"] = "House"

A primera vista, no parece que se esté creando una variable temporal. Sin embargo, al ejecutarlo también se obtiene un SettingWithCopyWarning

La forma en que se ejecuta este código es

  1. df[df["propertyType"] == "Not Specified"] se evalúa y se almacena temporalmente en la memoria. 
  2. Se accede al índice ["propertyType"] de esa posición de memoria temporal. 

Los accesos al índice se evalúan uno a uno y, por tanto, la indexación encadenada produce la misma advertencia porque no sabemos si los resultados intermedios son vistas o copias

Los accesos al índice se evalúan uno a uno y, por tanto, la indexación encadenada da lugar a la misma advertencia, porque no sabemos si los resultados intermedios son vistas o copias. El código anterior es esencialmente lo mismo que hacer:

tmp = df[df["propertyType"] == "Not Specified"]
tmp["propertyType"] = "House"

Este ejemplo suele denominarse indexación encadenada porque encadenamos los accesos indexados utilizando []. Primero accedemos a [df["propertyType"] == "Not Specified"] y después a ["propertyType"].

Indexación encadenada

Cómo resolver el SettingWithCopyWarning

Aprendamos a escribir nuestro código para que no haya ambigüedad y no se active el SettingWithCopyWarning. Aprendimos que la advertencia surge de una ambigüedad sobre si un DataFrame es una vista o una copia de otro DataFrame.

La forma de solucionarlo es asegurarnos de que cada DataFrame que creamos es una copia si queremos que sea una copia o una vista si queremos que sea una vista.

Modificar con seguridad el DataFrame original con loc 

Arreglemos el código del ejemplo anterior en el que queremos modificar el DataFrame original. Para evitar utilizar una variable temporal, utiliza la propiedad loc indexador.

df.loc[df["propertyType"] == "Not Specified", "propertyType"] = "House"

Con este código, estamos actuando directamente sobre el DataFrame original df a través de la propiedad indexadora loc, por lo que no hay necesidad de variables intermedias. Esto es lo que tenemos que hacer cuando queremos modificar directamente el DataFrame original.

A primera vista, esto puede parecer una indexación encadenada, porque sigue habiendo parámetros, pero no lo es. Lo que define cada indexación son los corchetes [].

Loc índice doble vs indexación encadenada

Ten en cuenta que utilizar loc sólo es seguro si asignamos directamente un valor, como hicimos antes. Si en su lugar utilizamos una variable temporal, volvemos a caer en el mismo problema. Aquí tienes dos ejemplos de código que no solucionan el problema: 

  1. Utilizando loc con una variable temporal:
# Using loc plus temporary variable doesn’t fix the issue
no_property_type = df.loc[df["propertyType"] == "Not Specified"]
no_property_type["propertyType"] = "House"
  1. Utilizando loc junto con un índice (igual que la indexación encadenada):
# Using loc plus indexing is the same as chained indexing
df.loc[df["propertyType"] == "Not Specified"]["propertyType"] = "House"

Estos dos ejemplos tienden a confundir a la gente porque es un error común pensar que mientras haya un loc, estamos modificando los datos originales. Esto es incorrecto. La única forma de asegurarse de que el valor se está asignando al DataFrame original es asignarlo directamente utilizando un único loc sin ninguna indexación separada.

Trabajar con seguridad con una copia del DataFrame original con copy()

Cuando queramos asegurarnos de que estamos operando sobre una copia del DataFrame, debemos utilizar el método .copy() .

Digamos que nos piden que analicemos el precio por pie cuadrado de las propiedades. No queremos modificar los datos originales. El objetivo es crear un nuevo DataFrame con los resultados del análisis para enviarlo a otro equipo.

El primer paso es filtrar algunas filas y limpiar los datos. Concretamente, necesitamos

  • Elimina las filas en las que sizeSqFeetMax no esté definido.
  • Elimina las filas en las que el price sea "POA" (precio a consultar).
  • Convierte los precios en valores numéricos (en el conjunto de datos original, los precios son cadenas con el siguiente formato: "£25,000,000")

Podemos realizar los pasos anteriores utilizando el siguiente código:

# 1. Filter out all properties without a size or a price
properties_with_size_and_price = df[df["sizeSqFeetMax"].notna() & (df["price"] != "POA")]

# 2. Remove the £ and , characters from the price columns
properties_with_size_and_price["price"] = properties_with_size_and_price["price"].str.replace("£", "", regex=False).str.replace(",", "", regex=False)

# 3. Convert the price column to numeric values
properties_with_size_and_price["price"] = pd.to_numeric(properties_with_size_and_price["price"])

Para calcular el precio por pie cuadrado, creamos una nueva columna cuyos valores son el resultado de dividir la columna price por la columna sizeSqFeetMax:

properties_with_size_and_price["pricePerSqFt"] = properties_with_size_and_price["price"] / properties_with_size_and_price["sizeSqFeetMax"]

Si ejecutamos este código, obtendremos de nuevo la dirección SettingWithCopyWarning. Esto no debería sorprenderte porque hemos creado y modificado explícitamente una variable temporal del DataFrame properties_with_size_and_price.

Como queremos trabajar con una copia de los datos y no con el DataFrame original, podemos solucionar el problema asegurándonos de que properties_with_size_and_price es una copia fresca del DataFrame y no una vista utilizando el método .copy() en la primera línea:

properties_with_size_and_price = df[df["sizeSqFeetMax"].notna() & (df["price"] != "POA")].copy()

Añadir nuevas columnas con seguridad

La creación de nuevas columnas se comporta del mismo modo que la asignación de valores. Siempre que resulte ambiguo si estamos trabajando con una copia o con una vista, pandas lanzará un mensaje SettingWithCopyWarning.

Si queremos trabajar con una copia de los datos, debemos copiarlos explícitamente utilizando el método .copy(). Luego, somos libres de asignar una nueva columna de la forma que queramos. Lo hicimos al crear la columna pricePerSqFt en el ejemplo anterior.

Por otra parte, si queremos modificar el DataFrame original hay dos casos a considerar. 

  1. Si la nueva columna abarca todas las filas, podemos modificar directamente el DataFrame original. Esto no provocará una advertencia porque no estaremos seleccionando un subconjunto de las filas. Por ejemplo, podríamos añadir una columna note para cada fila en la que falte el tipo de casa: 
df["notes"] = df["propertyType"].apply(lambda house_type: "Missing house type" if house_type == "Not Specified" else "")
  1. Si la nueva columna sólo define valores para un subconjunto de filas, podemos utilizar la propiedad loc indexador. Por ejemplo:
df.loc[df["propertyType"] == "Not Specified", "notes"] = "Missing house type"

Ten en cuenta que, en este caso, el valor de las columnas que no se seleccionaron será indefinido, por lo que es preferible el primer enfoque, ya que nos permite especificar un valor para cada fila.

SettingWithCopyWarning Error en Pandas 3.0

Ahora mismo, SettingWithCopyWarning es sólo una advertencia, no un error. Nuestro código se sigue ejecutando, y Pandas simplemente nos informa de que tengamos cuidado. 

Según la documentación oficial de PandasSettingWithCopyWarning dejará de utilizarse a partir de la versión 3.0 y será sustituido por un verdadero error por defecto, aplicando normas de código más estrictas.

Información sobre la actualización de Pandas 3.0

Para asegurarnos de que nuestro código sigue siendo compatible con futuras versiones de pandas, se recomienda actualizarlo ya para que lance un error en lugar de una advertencia.

Esto se hace configurando la siguiente opción después de importar pandas:

import pandas as pd
pd.options.mode.copy_on_write = True

Añadiendo esto al código existente nos aseguraremos de que tratamos cada asignación ambigua en nuestro código y nos aseguraremos de que el código sigue funcionando cuando actualicemos a pandas 3.0.

Conclusión

El SettingWithCopyWarning se produce siempre que nuestro código hace que sea ambiguo si un valor que estamos modificando es una vista o una copia. Podemos solucionarlo siendo siempre explícitos sobre lo que queremos:

  • Si queremos trabajar con una copia, debemos copiarla explícitamente utilizando el método copy().
  • Si queremos modificar el DataFrame original, debemos utilizar la propiedad indexadora loc y asignar el valor directamente al acceder a los datos, sin utilizar variables intermedias.

A pesar de no ser un error, no debemos ignorar esta advertencia porque puede dar lugar a resultados inesperados. Además, a partir de Pandas 3.0, se convertirá en un error por defecto, por lo que deberíamos preparar nuestro código para el futuro activando Copy-on-Write en nuestro código actual mediante pd.options.mode.copy_on_write = True. Esto garantizará que el código siga siendo funcional para futuras versiones de Pandas.

Preguntas frecuentes

¿Por qué recibo SettingWithCopyWarning aunque utilice .loc?

Utilizar loc es siempre seguro y no provocará ninguna advertencia ni comportamientos inesperados, siempre que df no se haya creado a partir de otro DataFrame. Por ejemplo, si df es el DataFrame que se creó al leer el conjunto de datos, entonces es seguro. Sin embargo, si es el resultado de un cálculo intermedio, sólo es seguro si el DataFrame se copió utilizando el método copy().

¿Cómo suprimir SettingWithCopyWarning?

Podemos utilizar el paquete warnings y añadir el código:

import warnings
warnings.simplefilter(action='ignore', category=pd.errors.SettingWithCopyWarning)

Sin embargo, se recomienda encarecidamente no suprimir ni ignorar esta advertencia, ya que significa que tu código podría no estar haciendo lo que crees que está haciendo.

¿Cómo ignorar SettingWithCopyWarning?

Podemos ignorar la advertencia temporalmente utilizando el paquete warnings y envolviendo nuestro código con:

import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore", category=pd.errors.SettingWithCopyWarning)
  # Our code goes here

Sin embargo, se recomienda encarecidamente no suprimir ni ignorar esta advertencia, ya que significa que tu código podría no estar haciendo lo que crees que está haciendo.

¿Cómo arreglo SettingWithCopyWarning al añadir una nueva columna?

La corrección de la advertencia al añadir una columna se realiza del mismo modo que al asignar valores. Asegúrate de copiar primero el DataFrame si pretendes trabajar con una copia de los datos o utilizar loc.

He seguido esta guía y sigo recibiendo el aviso SettingWithCopyWarning. ¿Cómo puedo solucionarlo?

En las versiones más recientes de pandas, seguir las prácticas de este tutorial no debería provocar ninguna advertencia.

Si utilizas una versión anterior, te aconsejamos que la actualices a la última versión estable. Se sabe que las versiones antiguas tienen errores en torno a esta advertencia y que la activan cuando no hay ningún problema con el código.

Si la versión de pandas está actualizada, es probable que el problema subyacente sea un caso de indexación encadenada no evidente. Revisa detenidamente cómo se creó el Marco de datos.


Photo of François Aubry
Author
François Aubry
LinkedIn
Enseñar siempre ha sido mi pasión. Desde mis primeros días como estudiante, busqué con entusiasmo oportunidades para dar clases particulares y ayudar a otros estudiantes. Esta pasión me llevó a realizar un doctorado, en el que también trabajé como ayudante de profesor para apoyar mis esfuerzos académicos. Durante esos años, encontré una inmensa satisfacción en el entorno tradicional del aula, fomentando las conexiones y facilitando el aprendizaje. Sin embargo, con la llegada de las plataformas de aprendizaje en línea, reconocí el potencial transformador de la educación digital. De hecho, participé activamente en el desarrollo de una plataforma de este tipo en nuestra universidad. Estoy profundamente comprometida con la integración de los principios de la enseñanza tradicional con metodologías digitales innovadoras. Mi pasión es crear cursos que no sólo sean atractivos e informativos, sino también accesibles para los alumnos en esta era digital.
Temas

¡Aprende Pandas con estos cursos!

curso

Joining Data with pandas

4 hr
163.9K
Learn to combine data from multiple tables by joining data together using pandas.
Ver detallesRight Arrow
Comienza El Curso
Ver másRight Arrow
Relacionado

tutorial

Python Copy List: Lo que debes saber

Comprende cómo copiar listas en Python. Aprende a utilizar las funciones copy() y list(). Descubre la diferencia entre copias superficiales y profundas.
Allan Ouko's photo

Allan Ouko

7 min

tutorial

Tutorial de Pandas: DataFrames en Python

Explora el análisis de datos con Python. Los DataFrames de Pandas facilitan la manipulación de tus datos, desde la selección o sustitución de columnas e índices hasta la remodelación de tus datos.
Karlijn Willems's photo

Karlijn Willems

20 min

tutorial

Tutorial de pandas en Python: la guía definitiva para principiantes

¿Todo preparado para comenzar tu viaje de pandas? Aquí tienes una guía paso a paso sobre cómo empezar.
Vidhi Chugh's photo

Vidhi Chugh

15 min

tutorial

Tutorial pandas read_csv(): importación de datos

La importación de datos es el primer paso importante en cualquier proyecto de ciencia de datos. Aprende por qué los científicos de datos actuales prefieren la función pandas read_csv() para hacerlo.
Kurtis Pykes 's photo

Kurtis Pykes

9 min

tutorial

Tutorial seleccionar columnas con Python

Utiliza Python Pandas y selecciona columnas de los DataFrames. ¡Sigue nuestro tutorial con ejemplos de código y aprende hoy mismo distintas formas de seleccionar tus datos!
DataCamp Team's photo

DataCamp Team

7 min

tutorial

Tutorial de unión de DataFrames en pandas

En este tutorial, usted aprenderá varias maneras en las que múltiples DataFrames pueden ser fusionados en python usando la librería Pandas.
DataCamp Team's photo

DataCamp Team

19 min

See MoreSee More