Skip to content

Latest commit

 

History

History
999 lines (806 loc) · 40.6 KB

File metadata and controls

999 lines (806 loc) · 40.6 KB

엑셀 파일 다루기 1: 데이터 다루기

그러면 본격적으로 엑셀로 하던 작업을 pandas를 사용하여 수행해봅시다.

(여기서 사용한 일부 데이터는, 통계청 산하 통계교육원의 이러닝 실습 자료를 사용하였습니다.)

엑셀로 자주 하던 작업들을 떠올려보면, 대략 아래와 같은 것들이 있습니다. 하나씩 살펴보도록 합시다.

  • Sorting
  • 컬럼별로 unique value 보기
  • 컬럼별 value frequency 보기
  • Pivot table로 보기
  • Vlookup (join)
  • 그래프 그리기
  • 새 컬럼에 recoding하기
  • 특정 컬럼에 특정 값을 가지는 row들만 보기

함수 목록

이번 실습에서 사용할 함수/메소드들을 미리 살펴보겠습니다.

Pandas의 일반 함수들 (전체 목록)

read_excel(path, sheet_name=0)
엑셀 파일을 읽어들인다.
pivot_table(data_frame)
피벗 테이블을 만든다.
merge(data_frame_1, data_frame_2)
두 데이터 프레임을 병합한다.

DataFrame의 메소드들 (전체 목록)

DataFrame(series_list, index=index_list)
DataFrame 생성 함수.
sort_values(column_name, ascending=True)
특정 컬럼(또는 컬럼들)을 기준으로 정렬한다.
describe()
데이터의 요약 정보를 가져온다.
apply(func)
DataFrame의 모든 셀에 함수를 적용하여 새 DataFrame을 반환한다.
corr()
변수들간의 상관관계 테이블을 구한다.
to_excel(path)
엑셀 파일로 저장한다.

Series의 메소드들 (전체 목록)

describe()
데이터의 요약 정보를 가져온다.
value_counts(normalize=False)
값의 빈도 분포를 가져온다.

엑셀 파일 읽어들이기

우선, 데이터로 사용할 엑셀 파일을 jupyter notebook으로 읽어들입니다.

import pandas as pd

df = pd.read_excel('https://github.com/toracle/python-basic-lecture/raw/master/assets/%EC%97%91%EC%85%80%EA%B3%BC%EC%A0%95%EC%8B%A4%EC%8A%B5%EC%83%9D.xlsx', sheet_name='Sheet1')
df

번호나이성별신장(cm)몸무게(kg)즐기는 음식
01301183821
12282160623
23271178772
34231172702
45251168723
56271179771
67261169711
78291171753
89342158602
910311183773
1011262162591
1112261173702
1213351173683
1314241176663
1415292170702
1516331177722
1617382159551
1718261166693
1819261169662
1920282159602

특정 컬럼으로 정렬하기

키가 큰 순서대로 정렬을 해볼까요?

df.sort_values('신장(cm)')
번호나이성별신장(cm)몸무게(kg)즐기는 음식
89342158602
1617382159551
1920282159602
12282160623
1011262162591
1718261166693
45251168723
67261169711
1819261169662
1415292170702
78291171753
34231172702
1112261173702
1213351173683
1314241176663
1516331177722
23271178772
56271179771
01301183821
910311183773

이런, 키가 작은 순으로 정렬이 되었네요.

df.sort_values('신장(cm)', ascending=False)
번호나이성별신장(cm)몸무게(kg)즐기는 음식
01301183821
910311183773
56271179771
23271178772
1516331177722
1314241176663
1112261173702
1213351173683
34231172702
78291171753
1415292170702
67261169711
1819261169662
45251168723
1718261166693
1011262162591
12282160623
1617382159551
1920282159602
89342158602

이제 키가 큰 순서대로 정렬이 되었습니다.

여러 컬럼도 정렬이 될까요? 같은 키 내에서는 몸무게 순으로 정렬을 해봅시다.

