본문 바로가기
데이터 분석/데이터 전처리

[Pandas Tip] 오류 해결 - SettingWithCopyWarning

by 부자 꽁냥이 2023. 2. 10.

안녕하세요~ 꽁냥이에요. Pandas로 특정 조건에 맞는 행을 추출해서 만든 데이터를 가지고 새로운 칼럼을 만들거나 특정 셀 값을 변경하려고 할 때 종종 SettingWithCopyWarning 경고가 나타나는 것을 알 수 있습니다. 이번 포스팅에서는 이러한 오류가 나는 이유를 예제와 함께 살펴보고 해결방법 또한 소개하겠습니다.


   SettingWithCopyWarning

SettingWithCopyWarning은 Chained Assignment인 경우와 Hidden Chaining의 경우에 따라 해결 방법이 다르므로 각각에 대해서 살펴보겠습니다. 또한 경고창을 끄는 방법도 살펴보겠습니다.

1) Chained Assignment

Chained Assignment를 알아보기 전에 Assignment와 Access에 대해서 알아보겠습니다. Assignment는 특정 셀에 값을 할당하거나 또는 특정 칼럼에 배열을 할당하는 것을 말하며 setting 또는 set 연산(집합이 아니라 설정한다는 뜻)의 의미를 갖습니다.

 

Access는 특정 셀, 특정 행 또는 칼럼에 접근하는 것을 말하며 getting 또는 get 연산의 의미를 갖습니다.

 

아래 코드는 Assignment와 Access를 나타낸 것입니다.

 

import pandas as pd

df = pd.DataFrame()
## Assignment
df['GROUP'] = [1, 1, 1, 2, 2, 2] ## 칼럼에 배열 할당
df['VALUE'] = [1.2, 1.3, 1.4, 3.6, 3.7, 3.1] ## 칼럼에 배열 할당
df.loc[2, 'VALUE'] = 100 ## 특정 셀에 값 할당

## Access
df.loc[2, 'VALUE'] ## 특정 셀 추출
df[df['GROUP']==1] ## 특정행 추출
df[['GROUP']] ## 특정 칼럼 추출

df

 

 

Chained Assignment는 아래와 같이 특정 조건으로 필터링을 함과 동시에 값을 할당하는 것을 말합니다. 아래 경우는 원본 데이터에서 GROUP 칼럼이 1인 경우에 대응하는 VALUE 칼럼의 값을 모두 10으로 바꾸려고 하는 것입니다.

 

df[df['GROUP'] == 1]['VALUE'] = 10
df

 

이 경우 원본 데이터는 바뀌지 않으며 아래와 같은 경고 메시지가 발생합니다.

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

 

 

