Failures & Issues/problem-solution

SettingWithCopyWarning 해결법?

metamong 2022. 4. 13.

dataframe을 조작하다 종종 마주하는 SettingWithCopyWarning 에러

1. SettingWithCopyWarning이란?

> 예를 들어서 설명하자

 

- toInt()라는 함수를 통해 df_subset의 '매출액' column data를 모두 int 정수형으로 바꾸고자 한다 -

 

def toInt(string):
    return int(string.replace(',',''))

df_subset['매출액'] = df_subset['매출액'].apply(toInt)
df_subset

 

> 이 때 SettingWithCopyWarning 발생

 

 

→ 바로 원본 dataframe의 copy 복사본에서 수정을 가한 것이므로

→ 원본 data에 수정을 가하는 것에 대한 potential warning을 미리 알려주는 것이다.

→ 예시에서는 df_subset이 원본 dataframe인데 df_subset['매출액'] 복사본을 internally하게 따로 가져와서 apply 함수를 적용시켜 df_subset 원본 dataframe 정보를 바꾸려는 시도 - 이에 대한 Warning!

 연쇄적으로 internally하게 dataframe 일부 또는 전체를 copy해서 copy한 결과에 수정을 가하여 원본을 바꾸는  'chained assignment'라고 한다

 

> 다른 예를 한 번 더 보자

 

1>

 

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

 

2>

 

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

1    3
2    6
Name: B, dtype: int64

 

 1>과 2>는 결과론적으로 보면 똑같아 보인다. 하지만 1>의 경우 df에서 internally하게 복사본을 생성해 conditioning을 진행한다

 

> 그렇다면 도대체 왜 Warning을 알려주는 것일까?

→ 위 1>과 2>의 internal operations를 따져보자

 

#1>
df.__getitem__(df.A > 5).__setitem__('B', 4)

#2>
df.__setitem__((df.A > 5, 'B'), 4)

 

→ 1>의 경우 __getitem__이 view나 copy를 돌려줄 지 우리는 확신할 수 없다는 뜻이다. 돌려주지 못하면 뒤의 __setitem__이 동작을 못한다.

→ 따라서, internal 복사본이 잘 return되지 않거나 잘못된 copy가 생길 것을 우려하여 미리 SettinWithCopyWarning을 발생시키는 것

2. SettingWithCopyWarning 해결법

* 이제 무엇인지도 알았고 얘네가 왜 이 메세지를 보여주는 지도 알겠으니 해결법을 찾아보자

* 크게 세 가지로 나눌 수 있다 - 무시, 복사, iloc & loc 활용

 

1> 의도 알겠으니까 걍 Warning 무시

→ 아마도 많은 사람들이 구글링의 결과 코드 복붙해서 1>의 방법으로 해결했을 것이다.

→ 비추. warning을 의도적으로 suppresing하는 건 코딩에 있어서 바람직한 자세이지 않으며 후에 실제 문제로 발생할 warning을 무시해 문제가 발생할 수도 있기에 하지 않는 것을 추천.

 

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

 

2> 명시적으로 copy본을 따로 만들어서 거기서 수정

→ 그렇다면 명시적으로 복사본을 만들면 chained assignment할 필요 없이 쉽게 수정을 가할 수 있다.

 copy() 사용해 예시의 경우 df_subset2라는 복사본을 만들었다. (Warning 발생 안함!)

 

df_subset2 = df_subset.copy()
df_subset2['매출액'] = df_subset2['매출액'].apply(toInt)
df_subset2

 

3> (BEST) .loc & .iloc 활용하기

→ best of THE BEST! 위 2> 코드 처럼 loc & iloc을 활용하면 원본 dataframe에서 원하는 data의 위치를 잡아 바로 수정할 수 있으므로 따로 복사 필요 없이 그대로 편하게 수정을 가할 수 있다.

 

df_subset.loc[:,"매출액"].apply(toInt)

** 내용 일부 출처) https://stackoverflow.com/questions/20625582/how-to-deal-with-settingwithcopywarning-in-pandas

** 썸네일 출처) https://www.analyticsvidhya.com/blog/2021/11/3-ways-to-deal-with-settingwithcopywarning-in-pandas/

댓글