df.sort_values(['신장(cm)', '몸무게(kg)'], ascending=False)
번호나이성별신장(cm)몸무게(kg)즐기는 음식
01301183821
910311183773
56271179771
23271178772
1516331177722
1314241176663
1112261173702
1213351173683
34231172702
78291171753
1415292170702
67261169711
1819261169662
45251168723
1718261166693
1011262162591
12282160623
1920282159602
1617382159551
89342158602

여러 컬럼을 정렬할 때, 컬럼별로 순서를 다르게 할 수 있을까요? 키는 내림차순, 몸무게는 오름차순으로요.

df.sort_values(['신장(cm)', '몸무게(kg)'], ascending=[False, True])
번호나이성별신장(cm)몸무게(kg)즐기는 음식
910311183773
01301183821
56271179771
23271178772
1516331177722
1314241176663
1213351173683
1112261173702
34231172702
78291171753
1415292170702
1819261169662
67261169711
45251168723
1718261166693
1011262162591
12282160623
1617382159551
1920282159602
89342158602

이렇게 하면 되는군요.

기초 통계

가장 간단히, 수강생들의 키에 대한 최소, 최대, 평균과 표준편차 등을 알아봅시다.

df['신장(cm)'].describe()
count     20.00000
mean     170.25000
std        7.81951
min      158.00000
25%      165.00000
50%      170.50000
75%      176.25000
max      183.00000
Name: 신장(cm), dtype: float64

빈도 분석

이번에는, 데이터별로 빈도가 어떻게 되는지 살펴봅시다.

우선 성별 비율이 어떻게 되는지 살펴봅니다. 성별 각각의 빈도는 아래와 같이 살펴볼 수 있습니다.

df['성별'].value_counts()
1    14
2     6
Name: 성별, dtype: int64

빈도 대신 비율을 살펴보려면 아래와 같이 normalize 옵션을 추가합니다. 최대 1의 값을 가지는 소숫점이 나오고, 100을 곱하면 퍼센트로 나타낼 수 있습니다.

df['성별'].value_counts(normalize=True)
1    0.7
2    0.3
Name: 성별, dtype: float64
df['성별'].value_counts(normalize=True) * 100
1    70.0
2    30.0
Name: 성별, dtype: float64

성별에 대한 빈도와 비율을 함께 표시해봅시다.

freq = df['성별'].value_counts()
ratio = df['성별'].value_counts(normalize=True) * 100
df_freq = pd.DataFrame([freq, ratio], index=['빈도', '비율(%)'])
df_freq
12
빈도146
비율(%)7030

빈도를 나타내는 series 를 하나, 비율을 나타내는 series 를 하나 생성하고, DataFrame 을 사용하여 두 요소를 하나의 표로 묶어줍니다.

피벗 테이블

엑셀에서 편리하게 사용하는 기능 중 하나가 피벗 테이블입니다.

assets/excel-pivot.png

pandas로는 피벗 테이블을 어떻게 만들까요? 우선, 피벗 테이블을 사용해서도 앞에서 구했던 빈도를 구할 수 있습니다.

pd.pivot_table(df, index='성별', aggfunc='size')
성별
  1    14
  2     6
  dtype: int64

아래와 같이 성별에 따른 값의 차이를 피벗 테이블로 살펴봅시다.

df_pv_1 = pd.pivot_table(df, index='성별')
df_pv_1
성별나이몸무게(kg)번호신장(cm)즐기는 음식
127.714372.28579.71429174.0712.21429
230.56112.3333161.3331.83333

소숫점 자릿수를 일치시켜봅시다.

df_pv_1 = pd.pivot_table(df, index='성별')
df_pv_1.apply(lambda x: round(x, 2))
성별나이몸무게(kg)번호신장(cm)즐기는 음식
127.7172.299.71174.072.21
230.5061.0012.33161.331.83

apply 함수는 DataFrame 의 각 셀에 인자로 오는 함수를 적용한 결과로 이루어진 새 DataFrame 을 반환합니다.

이번에는 성별과 함께 좋아하는 음식별로 연령이나 몸무게 등에 차이가 있는지 살펴봅시다.

