Pandas 에서 반복을 효율적으로 처리하는 방법
Pandas 를 통해 데이터 프로세싱을 할 때 종종 해야할일은 행에 반복적으로 접근을 하면서 그 값을 조작하는 일이다. 예를 들어, missing value 가 0 으로 코딩이 되어있는데, 이를 다른 값으로 바꾸고 싶을 경우 또는 A 컬럼의 값이 missing 일 때, B 컬럼의 값을 수정하고 싶은 경우 등이 있다. 이러한 작업을 하기 위해서는 모든 행을 조회 하면서 값을 조회하고 수정하는 일이 필요하다. 이번 포스팅에서는 이러한 반복작업이 필요한 상황에서 어떤 방법이 가장 효율적일지에 대해 정리해보려고한다.
사용할 데이터
1) pd.iterrows()
가장 기본적이고 많이 사용하는 방법이 iterrows 함수를 이용하는 것이다. 하지만 iterrows 함수는 다른 방법에 비해 느린 편이다.
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns %matplotlib inline diabetes = pd.read_csv("diabetes.csv") diabetes.head()
Pregnancies | Glucose | BloodPressure | SkinThickness | Insulin | BMI | DiabetesPedigreeFunction | Age | Outcome | |
---|---|---|---|---|---|---|---|---|---|
0 | 6 | 148 | 72 | 35 | 0 | 33.6 | 0.627 | 50 | 1 |
1 | 1 | 85 | 66 | 29 | 0 | 26.6 | 0.351 | 31 | 0 |
2 | 8 | 183 | 64 | 0 | 0 | 23.3 | 0.672 | 32 | 1 |
3 | 1 | 89 | 66 | 23 | 94 | 28.1 | 0.167 | 21 | 0 |
4 | 0 | 137 | 40 | 35 | 168 | 43.1 | 2.288 | 33 | 1 |
missing value 가 0 으로 코딩이 되어있는데, 이를 nan 으로 바꾸는 코드를 iterrows 를 이용해서 짜보자.
def fix_missing(df, col): for i, row in df.iterrows(): val = row[col] if val == 0: df.loc[i, col] = np.nan
%timeit fix_missing(diabetes, "SkinThickness")
33.9 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
2) pd.loc[]/pd.iloc[]
두 번째 방법은 index 를 통해 for 문을 돌면서, loc 또는 iloc 함수를 이용해 dataframe의 row에 접근하는 방법이다.
def fix_missing2(df, col): for i in df.index: val = df.loc[i, col] if val == 0 : df.loc[i, col] = np.nan
%timeit fix_missing2(diabetes, "Insulin")
9.54 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
1) iterrow 방법에 비해 약 3배 빨라졌다는 것을 알 수 있다. 따라서 iterrows 가 익숙하다고 하더라도 다른 방법으로 바꾸는 것이 같은 작업을 더 빠르게 실행할 수 있어 효율적이다.
3) pd.get_value()/pd.set_value()
다음은 위 방법과 마찬가지로 index를 통해 for 문을 돌면서 get_value 와 set_value 함수를 이용하는 방법이다. 2) 방법이 내부적으로 get_value, set_value를 호출하는 것이기 때문에 3) 이를 직접적으로 호출하는 방법이므로 더욱 빠르다.
def fix_missing3(df, col): for i in df.index: val = df.get_value(i, col) if val == 0: df.set_value(i, col, np.nan)
%timeit fix_missing3(diabetes, "BMI")
3.65 ms ± 31.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2)에 비해 3배 정도 빨라졌으며, 1)에 비해서는 거의 10배 정도의 속도차이가 난다.
4) pd.apply()
네 번째 방법은 apply 를 이용하는 것이다. apply 를 이용하는 것은 특별한 형태의 function 을 필요로 하는데 (이를 helper function 이라고도 한다), 이것은 Series 혹은 Dataframe의 각 원소마다 적용시킬 함수이다.
def fix_missing4(x): if x == 0 : return -999 else: return x %timeit diabetes.Age.apply(fix_missing4)
483 µs ± 3.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
- Default 방법으로 index 를 돌면서 set_value 와 get_value 를 호출하는 방법을 추천
- 비교적 큰 데이터에셋에서 비교적 간단한 작업을 할 때, apply 함수가 가장 효율적
참고
'Tools > Python' 카테고리의 다른 글
환경 파일을 통한 conda 가상환경 생성 (0) | 2019.07.18 |
---|---|
Jupyter 유용한 확장기능 - lab_black (0) | 2019.07.11 |
Jupyter Lab 단축키 및 매직 기능 팁 (2) | 2019.04.09 |
Python PEP8 code convention (1) | 2019.03.25 |
Python PDPbox 패키지 설치시 문제 해결 (0) | 2018.12.20 |