이 메시지는 데이터프레임의 슬라이스(df[df['GROUP] == 1]) 복사본에 값을 할당(Assignment 또는 Set)을 시도한다는 뜻입니다. 슬라이스는 get 연산이고 할당은 set 연산이며 위 코드에서 두 연산은 서로 연결되어 있음을 알 수 있습니다. Pandas는 get 연산과 set 연산이 연결되어 있다면 그 결과가 복사본(Copy)인지 원래 데이터에서 일부를 나타내는 것(View) 중 어떤 것이 나올지 예측 불가능하다고 합니다. 다시 말해 이 경고가 나타나는 이유는 Pandas가 작성자의 의도를 모르거나 Pandas 자신도 어떤 결과가 나올지 예측이 불가능하기 때문에 이에 대한 경고를 알려주기 위함입니다. 이 경우는 원본을 바꾸고 싶은 것인지 아니면 GROUP이 1인 경우만 따로 떼어내서 새로운 데이터를 만들고 거기에 VALUE 값을 바꾸고 싶은 것인지를 모르기 때문에 경고 메시지가 출력됩니다.

 

이 경우에는 의도를 명확하게 해주면 됩니다. 여기에서는 원본 데이터를 바꾸려는 것이었으므로 loc를 사용하여 내가 바꾸려는 대상이 원본 데이터임을 명확하게 나타내주면 됩니다.

 

df.loc[df['GROUP'] == 1, 'VALUE'] = 10
df

 

 

만약 원본 데이터가 아니라 데이터를 따로 떼어내서 바꾸는 경우라면 어떻게 해야 할까요? 이 내용은 아래에서 다루겠습니다.


2) Hidden Chaining

Hidden Chaining은 특정 조건으로 행을 추출(GROUP 이 1인 행)한 것을 다른 변수에 선언하여 숨긴 다음 Assignment를 수행하는 것을 말합니다. 아래처럼 말이죠.

 

## 새로운 원본 데이터 생성
df = pd.DataFrame()
df['GROUP'] = [1, 1, 1, 2, 2, 2] ## 칼럼에 배열 할당
df['VALUE'] = [1.2, 1.3, 1.4, 3.6, 3.7, 3.1] ## 칼럼에 배열 할당

## Hidden Chaining
aa = df[df['GROUP'] == 1] ## 필터링 결과 숨김
aa.loc[0, 'VALUE'] = 10 ## Assignment
aa

 

이 코드를 실행하면 앞서 살펴본 똑같은 경고 메시지가 발생합니다.

 

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

 

다만 데이터를 출력하면 아래와 같이 바뀐 것을 알 수 있습니다.

 

 

그렇다면 이 경우에는 경고가 왜 발생하는 것일까요? 왜냐하면 위 코드도 별개인듯 보이지만 get 연산과 set 연산이 연결되어 있기 때문입니다.

아까도 말씀드렸지만 Pandas는 get 연산과 set 연산이 연결되어 있다면 그 결과가 어떻게 나올지 예측할 수가 없습니다. 따라서 이 경우에도 경고 메시지를 발생시키는 것이지요. 이 문제는 copy를 사용하여 새로운 복사본을 만들어서 "나는 새로운 복사본에 데이터를 새로 할당할 거야"라고 명확하게 해주는 것으로 해결할 수 있습니다.

 

## Hidden Chaining
aa = df[df['GROUP'] == 1].copy() ## 복사본 생성
aa.loc[0, 'VALUE'] = 10 ## Assignment
aa

 

 

앞에서는 특정 셀을 바꾸는 것을 해보았는데 아래와 같이 칼럼을 새로 만드는 경우를 생각해보겠습니다.

 

## Hidden Chaining
aa = df[df['GROUP'] == 1] ## 필터링 결과 숨김
aa['B'] = aa['VALUE'].map(lambda x:x+1) ## 칼럼 생성
aa

 

이 경우에도 SettingWithCopyWarning 경고가 나타납니다. 이때에도 copy를 쓰면 경고가 나타나지 않습니다.

 

%%time
## Hidden Chaining
aa = df[df['GROUP'] == 1].copy() ## 필터링 결과 숨김
aa['B'] = aa['VALUE'].map(lambda x:x+1) ## 칼럼 생성
aa

 

 

copy를 많이 쓰면 메모리 용량을 많이 차지할 수 있습니다. 이 경우에는 concat을 써서 경고를 회피할 수도 있습니다. 다만 시간이 1000배 가까이 차이 나는 것을 알 수 있습니다.

 

%%time
## Hidden Chaining
aa = df[df['GROUP'] == 1] ## 필터링 결과 숨김
new_col = aa['VALUE'].map(lambda x:x+1) ## Assignment
new_col.name = 'B'
aa = pd.concat([aa, new_col], axis=1)
aa

 


3) 경고창 끄기

pd.set_option을 통해서 다음과 같이 설정하면 SettingWithCopyWarning 경고창을 끌 수 있습니다.

pd.set_option('mode.chained_assignment', None)

 

실제로 아래와 같이 경고창을 끄고 코드를 실행하면 경고창이 발생하지 않습니다.

pd.set_option('mode.chained_assignment', None)
aa = df[df['GROUP'] == 1]
aa['B'] = aa['VALUE'].map(lambda x:x+1) ## 칼럼 생성
aa

 

만약 원래대로 되돌리고 싶다면 mode.chained_assignment를 'warn'로 설정하면 됩니다.

pd.set_option('mode.chained_assignment', 'warn')

 

참고로 경고 수준이 아니라 에러로 취급하고 싶다면 mode.chained_assignment를 'raise'로 설정하면 됩니다.

pd.set_option('mode.chained_assignment', 'raise')

이번 포스팅은 데이터 전처리에서 종종 발생하는 SettingWithCopyWarning을 해결하는 방법에 대해서 살펴보았습니다. 사실 해결하지 않아도 분석하는 데에는 지장이 없는데 경고창이 뜨면 찝찝하더라고요. 이번에 다룬 내용을 알아두시면 그 찝찝합을 시원하게 날려 보낼 수 있을 것 같습니다.

 

지금까지 꽁냥이 글 읽어주셔서 감사합니다.


댓글


맨 위로