df_pv_2 = pd.pivot_table(df, index=['성별', '즐기는 음식'])
df_pv_2.apply(lambda x: round(x, 2))
나이몸무게(kg)번호신장(cm)
(1, 1)27.6776.674.67177.00
(1, 2)27.0071.0010.80173.80
(1, 3)28.3371.1711.33172.83
(2, 1)32.0057.0014.00160.50
(2, 2)30.3363.3314.67162.33
(2, 3)28.0062.002.00160.00
df_pv_3 = pd.pivot_table(df, index='성별', columns='즐기는 음식', values='몸무게(kg)')
df_pv_3.apply(lambda x: round(x, 1))
성별123
176.771.071.2
257.063.362.0

Vlookup

엑셀에서 피벗과 더불어 중급 기술로 여겨지는 것 중 하나가 vlookup 이죠. 데이터베이스 용어로 하면 JOIN 입니다.

pandas에서는 merge 함수를 사용할 수 있습니다.

우선 vlookup 의 대상이 될 테이블을 만듭니다.

import pandas as pd
df_food = pd.DataFrame(['한식', '일식', '중식'], index=[1, 2, 3], columns=['음식이름'])
df_food
음식이름
1한식
2일식
3중식
df_merged = pd.merge(df, df_food, how='left', left_on='즐기는 음식', right_index=True)
df_merged
번호나이성별신장(cm)몸무게(kg)즐기는 음식음식이름
01301183821한식
12282160623중식
23271178772일식
34231172702일식
45251168723중식
56271179771한식
67261169711한식
78291171753중식
89342158602일식
910311183773중식
1011262162591한식
1112261173702일식
1213351173683중식
1314241176663중식
1415292170702일식
1516331177722일식
1617382159551한식
1718261166693중식
1819261169662일식
1920282159602일식

왼쪽 테이블, 즉 df 의 ‘즐기는 음식’ 컬럼과, 오른쪽 테이블, 즉 df_foot 테이블의 인덱스 컬럼을 비교하여, 같은 row인 경우 두 row를 결합합니다.

Correlation

이번에는 컬럼간의 상관관계를 구해볼까요?

df.corr()
번호나이성별신장(cm)몸무게(kg)즐기는 음식
번호10.2284790.208141-0.31344-0.4950210.0790217
나이0.22847910.334697-0.147492-0.23975-0.104107
성별0.2081410.3346971-0.765901-0.751269-0.227276
신장(cm)-0.31344-0.147492-0.76590110.8829390.038434
몸무게(kg)-0.495021-0.23975-0.7512690.88293910.0680821
즐기는 음식0.0790217-0.104107-0.2272760.0384340.06808211

사실 지금까지 데이터를 다루는 과정에서 불편한 점이 있었는데, 바로 카테고리 값들을 숫자값처럼 인식하는 것이었습니다. 피벗 테이블에서 성별이나 번호에 대한 평균값을 표시해주는 것처럼요.

이 시점에서 각 컬럼에 올바른 데이터 타입을 지정해봅시다.

df_dtype = pd.read_excel('assets/엑셀과정실습생.xlsx', sheet_name='Sheet1',
                         index_col=0,
                         dtype={'번호': 'object', '성별': 'object', '즐기는 음식': 'object'})
df_dtype

번호, 성별, 즐기는 음식은 문자열 타입이라고 지정해주었습니다.

번호나이성별신장(cm)몸무게(kg)즐기는 음식
1301183821
2282160623
3271178772
4231172702
5251168723
6271179771
7261169711
8291171753
9342158602
10311183773
11262162591
12261173702
13351173683
14241176663
15292170702
16331177722
17382159551
18261166693
19261169662
20282159602

DataFrame 에 대해서 상관관계를 구해볼까요?

df_dtype.corr()
나이신장(cm)몸무게(kg)
나이1-0.147492-0.23975
신장(cm)-0.14749210.882939
몸무게(kg)-0.239750.8829391

이제 연속된 값을 나타내는 컬럼에 대해서만 상관관계를 구한 것을 볼 수 있습니다.

앞서 살펴보았던 피벗테이블도 어떻게 달라졌는지 살펴볼까요?

pd.pivot_table(df_dtype, index='성별')
성별나이몸무게(kg)신장(cm)
127.714372.2857174.071
230.500061.0000161.333

역시 연속값을 가지는 컬럼들만 대상으로 피벗테이블을 만듭니다.

