판다스(pandas)

판다스는 R에서 영감을 받아 개발된 데이터프레임 자료구조를 파이썬에 구현한 것이다. 판다스(pandas)는 넘파이(numpy)를 기반으로 데이터프레임을 파이썬에 구현한 자료구조다.

판다스 기초

Jay Alammar, "A Gentle Visual Intro to Data Analysis in Python Using Pandas"을 바탕으로 판다스에서 데이터프레임을 처리하는 기본기를 익혀보자

.csv 파일 읽어오기

다음과 같이 csv 파일을 준비하고 읽어온다.

Artists, Genre, Listeners, Plays
Billie Holiday, Jazz, 1300000, 27000000
Jimi Hendrix, Rock, 2700000, 70000000
Miles Davis, Jazz, 1500000, 48000000
SIA, Pop, 2000000, 74000000

In [1]:
import pandas as pd

df = pd.read_csv("data/music.csv", delimiter=',', encoding="utf-8-sig")
df
Out[1]:
Artists Genre Listeners Plays
0 BillieHoliday Jazz 1300000 27000000
1 JimiHendrix Rock 2700000 70000000
2 MilesDavis Jazz 1500000 48000000
3 SIA Pop 2000000 74000000

선택 - 행,열

In [2]:
df['Artists']
Out[2]:
0    BillieHoliday
1      JimiHendrix
2       MilesDavis
3              SIA
Name: Artists, dtype: object
In [3]:
df[1:3]
Out[3]:
Artists Genre Listeners Plays
1 JimiHendrix Rock 2700000 70000000
2 MilesDavis Jazz 1500000 48000000

.loc를 사용해서 행과 열을 선택할 수 있다.

In [4]:
df.loc[1:3, ['Artists']]
Out[4]:
Artists
1 JimiHendrix
2 MilesDavis
3 SIA

필터링

특정 조건을 만족하는 행만 추출하는 것이 필요한 경우 부울 마스크(mask)를 생성시켜 데이터프레임에서 특정 조건을 만족하는 행만 추출시킨다.

In [5]:
df[df["Genre"] == "Jazz"]
Out[5]:
Artists Genre Listeners Plays
0 BillieHoliday Jazz 1300000 27000000
2 MilesDavis Jazz 1500000 48000000
In [6]:
df[df['Listeners'] > 1800000]
Out[6]:
Artists Genre Listeners Plays
1 JimiHendrix Rock 2700000 70000000
3 SIA Pop 2000000 74000000

결측값 처리

In [7]:
import numpy as np

df.loc[1, ["Plays"]] = np.nan
df
Out[7]:
Artists Genre Listeners Plays
0 BillieHoliday Jazz 1300000 27000000.0
1 JimiHendrix Rock 2700000 NaN
2 MilesDavis Jazz 1500000 48000000.0
3 SIA Pop 2000000 74000000.0
In [8]:
df.dropna()
Out[8]:
Artists Genre Listeners Plays
0 BillieHoliday Jazz 1300000 27000000.0
2 MilesDavis Jazz 1500000 48000000.0
3 SIA Pop 2000000 74000000.0

그룹(group)

특정 변수의 그룹값을 기준으로 sum(), max(), min() 등을 구할 수 있다.

In [9]:
df.groupby('Genre').sum()
Out[9]:
Listeners Plays
Genre
Jazz 2800000 75000000.0
Pop 2000000 74000000.0
Rock 2700000 0.0

새로운 칼럼 생성

In [10]:
df['Avg Plays'] = df['Plays'] / df['Listeners']

판다스 사용자 정의함수

판다스 데이터 가져오기

교통사고분석시스템 - 교통사고상세통계 웹사이트에서 경찰DB(국가공식통계)를 통해 OECD국가 교통사고 통계 엑셀파일을 대상으로 작업을 수행한다.

traffic_df.columns, traffic_df.head() 명령어로 데이터프레임과 친숙해진다.

In [11]:
import pandas as pd

traffic_df = pd.read_excel("data/oecd_traffic_data_utf-8.xlsx")

traffic_df.columns

