주제
Pandas 데이터프레임을 반복의 최적의 방법에 대해 배웠다.
판다스 데이터프레임을 다루는 최적의 방법에 대해 배우기 위해 야구 샘플을 사용한다.
각 팀에 대한 연도, 시즌 별 특정 통계 데이터이다.
import pandas as pd
baseball_df = pd.read_csv('baseball_stats.tsv', sep=',')
baseball_df.head()
승리 횟수인 W
컬럼과 게임 횟수 G
컬럼을 이용하여 팀의 승률 통계를 구해보자.
import numpy as np
def calc_win_perc(wins, games_played):
win_perc = wins / games_played
return np.round(win_perc,2)
각 팀 별로 승률을 구하기위해 무식하게 반복문을 사용해보자.
win_perc_list = []
for i in range(len(baseball_df)):
row = baseball_df.iloc[i]
wins = row['W']
games_played = row['G']
win_perc = calc_win_perc(wins, games_played)
win_perc_list.append(win_perc)
baseball_df['WP'] = win_perc_list
baseball_df.head()
#53.1 ms ± 174 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
데이터프레임 행을 반복하는 경우, 매우 비효율적이란걸 확인했다.
pandas dataframe에는 iterrows()
함수를 제공한다.
효율적으로 행을 반복할 수 있는 함수다.
%%timeit
win_perc_list = []
for i, row in baseball_df.iterrows():
wins = row['W']
games_played = row['G']
win_perc = calc_win_perc(wins, games_played)
win_perc_list.append(win_perc)
baseball_df['WP'] = win_perc_list
#24.2 ms ± 207 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
개인적으로 판다스에서 apply
를 사용하는 것이 더 효율적임
%timeit baseball_df['WP'] = baseball_df.apply(lambda x : calc_win_perc(x.W, x.G), axis=1)
#13.7 ms ± 22 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
iterrows
보다 더 효율적인 방법이 itertuples()
이다.
팀, 연도, 우승횟수가 저장된 team_wins_df
가 있다.
team_wins_df = baseball_df[['Team', 'Year', 'W']]
team_wins_df.head()
iterrows()
를 사용하여 각 행을 출력하면 다음과 같이 Tuple에 series형태로 저장되어 있어서 값에 접근하려면 인덱싱이 필요하다.
for row_tuple in team_wins_df.iterrows():
print(row_tuple)
print(type(row_tuple[1]))
(0, Team ARI
Year 2012
W 81
Name: 0, dtype: object)
<class 'pandas.core.series.Series'>
(1, Team ATL
Year 2012
W 94
Name: 1, dtype: object)
<class 'pandas.core.series.Series'>
(2, Team BAL
Year 2012
W 93
Name: 2, dtype: object)
<class 'pandas.core.series.Series'>
(3, Team BOS
Year 2012
W 69
Name: 3, dtype: object)
<class 'pandas.core.series.Series'>
...
itertuples()
는 위와 다르게 namedtuple 타입으로 반환되어 name 을 통해 직접 값 접근이 가능하다.
for row_namedtuple in team_wins_df.itertuples():
print(row_namedtuple)
Pandas(Index=0, Team='ARI', Year=2012, W=81)
Pandas(Index=1, Team='ATL', Year=2012, W=94)
Pandas(Index=2, Team='BAL', Year=2012, W=93)
Pandas(Index=3, Team='BOS', Year=2012, W=69)
Pandas(Index=4, Team='CHC', Year=2012, W=61)
Pandas(Index=5, Team='CHW', Year=2012, W=85)
Pandas(Index=6, Team='CIN', Year=2012, W=97)
Pandas(Index=7, Team='CLE', Year=2012, W=68)
Pandas(Index=8, Team='COL', Year=2012, W=64)
Pandas(Index=9, Team='DET', Year=2012, W=88)
...
#96.8 ms ± 4.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
itertuples()
가 더 빠른 이유는 iterrows()
는 각 행 값을 series로 반환하는 부분에서 오버헤드가 발생하기 때문이다.
itertuples()
를 사용할 때 주의할 점은 pandas 자료구조나 dictionary 처럼 [키 이름]
형태를 지원하지 않기 때문에 멤버변수 접근하듯이 .키 이름
형태로 사용하면 된다.
효율적인 코드 작성법을 떠올려보자.
"루프를 최소화하자."
앞서 데이터프레임 행을 순회하기 위해 계속 루프를 사용했다.
판다스에서 제공하는 함수를 통해 이 루프를 없애고 효율적인 코드를 작성할 수 있다.
apply()
- axis=0(열) 또는 1(행)을 기준으로 데이터프레임 내 데이터에 특정 함수를 적용할 수 있다.
- 특정 함수는 사용자가 직접 정의한 함수, 내장 함수, 익명 함수가 가능하다.
판다스는 빌트인 모듈로 넘파이를 포함하기 때문에 넘파이에서 가능한 브로드캐스팅이 마찬가지로 가능하다.
또한 판다스에서 인덱싱하여 값을 가져오는 것보다 넘파이 어레이 기능을 사용하는 것이 더 빠르다.
%timeit baseball_df['WP'] = calc_win_perc(baseball_df.W.values, baseball_df.G.values)
# 80.4 µs ± 1.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Uploaded by Notion2Tistory v1.1.0