Pular para o conteúdo principal

SettingWithCopyWarning em Pandas: Como corrigir esse aviso

Saiba como corrigir o SettingWithCopyWarning do Pandas: Um valor está tentando ser definido em uma cópia de uma fatia de um DataFrame.
Actualizado 17 de nov. de 2024  · 8 min de leitura

SettingWithCopyWarning é um aviso que o Pandas pode gerar quando fazemos uma atribuição a um DataFrame. Isso pode acontecer quando usamos atribuições encadeadas ou quando usamos um DataFrame criado a partir de uma fatia. Essa é uma fonte comum de erros no código do Pandas que todos nós já enfrentamos. Isso pode ser difícil de depurar porque o aviso pode aparecer em um código que parece funcionar bem.

Entender o site SettingWithCopyWarning é importante porque ele sinaliza possíveis problemas com a manipulação de dados. Esse aviso sugere que seu código pode não estar alterando os dados conforme pretendido, o que pode resultar em consequências não intencionais e bugs obscuros que são difíceis de rastrear.

Neste artigo, exploraremos o site SettingWithCopyWarning em pandas e como evitá-lo. Você pode encontrar o site em pandas e como evitá-lo. Também discutiremos o futuro do Pandas e como a opção copy_on_write mudará a forma como trabalhamos com DataFrames.

Visualizações e cópias de DataFrame

Quando selecionamos uma fatia de um DataFrame e a atribuímos a uma variável, podemos obter uma visualização ou uma nova cópia do DataFrame.

Com uma visualização, a memória entre os dois DataFrames é compartilhada. Isso significa que, ao modificar um valor de uma célula presente em ambos os DataFrames, você modificará os dois.

Exemplo de uma exibição do Pandas DataFrame

Com uma cópia, uma nova memória é alocada e um DataFrame independente com os mesmos valores que o original é criado. Nesse caso, os dois DataFrames são entidades distintas, portanto, a modificação de um valor em um deles não afeta o outro.

Exemplo de cópia de um DataFrame

O Pandas tenta evitar a criação de uma cópia sempre que possível para otimizar o desempenho. No entanto, é impossível prever com antecedência se você receberá uma visualização ou uma cópia. O SettingWithCopyWarning é acionado sempre que atribuímos um valor a um DataFrame para o qual não está claro se é uma cópia ou uma visualização de outro DataFrame.

Entendendo o SettingWithCopyWarning com dados reais

Usaremos esse conjunto de dados do Kaggle Real Estate Data London 2024 para saber como ocorre o SettingWithCopyWarning e como corrigi-lo. 

Esse conjunto de dados contém dados imobiliários recentes de Londres. Aqui está uma visão geral das colunas presentes no conjunto de dados:

  • addedOn: A data em que a listagem foi adicionada.
  • title: O título da listagem.
  • descriptionHtml: Uma descrição HTML da listagem.
  • propertyType: O tipo de propriedade. O valor será "Not Specified" se o tipo não tiver sido especificado.
  • sizeSqFeetMax: O tamanho máximo em pés quadrados.
  • bedrooms: O número de quartos.
  • listingUpdatedReason: Motivo para atualizar a listagem (por exemplo, nova listagem, redução de preço).
  • price: O preço da listagem em libras.

Exemplo com uma variável temporária explícita

Digamos que você saiba que as propriedades com um tipo de propriedade não especificado são casas. Portanto, queremos atualizar todas as linhas com propertyType igual a "Not Specified" para "House". Uma maneira de fazer isso é filtrar as linhas com um tipo de propriedade não especificado em uma variável DataFrame temporária e atualizar os valores da coluna propertyType da seguinte maneira:

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"

A execução desse código fará com que o pandas produza o 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"

O motivo disso é que o pandas não consegue saber se o no_property_type DataFrame é uma visualização ou uma cópia do df

Quando atribuímos parte do dataframe a uma variável, podemos obter uma visualização ou uma cópia, mas não podemos saber qual delas obtivemos

Isso é um problema porque o comportamento do código a seguir pode ser muito diferente, dependendo de você ser uma visualização ou uma cópia. 

Neste exemplo, nosso objetivo é modificar o DataFrame original. Isso só ocorrerá se no_property_type for uma visualização. Se o restante do nosso código presumir que df foi modificado, ele pode estar errado porque não há como garantir que esse seja o caso. Devido a esse comportamento incerto, o Pandas lança o aviso para nos informar sobre esse fato.