traffic_df.head()
Out[11]:
국가 인구수(천명) 자동차등록대수(천대) 발생건수(건) 사망자수(명) 인구10만명당사망자수 자동차1만대당사망자수 자동차1만대당사망자순위
0 호주 23786.123 18007.8 - 1205 5.1 0.7 14
1 독일 81197.537 55752 305659 3459 4.3 0.6 16
2 그리스 10858.018 9518 11440 793 7.3 0.8 10
3 헝가리 9855.571 3822 16331 644 6.5 1.7 2
4 아이슬란드 329.100 285 912 16 4.9 0.6 20

사용자 정의 함수 사용 - range()

calc_death_percentage(사망자수, 인구수_천명) 함수를 정의해서 소수점 아래 1자리로 인구 천명당 사망자수를 산출한다. 예를 들어, 2015년 독일 인구 81,197,537에 대한 사망자수는 3,459명으로 천명당 4.3명이 나온다.

In [12]:
import numpy as np

def calc_death_percentage(death, population):
    death_perc = death / population * 100
    return np.round(death_perc, 1)

calc_death_percentage(3459, 81197.537)
Out[12]:
4.3

OECD 전체 국가에 대해서 천명당 사망자수를 계산해보자. 이를 위해서 별도 리스트(death_perc_list)를 for 루프 외곽에 준비하고 iloc 메쏘드를 통해 행을 하나 뽑아내고 각 행별로 뽑아 calc_death_percentage(death, population) 연산작업을 수행한다. 그렇게 채워진 death_perc_list 리스트를 데이터프레임에 결합시킨다.

In [13]:
death_perc_list = []

for row in range(len(traffic_df)):
    record = traffic_df.iloc[row]
    death = record['사망자수(명)']
    population = record['인구수(천명)']
    death_perc = calc_death_percentage(death, population)
    death_perc_list.append(death_perc)
    
traffic_df['사망자_비율(천명)'] = death_perc_list

traffic_df[['국가', '사망자수(명)', '인구수(천명)', '사망자_비율(천명)']].head()
Out[13]:
국가 사망자수(명) 인구수(천명) 사망자_비율(천명)
0 호주 1205 23786.123 5.1
1 독일 3459 81197.537 4.3
2 그리스 793 10858.018 7.3
3 헝가리 644 9855.571 6.5
4 아이슬란드 16 329.100 4.9

함수 사용 - iterrows()

앞선 코드와 동일하지만, 데이터프레임 iterrows() 메쏘드를 사용할 경우 속도가 두배이상 빠르게 실행된다. record = traffic_df.iloc[row] 행의 .iloc를 제거할 수 있어 코드도 간결해진다. .itertuples()는 동일한 기능을 구현하지만, 다소 속도가 빠르다.

In [14]:
death_perc_list = []

for row in traffic_df.iterrows():

    death = record['사망자수(명)']
    population = record['인구수(천명)']
    
    death_perc = calc_death_percentage(death, population)
    death_perc_list.append(death_perc)
    
traffic_df['사망자_비율(천명)'] = death_perc_list

traffic_df[['국가', '사망자수(명)', '인구수(천명)', '사망자_비율(천명)']].head()
Out[14]:
국가 사망자수(명) 인구수(천명) 사망자_비율(천명)
0 호주 1205 23786.123 5.4
1 독일 3459 81197.537 5.4
2 그리스 793 10858.018 5.4
3 헝가리 644 9855.571 5.4
4 아이슬란드 16 329.100 5.4

.apply() 메쏘드

동일한 작업을 .apply() 메쏘드를 사용하게 되면 한결 간결하게 코드를 작성할 수 있고, 속도도 더 빠르다.

In [15]:
death_perc_apply = traffic_df.apply(lambda row: 
                                    calc_death_percentage(row['사망자수(명)'], row['인구수(천명)']),
                                    axis=1)

traffic_df['사망자_비율(천명)'] = death_perc_list

traffic_df[['국가', '사망자수(명)', '인구수(천명)', '사망자_비율(천명)']].head()
Out[15]:
국가 사망자수(명) 인구수(천명) 사망자_비율(천명)
0 호주 1205 23786.123 5.4
1 독일 3459 81197.537 5.4
2 그리스 793 10858.018 5.4
3 헝가리 644 9855.571 5.4
4 아이슬란드 16 329.100 5.4