여러 층위의 index에 대해서도 그러합니다.

pd.pivot_table(df_dtype, index=['성별', '즐기는 음식'])
나이몸무게(kg)신장(cm)
(1, 1)27.666776.6667177.000
(1, 2)27.000071.0000173.800
(1, 3)28.333371.1667172.833
(2, 1)32.000057.0000160.500
(2, 2)30.333363.3333162.333
(2, 3)28.000062.0000160.000

Row 필터링

값을 기준으로 필터링하고 싶은 경우에는 어떻게 할까요? 너무 특이한 값을 가지는 아웃라이어를 제거한다던지 말이죠.

몸무게가 80 이상인 사람은 제외하도록 해봅시다.

우선, 어떤 사람이 몸무게가 80 이상인지 확인합니다.

df_dtype['몸무게(kg)'] < 80
번호
  1     False
  2      True
  3      True
  4      True
  5      True
  6      True
  7      True
  8      True
  9      True
  10     True
  11     True
  12     True
  13     True
  14     True
  15     True
  16     True
  17     True
  18     True
  19     True
  20     True
  Name: 몸무게(kg), dtype: bool

이 boolean 배열을 df_dtype 에게 전달합니다.

df_dtype[df_dtype['몸무게(kg)'] <= 80]
번호나이성별신장(cm)몸무게(kg)즐기는 음식
2282160623
3271178772
4231172702
5251168723
6271179771
7261169711
8291171753
9342158602
10311183773
11262162591
12261173702
13351173683
14241176663
15292170702
16331177722
17382159551
18261166693
19261169662
20282159602

몸무게가 80kg 이상인 항목은 제외된 것을 볼 수 있습니다. (여기서, df_dtype 자체는 변하지 않고, 항목이 제외된 새 DataFrame이 반환된다는 것에 주의하세요)

컬럼 추가, 변형

기존의 컬럼들을 사용해서 새로운 컬럼을 추가하고 싶으면 어떻게 할까요? 키와 몸무게를 사용해서 BMI 지수를 한번 구해봅시다.

우선, BMI 지수는 키를 cm 대신 m로 표시해야 합니다.

df_dtype['신장(cm)'] / 100
번호
  1     1.83
  2     1.60
  3     1.78
  4     1.72
  5     1.68
  6     1.79
  7     1.69
  8     1.71
  9     1.58
  10    1.83
  11    1.62
  12    1.73
  13    1.73
  14    1.76
  15    1.70
  16    1.77
  17    1.59
  18    1.66
  19    1.69
  20    1.59
  Name: 신장(cm), dtype: float64

이 컬럼을 신장(m) 이라는 컬럼으로 추가해봅시다.

df_dtype['신장(m)'] = df_dtype['신장(cm)'] / 100
df_dtype
번호나이성별신장(cm)몸무게(kg)즐기는 음식신장(m)
13011838211.83
22821606231.6
32711787721.78
42311727021.72
52511687231.68
62711797711.79
72611697111.69
82911717531.71
93421586021.58
103111837731.83
112621625911.62
122611737021.73
133511736831.73
142411766631.76
152921707021.7
163311777221.77
173821595511.59
182611666931.66
192611696621.69
202821596021.59

이제 같은 방식으로 BMI 지수를 구해볼까요?

df_dtype['BMI'] = round(df_dtype['몸무게(kg)'] / pow(df_dtype['신장(m)'], 2), 1)
df_dtype
번호나이성별신장(cm)몸무게(kg)즐기는 음식신장(m)BMI
13011838211.8324.5
22821606231.624.2
32711787721.7824.3
42311727021.7223.7
52511687231.6825.5
62711797711.7924
72611697111.6924.9
82911717531.7125.6
93421586021.5824
103111837731.8323
112621625911.6222.5
122611737021.7323.4
133511736831.7322.7
142411766631.7621.3
152921707021.724.2
163311777221.7723
173821595511.5921.8
182611666931.6625
192611696621.6923.1
202821596021.5923.7

종종 연속된 값을 일정 범주로 구분해야 하는 경우가 있습니다. 이를테면 13세를 10대, 23세를 20대 이렇게 말이죠. 이런건 어떻게 처리할까요?