Mesmo que o nosso código seja executado corretamente porque obtivemos uma visualização, podemos obter uma cópia em execuções subsequentes e o código não funcionará como pretendido. Portanto, é importante não ignorar esse aviso e garantir que nosso código sempre faça o que você deseja.

Exemplo com uma variável temporária oculta

No exemplo anterior, está claro que uma variável temporária está sendo usada porque estamos atribuindo explicitamente parte do DataFrame a uma variável chamada no_property_type.

Entretanto, em alguns casos, isso não é tão explícito. O exemplo mais comum em que o SettingWithCopyWarning ocorre é com a indexação encadeada. Suponha que substituamos as duas últimas linhas por uma única linha:

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

À primeira vista, não parece que uma variável temporária esteja sendo criada. No entanto, sua execução também resulta em um SettingWithCopyWarning

A maneira como esse código é executado é:

  1. df[df["propertyType"] == "Not Specified"] é avaliado e armazenado temporariamente na memória. 
  2. O índice ["propertyType"] desse local de memória temporária é acessado. 

Os acessos ao índice são avaliados um a um e, portanto, a indexação encadeada resulta no mesmo aviso, pois não sabemos se os resultados intermediários são exibições ou cópias

Os acessos ao índice são avaliados um a um e, portanto, a indexação encadeada resulta no mesmo aviso, pois não sabemos se os resultados intermediários são exibições ou cópias. O código acima é essencialmente o mesmo que você faz:

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

Esse exemplo costuma ser chamado de indexação encadeada porque encadeamos os acessos indexados usando []. Primeiro, acessamos [df["propertyType"] == "Not Specified"] e depois ["propertyType"].

Indexação encadeada

Como resolver o problema SettingWithCopyWarning

Vamos aprender a escrever nosso código para que não haja ambiguidade e o SettingWithCopyWarning não seja acionado. Descobrimos que o aviso surge de uma ambiguidade sobre se um DataFrame é uma visualização ou uma cópia de outro DataFrame.

A maneira de corrigir isso é garantir que cada DataFrame que criamos seja uma cópia, se quisermos que seja uma cópia, ou uma exibição, se quisermos que seja uma exibição.

Modificar com segurança o DataFrame original com loc 

Vamos corrigir o código do exemplo acima, no qual queremos modificar o DataFrame original. Para evitar o uso de uma variável temporária, use a propriedade loc indexer.

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

Com esse código, estamos agindo diretamente no df DataFrame original por meio da propriedade loc indexer, portanto, não há necessidade de variáveis intermediárias. Isso é o que precisamos fazer quando quisermos modificar diretamente o DataFrame original.

À primeira vista, isso pode parecer uma indexação encadeada, pois ainda há parâmetros, mas não é assim. O que define cada indexação são os colchetes [].

Índice duplo local vs. indexação encadeada

Observe que o uso do loc só é seguro se atribuirmos um valor diretamente, como fizemos acima. Se, em vez disso, usarmos uma variável temporária, cairemos novamente no mesmo problema. Aqui estão dois exemplos de código que não corrigem o problema: 

  1. Usando loc com uma variável temporária:
# 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. Usando loc junto com um índice (o mesmo que indexação encadeada):
# Using loc plus indexing is the same as chained indexing
df.loc[df["propertyType"] == "Not Specified"]["propertyType"] = "House"

Esses dois exemplos tendem a confundir as pessoas porque é um equívoco comum pensar que, enquanto houver um loc, estaremos modificando os dados originais. Isso está incorreto. A única maneira de garantir que o valor esteja sendo atribuído ao DataFrame original é atribuí-lo diretamente usando um único loc sem nenhuma indexação separada.

Trabalhar com segurança com uma cópia do DataFrame original com copy()

Quando quisermos ter certeza de que estamos operando em uma cópia do DataFrame, devemos usar o método o método .copy() .

Digamos que nos peçam para analisar o preço por metro quadrado das propriedades. Não queremos modificar os dados originais. O objetivo é criar um novo DataFrame com os resultados da análise para enviar a outra equipe.

A primeira etapa é filtrar algumas linhas e limpar os dados. Especificamente, precisamos:

  • Remova as linhas em que sizeSqFeetMax não está definido.
  • Remova as linhas em que o site price é "POA" (preço sob consulta).
  • Converta os preços em valores numéricos (no conjunto de dados original, os preços são cadeias de caracteres com o seguinte formato: "£25,000,000").

