최근에 회사에서 디버깅 도중 아래와 같은 로그를 발견했었다.
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
해석:
`데이터프레임의 슬라이스로부터 만들어진 복사본에 값을 설정하려고 합니다.
해당 방법으로 시도하세요. Try using .loc[row_indexer,col_indexer] = value instead`
무엇이 문제이길래 이러한 경고문을 띄우는걸까?
아래의 예시에서는 음료 자판기에서 소다의 가격을 1100원에서 1500원으로 바꾸려고 한다.
import pandas as pd
자판기 = pd.DataFrame({
'음료수': ['Coke', 'Soda', 'Dr.Pepper', 'Fanta'],
'가격': [1000 , 1100 , 1200, 1500]
})
# "Soda"의 가격을 1100원에서 1500원으로 바꾸기.
자판기[자판기['음료수'] == 'Soda']['가격'] = 1500
print(자판기)
결과는 어떻게 되었을까?
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
자판기[자판기['음료수'] == 'Soda']['가격'] = 1500
음료수 가격
0 Coke 1000
1 Soda 1100 <--- 가격이 그대로다 😫
2 Dr.Pepper 1200
3 Fanta 1500
엥?? 내가 의도한대로 변경되지 않았다.
이는 우리가 기대했던 대로 동작 하지 않을 가능성이 있다는 뜻이다.
공식문서에 의하면 chained assignment
를 데이터 프레임에 적용할 경우 원본이 아닌 복사본에 값이 변경될 수 있어 정상적인 리턴 값을 보장할 수 없다고 한다.
값을 변경하고자 할 경우 두 가지의 방법을 이용하라고 한다.
.loc
사용하기.loc
의 경우 데이터프레임에 직접 인덱싱 및 접근하여 값을 변경해준다고 한다.
import pandas as pd
자판기 = pd.DataFrame({
'음료수': ['Coke', 'Soda', 'Dr.Pepper', 'Fanta'],
'가격': [1000 , 1100 , 1200, 1500]
})
자판기.loc[자판기['음료수'] == 'Soda', '가격'] = 1500
print(자판기)
"""
결과 :
음료수 가격
0 Coke 1000
1 Soda 1500 <-- 변경 됨!
2 Dr.Pepper 1200
3 Fanta 1500
"""
copy()
이용하기슬라이싱한 데이터프레임을 원본 데이터프레임과 독립적인 복사본으로 사용하고자 할 경우 다음과 같이 사용 가능하다.
나는_복제_자판기 = 자판기.loc[자판기['음료수'] == 'Dr.Pepper'].copy()
나는_복제_자판기['가격'] = 2200
print(나는_복제_자판기)
"""
결과 :
2 Dr.Pepper 2200
"""
이렇게 사용하면 원본 데이터프레임에 영향을 끼치지 않고 값 변경이 가능하다.
pandas
의 데이터프레임을 핸들링 할 때 필터링과 값 변경을 동시에 하고자 할 경우 loc
또는 copy()
를 이용하면 된다.
이를 무시하고 변경하고자 하면 값 변경의 보장을 못한다.