우선, 연속된 연령값을 연령대로 구분해주는 함수를 만들어봅시다.

def categorize_age(age):
    return '{}~{}세'.format(age // 5 * 5, (age // 5 + 1) * 5-1)

print(categorize_age(35))
print(categorize_age(31))
print(categorize_age(28))
35~39세
30~34세
25~29세

apply 함수는, 원소 하나 하나에 주어진 함수를 대입해서 결과를 반환해줍니다.

df_dtype['나이'].apply(categorize_age)
번호
  1     30~34세
  2     25~29세
  3     25~29세
  4     20~24세
  5     25~29세
  6     25~29세
  7     25~29세
  8     25~29세
  9     30~34세
  10    30~34세
  11    25~29세
  12    25~29세
  13    35~39세
  14    20~24세
  15    25~29세
  16    30~34세
  17    35~39세
  18    25~29세
  19    25~29세
  20    25~29세
  Name: 나이, dtype: object

이 결과를 컬럼으로 추가합니다.

df_dtype['연령대'] = df_dtype['나이'].apply(categorize_age)
df_dtype
번호나이성별신장(cm)몸무게(kg)즐기는 음식신장(m)BMI연령대
13011838211.8324.530~34세
22821606231.624.225~29세
32711787721.7824.325~29세
42311727021.7223.720~24세
52511687231.6825.525~29세
62711797711.792425~29세
72611697111.6924.925~29세
82911717531.7125.625~29세
93421586021.582430~34세
103111837731.832330~34세
112621625911.6222.525~29세
122611737021.7323.425~29세
133511736831.7322.735~39세
142411766631.7621.320~24세
152921707021.724.225~29세
163311777221.772330~34세
173821595511.5921.835~39세
182611666931.662525~29세
192611696621.6923.125~29세
202821596021.5923.725~29세
df_cat_pv = pd.pivot_table(df_dtype, index='연령대', columns='즐기는 음식', values='몸무게(kg)')
df_cat_pv.apply(lambda x: round(x, 1))
연령대123
20~24세nan70.066.0
25~29세69.068.669.5
30~34세82.066.077.0
35~39세55.0nan68.0

더 이상 필요 없는 컬럼을 지울 때는, dict 에서 key 를 삭제할 때처럼, a_dict.pop('나이'), 혹은 del a_dict['나이'] 를 사용할 수 있습니다.

df_dtype[['신장(m)', '몸무게(kg)', 'BMI']]

엑셀로 저장하기

지금까지 가공한 DataFrame을 다시 엑셀 파일로 저장해봅시다.

df_dtype.to_excel('outputs/실습생_컬럼추가.xlsx')

연습문제

앞에서 구했던 BMI 지수를 가지고, 비만도를 나타내는 컬럼을 DataFrame에 추가해보세요.

비만도구간
고도 비만35 이상
중등도 비만 (2단계 비만)30 ~ 35
경도 비만 (1단계 비만)25 ~ 30
과체중23 - 24.9
정상18.5 - 22.9
저체중18.5 미만

비만 정도와 좋아하는 음식 사이에 어떤 연관성이 있는지 한번 살펴보세요.

번호나이성별신장(cm)몸무게(kg)즐기는 음식신장(m)BMI연령대비만도
13011838211.8324.530~34세과체중
22821606231.624.225~29세과체중
32711787721.7824.325~29세과체중
42311727021.7223.720~24세과체중
52511687231.6825.525~29세경도 비만
62711797711.792425~29세과체중
72611697111.6924.925~29세과체중
82911717531.7125.625~29세경도 비만
93421586021.582430~34세과체중
103111837731.832330~34세과체중
112621625911.6222.525~29세정상
122611737021.7323.425~29세과체중
133511736831.7322.735~39세정상
142411766631.7621.320~24세정상
152921707021.724.225~29세과체중
163311777221.772330~34세과체중
173821595511.5921.835~39세정상
182611666931.662525~29세경도 비만
192611696621.6923.125~29세과체중
202821596021.5923.725~29세과체중
비만도123
경도 비만nannan3
과체중382
정상2nan2