Você pode executar as etapas acima usando o código a seguir:

# 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 o preço por pé quadrado, criamos uma nova coluna cujos valores são o resultado da divisão da coluna price pela coluna sizeSqFeetMax:

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

Se executarmos esse código, você receberá o endereço SettingWithCopyWarning novamente. Isso não deve ser uma surpresa, pois criamos e modificamos explicitamente uma variável DataFrame temporária properties_with_size_and_price.

Como queremos trabalhar em uma cópia dos dados em vez do DataFrame original, podemos corrigir o problema certificando-nos de que properties_with_size_and_price é uma cópia nova do DataFrame e não uma visualização, usando o método o método .copy() na primeira linha:

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

Adicionar novas colunas com segurança

A criação de novas colunas se comporta da mesma forma que a atribuição de valores. Sempre que for ambíguo se você estiver trabalhando com uma cópia ou com uma visualização, o pandas exibirá um SettingWithCopyWarning.

Se quisermos trabalhar com uma cópia dos dados, devemos copiá-los explicitamente usando o método .copy(). Então, você pode atribuir uma nova coluna da maneira que quiser. Fizemos isso quando criamos a coluna pricePerSqFt no exemplo anterior.

Por outro lado, se quisermos modificar o DataFrame original, há dois casos a serem considerados. 

  1. Se a nova coluna abranger todas as linhas, você poderá modificar diretamente o DataFrame original. Isso não causará um aviso porque não estaremos selecionando um subconjunto das linhas. Por exemplo, podemos adicionar uma coluna note para cada linha em que o tipo de casa estiver faltando: 
df["notes"] = df["propertyType"].apply(lambda house_type: "Missing house type" if house_type == "Not Specified" else "")
  1. Se a nova coluna definir valores apenas para um subconjunto de linhas, você poderá usar a propriedade de indexador loc. Por exemplo:
df.loc[df["propertyType"] == "Not Specified", "notes"] = "Missing house type"

Observe que, nesse caso, o valor nas colunas que não foram selecionadas será indefinido, portanto, a primeira abordagem é preferível, pois nos permite especificar um valor para cada linha.

SettingWithCopyWarning Erro no Pandas 3.0

No momento, o site SettingWithCopyWarning é apenas um aviso, não um erro. Nosso código ainda é executado, e o Pandas simplesmente nos informa que devemos ter cuidado. 

De acordo com a a documentação oficial do PandasSettingWithCopyWarning não será mais usado a partir da versão 3.0 e será substituído por um error por padrão, impondo padrões de código mais rígidos.

Informações sobre a atualização do Pandas 3.0

Para garantir que nosso código permaneça compatível com futuras versões do pandas, é recomendável que você já o atualize para gerar um erro em vez de um aviso.

Para isso, você deve definir a seguinte opção após a importação do pandas:

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

Ao adicionar isso ao código existente, você se certificará de que lidamos com cada atribuição ambígua em nosso código e de que o código ainda funcionará quando atualizarmos para o pandas 3.0.

Conclusão

O SettingWithCopyWarning ocorre sempre que nosso código torna ambíguo se um valor que estamos modificando é uma visualização ou uma cópia. Podemos corrigir isso sendo sempre explícitos sobre o que queremos:

  • Se quisermos trabalhar com uma cópia, devemos copiá-la explicitamente usando o método copy().
  • Se quisermos modificar o DataFrame original, devemos usar a propriedade loc indexer e atribuir o valor diretamente ao acessar os dados sem usar variáveis intermediárias.

Apesar de não ser um erro, não devemos ignorar esse aviso, pois ele pode levar a resultados inesperados. Além disso, a partir do Pandas 3.0, isso se tornará um erro por padrão, portanto, devemos preparar nosso código para o futuro ativando a Copy-on-Write em nosso código atual usando o pd.options.mode.copy_on_write = True. Isso garantirá que o código permaneça funcional para futuras versões do Pandas.

Perguntas frequentes

Por que recebo o aviso SettingWithCopyWarning mesmo quando uso .loc?

O uso do loc é sempre seguro e não acionará um aviso nem levará a um comportamento inesperado, desde que o df não tenha sido criado a partir de outro DataFrame. Por exemplo, se df for o DataFrame que foi criado a partir da leitura do conjunto de dados, então é seguro. No entanto, se for o resultado de um cálculo intermediário, só será seguro se o DataFrame tiver sido copiado usando o método copy().

Como suprimir o SettingWithCopyWarning?

Podemos usar o pacote warnings e adicionar o código:

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

No entanto, é altamente recomendável não suprimir ou ignorar esse aviso, pois isso significa que seu código pode não estar fazendo o que você acha que está fazendo.

Como ignorar o SettingWithCopyWarning?

Podemos ignorar o aviso usando temporariamente o pacote warnings e encapsulando nosso código com:

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

No entanto, é altamente recomendável não suprimir ou ignorar esse aviso, pois isso significa que seu código pode não estar fazendo o que você acha que está fazendo.

Como faço para corrigir o SettingWithCopyWarning ao adicionar uma nova coluna?

A correção do aviso ao adicionar uma coluna é feita da mesma forma que na atribuição de valores. Certifique-se de copiar o DataFrame primeiro se você pretende trabalhar em uma cópia dos dados ou usar o site loc.

Segui essa orientação e ainda estou recebendo o aviso SettingWithCopyWarning. Como posso consertar isso?

Nas versões mais recentes do pandas, se você seguir as práticas deste tutorial, não deverá receber nenhum aviso.

Se você estiver usando uma versão mais antiga, recomendamos que a atualize para a versão estável mais recente. Sabe-se que as versões mais antigas têm bugs em torno desse aviso e o acionam quando não há nenhum problema com o código.

Se a versão do pandas estiver atualizada, é provável que o problema subjacente seja um caso de indexação encadeada não óbvia. Analise cuidadosamente como o DataFrame foi criado.


Photo of François Aubry
Author
François Aubry
LinkedIn
Ensinar sempre foi minha paixão. Desde meus primeiros dias como estudante, eu buscava ansiosamente oportunidades para dar aulas particulares e ajudar outros alunos. Essa paixão me levou a fazer um doutorado, onde também atuei como assistente de ensino para apoiar meus esforços acadêmicos. Durante esses anos, encontrei imensa satisfação no ambiente tradicional da sala de aula, promovendo conexões e facilitando o aprendizado. Entretanto, com o advento das plataformas de aprendizagem on-line, reconheci o potencial transformador da educação digital. Na verdade, participei ativamente do desenvolvimento de uma dessas plataformas em nossa universidade. Estou profundamente comprometido com a integração dos princípios tradicionais de ensino com metodologias digitais inovadoras. Minha paixão é criar cursos que não sejam apenas envolventes e informativos, mas também acessíveis aos alunos nesta era digital.
Temas

Aprenda sobre Pandas com estes cursos!

curso

Joining Data with pandas

4 hr
163.9K
Learn to combine data from multiple tables by joining data together using pandas.
Ver DetalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

tutorial

Tutorial de junção de DataFrames no pandas

Neste tutorial, você aprenderá várias maneiras pelas quais vários DataFrames podem ser mesclados em python usando a biblioteca Pandas.
DataCamp Team's photo

DataCamp Team

19 min

tutorial

Tutorial do Python pandas: O guia definitivo para iniciantes

Você está pronto para começar sua jornada com os pandas? Aqui está um guia passo a passo sobre como você pode começar.
Vidhi Chugh's photo

Vidhi Chugh

15 min

tutorial

Pandas Tutorial: DataFrames em Python

Explore a análise de dados com Python. Os DataFrames do Pandas facilitam a manipulação de seus dados, desde a seleção ou substituição de colunas e índices até a remodelagem dos dados.
Karlijn Willems's photo

Karlijn Willems

20 min

tutorial

Python Copy List: O que você deve saber

Entenda como copiar listas em Python. Saiba como usar as funções copy() e list(). Descubra a diferença entre cópias superficiais e profundas.
Allan Ouko's photo

Allan Ouko

7 min

tutorial

Tutorial de seleção de colunas em Python

Use o Python Pandas e selecione colunas de DataFrames. Siga nosso tutorial com exemplos de código e aprenda diferentes maneiras de selecionar seus dados hoje mesmo!
DataCamp Team's photo

DataCamp Team

7 min

tutorial

Tutorial pandas read csv(): Importação de dados

A importação de dados é a primeira etapa de qualquer projeto de ciência de dados. Saiba por que os cientistas de dados atuais preferem a função read_csv() do pandas para fazer isso.
Kurtis Pykes 's photo

Kurtis Pykes

9 min

See MoreSee More