데이터를 분석하려면 데이터를 자유자재로 다룰 수 있어야 함. 이 장에서는 데이터를 추줄하거나 여러 데이터를 합치는 등 데이터를 가공하는 방법을 익힘.
06-1 데이터 전처리 - 원하는 형태로 데이터 가공하기
주어진 데이터를 그대로 사용하기보다 원하는 형태로 변형해 분석할 때가 많음. 분석에 적합하게 데이터를 가공하는 작업을 데이터 전처리(data preprocessing)라고 함. 일부를 추출하거나 종류별로 나누거나, 여러 데이터를 합치는 등 데이터를 자유롭게 가공할 수 있어야 목적에 맞게 분석할 수 있음.
Tip) 데이터 가공(data manipulation), 데이터 핸들링(data handling), 데이터 랭글링(data wrangling), 데이터 먼징(data munging) 등의 용어도 데이터 전처리와 비슷한 의미로 사용됨.
pandas는 데이터 전처리 작업에 가장 많이 사용되는 패키지임. pandas를 이용해 데이터를 가동하는 방법을 알아보겠음.
함수 | 기능 |
query() | 행 추출 |
df[] | 열(변수) 추출 |
sort_values() | 정렬 |
groupby() | 집단별 나누기 |
assign() | 변수 추가 |
agg() | 통계치 구하기 |
merge() | 데이터 합치기(열) |
concat() | 데이터 합치기(행) |
06-2 조건에 맞는 데이터만 추출하기
데이터를 분석할 때 전체 데이터를 사용하기도 하지만 관심 있는 일부를 추출해 분석하기도 함. pandas의 df.query()를 이용하면 원하는 데이터를 추출할 수 있음.
import pandas as pd
exam = pd.read_csv('exam.csv')
exam
출력 결과의 nclass 변수 열을 보면 데이터가 5개 반의 학생들로 구성된다는 것을 알 수 있음.
Tip) exam.csv 파일을 불러올 때 오류가 발생한다면 exam.csv 파일을 워킹 디렉터리에 복사했는지 확인해보기
pandas의 query()를 이용해 1반 학생들의 데이터만 추출하겠음. 다음 코드를 실행하면 nclass가 1인 행만 출력됨.
# exam에서 nclass가 1인 경우만 추출
exam.query('nclass == 1')
이 코드는 exam을 출력하되 nclass가 1인 행만 추출하라는 조건이 지정되어 있음. query()에 입력한 'nclass == 1'은 ‘nclass 변수의 값이 1인 행’을 의미함. 이처럼 따옴표 안에 조건을 입력하면 조건에 해당하는 행만 추출함.
query()에 ‘같다’를 의미하는 조건을 입력할 때 등호(=)를 두 번 반복해 쓰는 것을 꼭 기억해야 함. 함수의 파라미터를 지정할 때는 data = df와 같이 등호를 한 번 쓰고, ‘같다’를 의미할 때는 nclass == 1와 같이 등호를 두 번 씀. 같은 방식으로 2반 학생만 추출해 보겠음.
# 2반인 경우만 추출
exam.query('nclass == 2')
이번에는 변수가 특정 값이 ‘아닌 경우’에 해당하는 데이터만 추출해 보겠음. 등호 앞에 느낌표를 붙여 !=을 입력하면 ‘같지 않다’를 의미하는 기호가 됨.
# 1반이 아닌 경우
exam.query('nclass != 1')
# 3반이 아닌 경우
exam.query('nclass != 3')
[Do it! 실습] 초과, 미만, 이상, 이하 조건 걸기
부등호를 이용하면 특정 값 초과나 미만, 또는 특정 값 이상이나 이하인 데이터만 추출할 수 있음.
# 수학 점수가 50점을 초과한 경우
exam.query('math > 50')
# 수학 점수가 50점 미만인 경우
exam.query('math < 50')
# 영어 점수가 50점 이상인 경우
exam.query('english >= 50')
# 영어 점수가 80점 이하인 경우
exam.query('english <= 80')
[Do it! 실습] 여러 조건을 충족하는 행 추출하기
‘그리고(and)’를 의미하는 & 기호를 사용해 조건을 나열하면 여러 조건을 동시에 충족하는 행을 추출할 수 있음.
# 1반이면서 수학 점수가 50점 이상인 경우
exam.query('nclass == 1 & math >= 50')
# 2반이면서 영어 점수가 80점 이상인 경우
exam.query('nclass == 2 & english >= 80')
[Do it! 실습] 여러 조건 중 하나 이상 충족하는 행 추출하기
‘또는(or)’을 의미하는 | 기호를 이용하면 여러 조건 중 하나라도 충족하는 데이터를 추출할 수 있음.
# 수학 점수가 90점 이상이거나 영어 점수가 90점 이상인 경우
exam.query('math >= 90 | english >= 90')
# 영어 점수가 90점 미만이거나 과학 점수가 50점 미만인 경우
exam.query('english < 90 | science < 50')
[Do it! 실습] 목록에 해당하는 행 추출하기
변수의 값이 지정한 목록에 해당될 경우만 추출해야 할 때가 있음. 예를 들어 1, 3, 5반에 속한 학생의 데이터만 추출하는 상황을 생각할 수 있음. 이럴 때는 | 기호를 이용해 여러 조건을 나열하면 됨.
# 1, 3, 5반에 해당하면 추출
exam.query('nclass == 1 | nclass == 3 | nclass == 5')
in과 []를 이용해 조건 목록을 입력하면 코드를 좀 더 간결하게 작성할 수 있음. in은 변수의 값이 []에 입력한 목록에 해당되는지 확인하는 기능을 함.
# 1, 3, 5반에 해당하면 추출
exam.query('nclass in [1, 3, 5]')
[Do it! 실습] 추출한 행으로 데이터 만들기
추출한 행으로 새로운 데이터를 만들려면 =을 이용해 추출한 행을 새 변수에 할당하면 됨. 데이터를 조건별로 나눠 활용할 때 이런 형태로 코드를 작성함. 1반과 2반을 추출해 각각 새 데이터로 만든 다음 두 반의 수학 점수 평균을 구해 보겠음.
# nclass가 1인 행을 추출해 nclass1에 할당
nclass1 = exam.query('nclass == 1')
# nclass가 2인 행을 추출해 nclass2에 할당
nclass2 = exam.query('nclass == 2')
nclass1['math'].mean() # 1반 수학 점수 평균 구하기
# output: 46.25
nclass2['math'].mean() # 2반 수학 점수 평균 구하기
# output: 61.25
[Do it! 실습] 문자 변수를 이용해 조건에 맞는 행 추출하기
문자 변수를 이용해 조건에 맞는 행을 추출할 때는 query()에 전체 조건을 감싸는 따옴표와 추출할 문자를 감싸는 따옴표를 서로 다른 모양으로 입력해야 함. 전체 조건을 query('')처럼 작은따옴표로 감쌌다면, 추출할 문자는 var == " "처럼 큰따옴표로 감싸야 함.
df = pd.DataFrame({'sex' : ['F', 'M', 'F', 'M'],
'country': ['Korea', 'China', 'Japan', 'USA']})
df
# 전체 조건에 작은따옴표, 추출할 문자에 큰따옴표 사용
df.query('sex == "F" & country == "Korea"')
반대로 전체 조건을 query(" ")처럼 큰따옴표로 감쌌다면, 추출할 문자는 sex == ' '처럼 작은따옴표로 감싸야 함.
# 전체 조건에 큰따옴표, 추출할 문자에 작은따옴표 사용
df.query("sex == 'M' & country == 'China'")
전체 조건과 추출할 문자를 입력할 때 같은 모양 따옴표를 사용하면 다음과 같이 에러 메시지가 출력됨.
# 전체 조건과 추출할 문자에 모두 작은 따옴표 사용
df.query('sex == 'F' & country == 'Korea'')
{알아 두면 좋아요!} 외부 변수를 이용해 추출하기
데이터 프레임에 들어 있는 변수가 아니라 별도의 변수를 이용해 행을 추출하려면 변수명 앞에 @를 붙여서 조건을 입력하면 됨.
var = 3
exam.query('nclass == @var')
{알아 두면 좋아요!} 파이썬에서 사용하는 기호
조건에 맞는 데이터를 추출할 때 여러 가지 기호를 사용했음. 조건을 지정할 때 사용하는 기호를 ‘논리 연산자(logical operators)’, 계산할 때 사용하는 기호를 ‘산술 연산자(arithmetic operators)’라고 함. 실습을 하면서 상황에 맞게 활용하다 보면 자연스럽게 여러 기호의 기능을 익히게 됨.
논리 연산자 | 기능 | 산술 연산자 | 기능 |
< | 작다 | + | 더하기 |
<= | 작거나 같다 | - | 빼기 |
> | 크다 | * | 곱하기 |
>= | 크거나 같다 | ** | 제곱 |
== | 같다 | / | 나누기 |
!= | 같지 않다 | // | 나눗셈의 몫 |
| | 또는 | % | 나눗셈의 나머지 |
& | 그리고 | - | - |
in | 매칭 확인 | - | - |
{알아 두면 좋아요!} 데이터 프레임 출력 제한 설정하기
노트북은 데이터 프레임이 60행을 넘기면 위아래 5행씩 10행만 출력하고 중간을 생략함. 열은 20열을 넘기면 좌우 10열씩 20열만 출력하고 중간을 생략함. 다음 코드를 실행하면 제한 없이 행과 열을 모두 출력할 수 있음.
pd.set_option('display.max_rows', None) # 모든 행 출력되도록 설정
pd.set_option('display.max_columns', None) # 모든 열 출력되도록 설정
Tip) 너무 큰 데이터 프레임을 출력하면 JupyerLab이 멈출 수 있음
설정한 내용은 JupyterLab을 새로 실행하거나 커널(Kernel)을 새로 실행하면 원래대로 돌아감. JupyterLab이나 커널을 새로 실행하지 않고 설정을 되돌리려면 다음 코드를 실행하면 됨.
pd.reset_option('display.max_rows') # 행 출력 제한 되돌리기
pd.reset_option('display.max_columns') # 열 출력 제한 되돌리기
pd.reset_option('all') # 모든 설정 되돌리기
Tip) 커널은 JupyterLab에서 파이썬을 사용할 수 있도록 JupyterLab과 파이썬을 연결하는 역할을 함. 커널을 새로 실행하면 노트북을 처음 열었을 때처럼 초기화됨. 커널을 실행하려면 JupyterLab 상단 메뉴의 [Kernel → Restart Kernal...]을 클릭하면 됨.
{혼자서 해보기} mpg 데이터를 이용해 분석 문제를 해결해 보세요.
Q1. 자동차 배기량에 따라 고속도로 연비가 다른지 알아보려고 함. displ(배기량)이 4 이하인 자동차와 5 이상은 자동차중 어떤 자동차의 hwy(고속도로 연비) 평균이 더 높은지 알아보기.
import pandas
mpg = pd.read_csv('mpg.csv')
mpg_copy = mpg.copy()
# 배기량이 4 이하인 자동차의 고속도로 연비 평균
displ4 = mpg_copy.query('displ <= 4')
displ4['hwy'].mean()
# output: 25.96319018404908
# 배기량이 5 이상인 자동차의 고속도로 연비 평균
displ5 = mpg_copy.query('displ >= 5')
displ5['hwy'].mean()
# output: 18.07894736842105
결론) 배기량이 4 이하인 자동차의 고속도로 연비 평균(약 25.96마일)이 배기량이 5 이하인 자동차의 고속도로 연비 평균(약 18.08마일)보다 좋다.
Q2. 자동차 제조 회사에 따라 도시 연비가 어떻게 다른지 알아보려고 함. ‘audi’와 ‘toyota’ 중 어니 manufacturer(자동차 제조 회사)의 cty(도시 연비) 평균이 더 높은지 알아보기.
# 'audi'의 도시연비 평균
audi = mpg_copy.query('manufacturer in ["audi"]')
audi['cty'].mean()
# output: 17.61111111111111
# 'toyota'의 도시연비 평균
toyota = mpg_copy.query('manufacturer in ["toyota"]')
toyota['cty'].mean()
# output: 18.529411764705884
결론) 도시연비 평균은 'audi'(17.61)보다 'toyota'(18.53) 제조 회사의 자동차가 더 좋다.
Q3. ‘chevrolet’, ‘ford’, ‘honda’ 자동차의 고속도로 연비 평균을 알아보려고 함. 세 회사의 데이터를 추출한 다음 hwy 전체 평균을 구해보기.
# 'chevrolet', 'ford', 'honda'의 고속도로 연비 평균
company3 = mpg_copy.query("manufacturer in ['chevrolet', 'ford', 'honda']")
company3['hwy'].mean()
# output: 22.50943396226415
06-3 필요한 변수만 추출하기
데이터 분석할 때 데이터에 들어있는 모든 변수를 사용하기보다는 관심 있는 변수만 추출해 활용할 때가 많음. [] 기호를 이용해 변수를 추출하는 방법을 알아보겠음
[Do it! 실습] 변수 추출하기
변수를 추출하려면 데이터 프레임명 뒤에 []를 입력한 다음 추출할 변수명을 따옴표로 감싸서 입력하면 됨. 다음 코드는 exam에서 math 변수만 추출하는 기능을 함.
exam['math'] # math 추출
같은 방식으로 english 변수를 추출해 보겠음.
exam['english'] # english 추출
여러 변수 추출하기
여러 변수를 함께 추출하려면 [] 안에 다시 []를 넣어 변수명을 나열하면 됨.
exam[['nclass', 'math', 'english']] # nclass, math english 추출
{알아 두면 좋아요} 변수가 1개 일 때 데이터 프레임 유지하기
변수를 1개만 추출하면 출결 결과가 시리즈(series) 자료 구조로 바뀜. 데이터 프레임 자료 구조를 유지하려면 exam[['math']]처럼 변수명을 []로 한 번 더 감싸면 됨.
tip) 시리즈 자료 구조는 17-6에서 다룸.
# 시리즈로 추출
exam['math']
# 데이터 프레임으로 추출
exam[['math']]
[Do it! 실습] 변수 제거하기
특정 변수를 추출할 때도 있지만 반대로 특정 변수만 제외하고 나머지 모든 변수를 추출할 때가 있음. 이럴 때는 df.drop()을 이용하면 됨. 제거할 변수명을 columns에 입력하면 입력한 변수만 제외하고 나머지 모든 변수를 추출함.
exam.drop(columns = 'math') # math 제거
여러 변수를 제거하려면 []에 제거할 변수명을 나열하면 됨.
exam.drop(columns = ['math', 'english']) # math, english 제거
[Do it! 실습] pandas 함수 조합하기
pandas의 함수들은 조합하여 함께 사용할 수 있다는 장점이 있음. 함수를 조합하면 코드 길이가 줄어들어 이해하기 쉽고, 함수의 출력 결과를 변수에 할당하고 불러오는 작업을 반복하지 않아도 됨.
query()와 [] 조합하기
query()와 []를 조합해서 1반 학생의 영어 점수를 추출해 보겠음. nclass가 1인 행을 추출하면 query('nclass == 1')에 english 변수를 추출하는 ['english']를 연결하면 됨.
# nclass가 1인 행만 추출한 다음 english 추출
exam.query('nclass == 1')['english']
이번에는 수학 점수가 50점 이상인 학생의 id와 math 변수를 추출해 보겠음.
# math가 50 이상인 행만 추출한 다음 id, math 추출
exam.query('math >= 50')[['id', 'math']]
일부만 추출하기
출력 결과 중 일부만 확인하고 싶을 때 head()를 연결해 사용하면 편리함. head()만 입력하면 5행까지 출력하고, 괄호 안에 숫자를 입력하면 입력한 숫자만큼 행을 출력함.
# math가 50 이상인 행만 추출한 다음 id, math 앞부분 5행까지 추출
exam.query('math >= 50')[['id', 'math']].head(5)
# math가 50 이상인 행만 추출한 다음 id, math 앞부분 10행까지 추출
exam.query('math >= 50')[['id', 'math']].head(10)
[Do it! 실습] 가독성 있게 코드 줄 바꾸기
코드를 한 줄로 길게 작성하기보다 여러 불로 나누어 작성하면 가독성 있는 코드를 만들 수 있음. 명령어가 연결되는 부분에서 줄을 바꾸면 한 행이 하나의 명령어로 구성되므로 코드의 의미가 한눈에 파악됨. 다음 절차로 코드의 줄을 바꾸면 됨.
- 명령어가 끝난 부분 뒤에 백슬래시(\)를 입력하고 [Enter]를 눌러 줄을 바꿈.
- [Spacebar]을 [tab]을 이용해 간격을 띄우고 다음 명령어를 입력함.
exam.query('math >= 50')\
[['id', 'math']] \
.head(5)
\ 뒤에는 아무것도 입력하면 안됨. \ 뒤에 주석이나 띄어쓰기 등 문자를 입력하면 오류가 발생하니 주의하기.
{혼자서 해보기} mpg 데이터를 이용해 분석 문제를 해결해 보세요.
Q1. mpg 데이터는 11개 변수로 구성됨. 이 중 일부만 추출해 분석에 활용하고자 함. mpg 데이터에서 category(자동차 종류), cty(도시 연비) 변수를 추출해 새로운 데이터를 만들고, 새로 만든 데이터의 일부를 출력해 두 변수로만 구성되어 있는지 확인하기.
# 데이터 불러오기
import pandas as pd
mpg = pd.read_csv('mpg.csv')
# 데이터에서 자동차 종류, 도시 연비 변수를 추출해 일부 데이터 확인
mpg = mpg[['category', 'cty']]
mpg.head()

Q2. 자동차 종류에 따라 도시 연비가 어떻게 다른지 알아보려고 함. 앞에서 추출한 데이터를 이용해 category(자동차 종류)가 ‘suv’인 자동차와 ‘compact’인 자동차 중 어떤 자동차의 cty(도시 연비) 평균이 더 높은지 알아보기.
# 'suv'인 자동차의 도시 연비 평균
suv_cty_mean = mpg.query('category == "suv"') \
[['cty']].mean()
suv_cty_mean

# 'compact'인 자동차의 도시 연비 평균
compact_cty_mean = mpg.query('category == "compact"')\
[['cty']].mean()
compact_cty_mean

# 'suv'와 'compact'인 자동차의 도시 연비 평균 백분율
suv_compact_difference = suv_cty_mean / compact_cty_mean
suv_compact_difference

결론) ‘suv’인 자동차와 ‘compact’인 자동차 중 cty(도시 연비) 평균은 ‘suv’인 자동차보다 ‘compact’인 자동차 중 cty(도시 연비) 평균이 67% 더 좋음
06-4 순서대로 정렬하기
데이터를 순서대로 정렬하면값이 매우 크거나 매우 작아서 두드러지는 데이터를 알아낼 수 있음. df.sort_values()를 이용해 데이터를 정렬하는 방법을 알아보겠음.
[Do it! 실습] 오름차순으로 정렬하기
데이터를 정렬하려면 df.sort_values()에 정렬 기준으로 삼을 변수를 입력하면 됨. exam 데이터는 학생의 번호를 나타내는 id 변수 순으로 정렬되어 있음. exam.sort_values()에 math를 입력해 수학 점수가 낮은 사람부터 높은 사람 순으로 오름차순 정렬해 출력해 보겠음.
exam.sort_values('math') # math 오름차순 정렬
[Do it! 실습] 내림차순으로 정렬하기
높은 값부터 낮은 값으로 내림차순 정렬하려면 ascending = False를 입력하면 됨. 다음 코드를 실행하면 math가 높은 사람부터 낮은 사람 순으로 내림차순 정렬해 출력됨.
exam.sort_values('math', ascending = False) # math 내림차순 정렬
[Do it! 실습] 여러 정렬 기준 적용하기
정렬 기준으로 삼을 변수를 여러 개 지정하려면 []를 이용해 변수명을 나열하면 됨. 다음 코드를 실행하면 먼저 반을 기준으로 오름차순 정렬한 다음 각 반에서 수학 점수를 기준으로 오름차순 정렬해 출력함.
# nclass, math 오름차순 정렬
exam.sort_values(['nclass', 'math'])
변수별로 정렬 순서를 다르게 지정하려면 ascending에 []를 이용해 값을 입력하면 됨. True를 입력하면 오름차순, False를 입력하면 내림차순으로 정렬함. 다음 코드를 실행하면 먼저 반을 기준으로 오름차순 정렬한 다음, 각 반에서 수학 점수 기준으로 내림차순 정렬해 출력함.
# nclass 오름차순, math 내림차순 정렬
exam.sort_values(['nclass', 'math'], ascending = [True, False])
{혼자서 해보기} mpg 데이터를 이용해 분석 문제를 해결해 보세요.
Q1. 'audi'에서 생산한 자동차 중에 어떤 자동차 모델의 hwy(고속도로 연비)가 높은지 알아보려고 함. 'audi'에서 생산한 자동차 중 hwy가 1~5위에 해당하는 자동차의 데이터를 출력하기.
# 데이터 불러오기
import pandas as pd
mpg = pd.read_csv('mpg.csv')
# 'audi'에서 생산한 자동차의 고속도로 연비를 기준으로 내림차순 정렬하기
hwy = mpg.sort_values(['hwy'], ascending = [False])
audi = hwy.query('manufacturer == "audi"')
# 1~5위에 해당하는 자동차의 데이터를 출력하기
audi.head()

06-5 파생변수 추가하기
데이터에 들어 있는 변수만 이용해 분석할 수도 있지만, 변수를 조합하거나 함수를 이용해 새 변수를 만들어 분석할 수도 있음. df.assign()을 이용하면 데이터에 파생 변수를 만들어 추가할 수 있음.
[Do it! 실습] 파생변수 추가하기
exam에 세 과목의 점수를 합한 총점 변수를 추가해 출력해 보겠음. exam.assign()에 새 변수명 = 변수를 만드는 공식을 입력하면 됨. 여기서는 total이라는 변수명을 사용했는데, 변수명은 자유롭게 정하면 됨. 새로 만들 변수명에는 따옴표를 입력하지 않으니 주의하기.
# total 변수 추가
exam.assign(total = exam['math'] + exam['english'] + exam['science'])
여러 파생변수 한 번에 추가하기
여러 파생변수를 한 번에 추가하려면 쉼표를 이용해 새 변수명과 변수를 만드는 공식을 나열하면 됨.
exam.assign(
total = exam['math'] + exam['english'] + exam['science'], # total 추가
mean = (exam['math'] + exam['english'] + exam['science']) / 3) # mean 추가
[Do it! 실습] df.assign()에 np.where() 적용하기
df.assign()에 NumPy 패키지의 where()을 적용하면 조건에 따라 다른 값을 부여한 변수를 추가할 수 있음.
import numpy as np
exam.assign(test = np.where(exam['science'] >= 60, 'pass', 'fail'))
[Do it! 실습] 추가한 변수를 pandas 함수에 바로 활용하기
변수를 추가하고 나면 이어지는 pandas 함수에 바로 활용할 수 있음. 다음 코드를 실행하면 assign()으로 추가한 total을 sort_values()의 기준으로 삼아 정렬함.
# total 변수 추가, total 기준 정렬
exam.assign(total = exam['math'] + exam['english'] + exam['science']) \
.sort_values('total')
[Do it! 실습] lambda 이용해 데이터 프레임명 줄여 쓰기
df.assign()을 사용할 때 변수명 앞에 데이터 프레임명을 반복해서 입력해야 하므로 번거로운데, lambda를 이용하면 데이터 프레임명 대신 약어를 입력해 코드를 간결하게 작성할 수 있음. 다음 코드에서 lambda x:는 데이터 프레임명 자리에 x를 입력하겠다는 의미임. 이렇게 하면 데이터 프레임명 대신 x를 입력하면 되므로 코드가 간결해짐.
# 긴 데이터 프레임명 지정
long_name = pd.read_csv('exam.csv')
# long_name 직접 입력
long_name.assign(new = long_name['math'] + long_name['english'] + long_name['science'])
# long_name 대신 x 입력
long_name.assign(new = lambda x: x['math'] + x['english'] + x['science'])
앞에서 만든 변수를 활용해 다시 변수 만들기
앞에서 만든 파생변수를 만들 때가 있음. 이때는 반드시 lambda를 이용해 데이터 프레임명을 약어로 입력해야 함.
exam.assign(total = exam['math'] + exam['english'] + exam['science'],
mean = lambda x: x['total'] / 3)
앞의 코드는 데이터 프레임명을 서로 다른 문자로 입력하여 읽기 불편함. 파생변수를 만드는 행에 lambda를 각각 입력하면 데이터 프레임명을 통일하여 가독성을 높일 수 있음.
exam.assign(total = lambda x: x['math'] + x['english'] + x['science'],
mean = lambda x: x['total'] / 3)
앞에서 만든 파생변수를 이용해 차시 파생변수를 만들 때 lambda를 이용하지 않고 데이터 프레임명을 직접 입력하면 에러 메시지가 출력되므로 주의하기.
exam.assign(total = exam['math'] + exam['english'] + exam['science'],
mean = exam['total'] / 3)
{혼자서 해보기} mpg 데이터를 이용해 분석 문제를 해결해 보세요.
mpg 데이터는 연비를 나타내는 변수가 hwy(고속도로 연비), cty(도시 연비) 두 종류로 분리되어 있음. 두 변수를 각각 활용하는 대신 하나의 합산 연비 변수를 만들어 분석하려고 함.
Q1. mpg 데이터 복사본을 만들고, cty와 hwy를 더한 ‘합산 연비 변수’를 추가하기.
# 패키지 입력
import pandas as pd
import numpy as np
# 데이터 불러오기
mpg = pd.read_csv('mpg.csv')
mpg_copy = mpg.copy()
# cty와 hwy를 더한 ‘합산 연비 변수’를 추가
mpg_copy = mpg_copy.assign(sum_gas_mileage = lambda x: x['cty'] + x['hwy'])
mpg_copy

Q2. 앞에서 만든 ‘합산 연비 변수’를 2로 나눠 ‘평균 연비 변수’를 추가하기.
# cty와 hwy를 더한 ‘평균 연비 변수’를 추가
mpg_copy = mpg_copy.assign(mean_gas_mileage = lambda x: x['sum_gas_mileage'] / 2)
mpg_copy

Q3. ‘평균 연비 변수’가 가장 높은 자동차 3종의 데이터를 출력하기.
# '평균 연비 변수'가 가장 높은 자동차 3종의 데이터를 출력하기
mpg_copy = mpg_copy.sort_values(['mean_gas_mileage'], ascending = [False])
mpg_copy.head(3)

Q4. 1~3번 문제를 해결할 수 있는 하나로 연결된 pandas 구문을 만들어 실행해 보고, 데이터는 복사본 대신 mpg 원본을 이용하기.
mpg.assign(sum_gas_mileage = lambda x: x['cty'] + x['hwy'],
mean_gas_mileage = lambda x: x['sum_gas_mileage'] / 2) \
.sort_values(['mean_gas_mileage'], ascending = [False]) \
.head(3)

06-6 집단별로 요약하기
‘집단별 평균’이나 ‘집단별 빈도’처럼 각 집단을 요약한 값을 구할 때는 df.groupby()와 df.agg()를 사용함. 이 함수들을 이용해 요약표를 만들면 집단 간에 어떤 차이가 있는지 쉽게 파악할 수 있음.
[Do it! 실습] 집단별로 요약하기
전체 요약 통계량 구하기
먼저 요약 통계량을 구하는 df.agg()를 사용하겠음. df.agg()에 mean_math = ('math', 'mean')처럼 요약값을 변수명과 =를 입력한 다음, 값을 요약하는데 사용할 변수와 함수를 괄호 안에 나열하면 됨. 다음 코드는 수학 점수 평균을 구해 mean_math에 할당하는 기능을 함.
# math 평균 구하기
exam.agg(mean_math = ('math', 'mean'))
- 요약한 값을 할당할 변수명은 assign()을 사용할 때와 마찬가지로 자유롭게 정하면 되고, 따옴표는 입력하지 않음.
- 요약하는데 사용할 변수명과 함수명은 따옴표로 감싸 문자 형태로 입력하고, 함수명 뒤에 ()를 넣지 않으니 주의하기.
만약 전체 평균을 구한다면 exam['math'].mean()처럼 간단히 mean()을 이용하면 됨. agg()는 전체를 요약한 값을 구하기보다는 groupby()에 적용해 집단별 요약값을 구할 때 사용함.
집단별 요약 통계량 구하기
df.groupby()에 변수를 지정하면 변수의 범주별로 데이터를 분리함. 여기서 agg()를 적용하면 집단별 요약 통계량을 구함. 다음 코드를 실행하면 반별 수학 점수 평균을 구함.
# 1. nclass별로 분리하기
# 2. math 평균 구하기
exam.groupby('nclass') \
.agg(mean_math = ('math', 'mean'))
{알아 두면 좋아요} 변수를 인덱스로 바꾸지 않기
앞의 코드의 출력값을 보면 집단을 나타낸 변수명 nclass가 인덱스(index)로 바뀌어 mean_math보다 밑에 표시되어 있음. 이는 groupby()의 기본값이 변수를 인덱스로 바꾸도록 설정되어 있기 때문임. df.groupby()에 as_index = False를 입력하면 변수를 인덱스로 바꾸지 않고 원래대로 유지됨.
exam.groupby('nclass', as_index = False) \
.agg(mean_math = ('math', 'mean'))
여러 통계량 한 번에 구하기
df.assign()으로 여러 변수를 한 번에 추가하는 것과 마찬가지로 df.agg로도 여러 요약 통계량을 한 번에 구할 수 있음.
exam.groupby('nclass') \
.agg(mean_math = ('math', 'mean'), # 수학 점수 평균
sum_math = ('math', 'sum'), # 수학 점수 합계
median_math = ('math', 'median'),# 수학 점수 중앙값
n = ('math', 'count')) # 빈도(학생 수)
다음은 agg()에 자주 사용하는 요약 통계량 함수임.
함수 | 통계량 |
mean() | 평균 |
std() | 표준편차 |
sum() | 합계 |
median() | 중앙값 |
min() | 최솟값 |
max() | 최댓값 |
count() | 빈도(개수) |
{알아 두면 좋아요} 모든 변수의 요약 통계량 한 번에 구하기
df.groupby()에 agg() 대신 mean()이나 sum()과 같은 요약 통계량 함수를 적용하면 데이터 프레임에 들어 있는 모든 변수의 요약 통계량을 한 번에 구할 수 있음.
exam.groupby('nclass').mean()
df.agg()로 평균, 함계 등 여러 종류의 요약 통계량을 구하는 것과 달리 이 방법으로는 한 가지 요약 통계량만 구할 수 있음
[Do it! 실습] 집단별로 다시 집단 나누기
groupby()에 여러 변수를 지정하면 집단을 나눈 다음 다시 하위 집단으로 나눌 수 있음. 예를 들어 성적 데이터를 반별로 나눈 다음 다시 성별로 나눠 각 반의 성별 평균 전수를 구할 수 있음.
mpg 데이터를 이용해 하위 집단별 평균을 구해보겠음. 다음 코드는 제조 회사별로 집단을 나눈 다음 다시 구동 방식별로 나눠 도시 연비 평균을 구함. 출력 결과를 보면 제조 회사별로 구동 방식에 따라 평균 연비가 어떻게 다른지 알 수 있음.
tip) drv의 4는 사륜구동, f는 전륜구동, r은 후륜구동을 의미
# 제조 회사 및 구동 방식별 분리 후, cty 평균 구하기
mpg.groupby(['manufacturer', 'drv']) \
.agg(mean_cty = ('cty', 'mean'))
출력 결과에서 drv 행의 개수가 제조 회사별로 다른 이유는 회사마다 생산한 drv 종류가 다르기 때문임. 다음 코드의 출력 결과를 보면 'audi'는 drv를 2종 생산한 반면 'chevrolet'는 3종 생산했음.
mpg.query('manufacturer == "audi"') \
.groupby(['drv']) \
.agg(n = ('drv', 'count'))
# chevrolet의 drv별 빈도
mpg.query('manufacturer == "chevrolet"') \
.groupby(['drv']) \
.agg(n = ('drv', 'count'))
{알아 두면 좋아요} value_counts()로 집단별 빈도 간단하게 구하기
앞에서는 집단별 빈도를 구할 때 groupby()로 데이터를 분리한 다음 agg()에 count() 함수를 입력하는 방법을 사용했음.
mpg.groupby('drv') \
.agg(n = ('drv', 'count'))
df.value_counts()를 이용하면 집단별 빈도를 더 간단히 구할 수 있음. 데이터 프레임에서 빈도를 구할 변수를 추출한 다음 value_counts()를 적용하면 됨.
mpg['drv'].value_counts()
df.value_counts()는 짧은 코드로 빈도를 구할 수 있고 자동으로 빈도 기준으로 내림차순 정렬한다는 장점이 있음. 하지만 출력 결과가 데이터 프레임이 아닌 시리즈(series) 자료 구조이므로 query()를 적용할 수 없음.
mpg['drv'].value_counts().query('n > 100')
AttributeError: 'Series' object has no attribute 'query'
df.counts()의 출력 결과에 query()를 적용하려면 먼저 데이터 프레임으로 변환해야 함.
tip) 시리즈 자료 구조는 17-6절에서 자세히 다룸.
# 1. drv 빈도 구하기
# 2. 데이터 프레임으로 바꾸기, 변수명 n으로 바꾸기
# 3. axis 이름에 drv 지정
# 4. n이 100을 초과한 경우 추출
mpg['drv'].value_counts() \
.to_frame('n') \
.rename_axis('drv') \
.query('n > 100')
[Do it! 실습] pandas 함수 조합하기
pandas 패키지는 함수를 조합할 때 진가를 발휘함. 절차가 복잡해 보이는 분석도 pandas 함수를 조합하면 코드 몇 줄로 간단히 해결할 수 있음. 지금까지 다룬 pandas 함수들을 하나의 구문으로 조합해 다음 분석 문제를 해결해 보겠음.
Q. 제조 회사별로 'suv' 자동차의 도시 및 고속도로 합산 연비 평균을 구해 내림차순으로 정렬하고, 1~5위까지 출력하기 |
코드를 작성하기 전에 어떤 절차로 어떤 함수를 사용할지 생각하면서 정리함.
절차 | 기능 | pandas 함수 |
1 | suv 추출 | query() |
2 | 합산 연비 변수 만들기 | assign() |
3 | 회사별로 분리 | groupby() |
4 | 합산 연비 평균 구하기 | agg() |
5 | 내림차순 정렬 | sort_values() |
6 | 1~5위까지 출력 | head() |
앞의 절차에 따라 함수들을 연결해 하나의 pandas 구문으로 만듬.
mpg.query('category == "suv"') \
.assign(total = (mpg['hwy'] + mpg['cty']) / 2) \
.groupby('manufacturer') \
.agg(mean_tot = ('total', 'mean')) \
.sort_values('mean_tot', ascending = False) \
.head()
{혼자서 해보기} mpg 데이터를 이용해 분석 문제를 해결해 보세요.
Q1. mpg 데이터의 category는 자동차를 특징에 따라 'suv', 'compact' 등 일곱 종류로 분류한 변수임. 어떤 차종의 도시 연비가 높은지 비교해 보려고 함. category별 cty 평균을 구해보기.
mpg.groupby('category') \
.agg(mean_cty = ('cty', 'mean'))

Q2. 앞 문제의 출력 결과를 category 값 알파벳순으로 정렬되어 있음. 어떤 차종의 도시 연비가 높은지 쉽게 알아볼 수 있도록 cty 평균이 높은 순으로 정렬해 출력하기.
mpg.groupby('category') \
.agg(mean_cty = ('cty', 'mean')) \
.sort_values('mean_cty', ascending = False)

Q3. 어떤 회사 자동차 hwy(고속도로 연비)가 가장 높은지 알아보려고 함. hwy 평균이 가장 높은 회사 세 곳을 출력하기.
mpg.groupby('manufacturer') \
.agg(mean_hwy = ('hwy', 'mean')) \
.sort_values('mean_hwy', ascending = False) \
.head(3)

Q4. 어떤 회사에서 'compact' 차종을 가장 많이 생산하는지 알아보려고 함. 회사별 'compact' 차종 수를 내림차순 정렬해 출력하기.
06-7 데이터 합치기
하나의 데이터만 가지고 분석하기도 하지만 여러 데이터를 합쳐 하나로 만든 다음 분석하기도 함. 예를 들어 중간고사 데이터와 기말고사 데이터를 합쳐 하나로 만들면 기말고사 점수가 중간고사 점수보다 올랐는지 혹은 떨어졌는지 알 수 있음.
가로로 합치기
데이터를 합치는 방법은 크게 2가지가 있음. 첫 번째는 데이터를 가로로 합치는 방법으로, 기존 데이터에 변수(열)을 추가한다고 볼 수 있음. 앞에서 든 예처럼 중간고사 데이터에 기말고사 데이터를 합친다면 가로로 합치는 방법을 사용해야 함.
세로로 합치기
두 번째는 데이터를 세로로 합치는 방법으로, 기존 데이터에 행을 추가한다고 볼 수 있음. 예를 들어 3명이 먼저 시험을 보고, 나중에 다른 학생 3명이 따로 시험을 봤을 때, 두 시험 데이터를 합친다면 세로로 합치는 방법을 사용해야 함.
[Do it! 실습] 가로로 합치기
데이터를 가로로 합치는 방법을 알아보겠음. 우선 학생 5명이 중간고사와 기말고사를 봤다고 가정하고 데이터 프레임을 2개 만들겠음.
# 중간고사 데이터 만들기
test1 = pd.DataFrame({'id' :[1, 2, 3, 4, 5],
'midterm':[60, 80, 70, 90, 85]})
# 기말고사 데이터 만들기
test2 = pd.DataFrame({'id' :[1, 2, 3, 4, 5],
'final' :[70, 83, 65, 95, 80]})
test1 # test1 출력
test2 # test2 출력
데이터를 가로로 합칠 때는 pd.merge()를 이용함.
- pd.merge()에 결합할 데이터 프레임명을 나열함.
- 오른쪽에 입력한 데이터 프레임을 왼쪽 데이터 프레임에 결합하도록 how = 'left'를 입력함
- 데이터를 합칠 때 기준으로 삼을 변수명을 on에 입력함.
다음 코드를 실행하면 학생의 번호를 나타낸 id를 기준으로 두 시험 데이터를 합쳐 하나의 데이터 프레임을 만듦.
# id 기준으로 합쳐서 total에 할당
total = pd.merge(test1, test2, how = 'left', on = 'id')
total
다른 데이터를 활용해 변수 추가하기
pd.merge()를 응용하면 특정 변수의 값을 기준으로 다른 데이터의 값을 추가할 수 있음. 예를 들어 지역 코드가 들어 있는 데이터를 분석할 경우, 어떤 지역인지 알 수 있도록 지역 이름을 추가해야 함. 이럴 때 지역 코드와 지역 이름을 담은 데이터가 있다면 지역 코드 기준으로 결합해 지역 이름을 추가할 수 있음.
각 반 학생들의 시험 점수를 담은 exam 데이터를 분석하는데, 반별 담임교사 명단을 추가로 얻었다고 가정해 보겠음. 먼저 반별 담임교사 명단 데이터를 만들겠음.
name = pd.DataFrame({'nclass' :[1, 2, 3, 4, 5],
'teacher':['kim', 'lee', 'park', 'choi', 'jung']})
name
name은 nclass와 teacher 두 변수로 구성되어 있음. nclass 변수를 기준으로 삼아 nclass의 teacher 변수를 exam에 추가하겠음. 다음 코드를 실행하면 exam에 teacher 변수가 추가됨.
exam_new = pd.merge(exam, name, how = 'left', on = 'nclass')
exam_new
[Do it! 실습] 세로로 합치기
이번에는 데이터를 세로로 합치는 방법을 알아보겠음. 학생 5명이 먼저 시험을 보고 나중에 5명이 시험을 따로 봤다고 가정하고, 데이터 프레임을 2개 만들겠음.
# 학생 1~5번 시험 데이터 만들기
group_a = pd.DataFrame({'id' :[1, 2, 3, 4, 5],
'midterm':[60, 80, 70, 90, 85]})
# 학생 6~10번 시험 데이터 만들기
group_b = pd.DataFrame({'id' :[1, 2, 3, 4, 5],
'final' :[70, 83, 65, 95, 80]})
group_a # group_a 출력
group_b # group_b 출력
데이터를 세로로 함칠 때는 pd.concat()를 이용함. pd.concat()에 결합할 데이터 프레임명을 []를 이용해 나열하면 됨. 다음 코드를 실행하면 두 시험 데이터를 합쳐 하나로 만듦.
# 데이터 합쳐서 group_all에 할당
group_all = pd.concat([group_a, group_b])
group_all
tip) 앞 코드의 출력 결과는 인덱스 0~4가 중복됨. 인덱스를 새로 부여하려면 pd.concat()에 ignore_index = True를 입력하면 됨.
데이터를 세로로 합칠 때는 두 데이터의 변수명이 같아야 함. 앞에서 사용한 group_a와 group_b는 변수명이 id와 test로 같기 때문에 세로로 합칠 수 있었음. 만약 변수명이 다르면 pd.rename()을 이용해 똑같이 맞춘 다음 합치면 됨.
{알아 두면 좋아요} pandas 더 알아보기
치트 시트
pandas에는 앞에서 다룬 함수 외에도 데이터를 가공할 때 사용하는 다양한 함수가 있음. 패키지 사용법을 요약한 메뉴얼인 치트 시트(cheat sheet)를 참고하면, 자주 사용하는 함수들을 한눈에 볼 수 있음.
- Pandas Cheasheet: bit.ly/easypy_pandas
pandas 공식 문서 검색하기
pandas 사용 방법을 자세히 알고 싶다면 pandas 공식 문서의 검색창에 함수명을 입력해보면 됨.
- pandas documentation: pandas.pydata.org/docs
{혼자서 해보기} mpg 데이터를 이용해 분석 문제를 해결해 보세요.
mpg 데이터의 fl 변수는 자돈차에 사용하는 연료(fuel)을 의미함. 다음은 자동차 연료별 가격을 나타낸 표임.
f1 | 연료 종류 | 가격(갤런당 USD) |
c | CNG | 2.35 |
d | diesel | 2.38 |
e | ethanol E85 | 2.11 |
p | premium | 2.76 |
r | regular | 2.22 |
우선 이 정보를 이용해 연료와 가격으로 구성된 데이터 프레임을 만들어 보기.
fuel = pd.DataFrame({'fl' : ['c', 'd', 'e', 'p', 'r'],
'price_fl' : [2.35, 2.38, 2.11, 2.76, 2.22]})
fuel
정리하기
## 1. 조건에 맞는 데이터만 추출하기
exam.query('english <= 80')
# 여러 조건 동시 충족
exam.query('nclass == 1 & math >= 50')
# 여러 조건 중 하나 이상 충족
exam.query('math >= 90 | english >= 90')
exam.query('nclass in [1, 3, 5]')
## 2. 필요한 변수 만들기
exam['math'] # 한 변수 추출
exam[['nclass', 'math', 'english']] # 여러 변수 추출
exam.drop(column = 'math') # 변수 제거
exam.drop(column = ['math', 'english']) # 여러 변수 제거
## 3. pandas 명령어 조합하기
exam.query('math >= 50')[['id', 'math']].head()
## 4. 순서대로 정렬하기
exam.sort_values('math') # 오름차순 정렬
exam.sort_values('math', ascending = False) # 내림차순 정렬
# 여러 변수 기준 정렬
exam.sort_values('nclass', 'math', ascending = [True, False])
## 5. 파생변수 추가하기
exam.assign(total = exam['math'] + exam['english'] + exam['science'])
# 여러 파생변수 한 번에 추가하기
exam.assign(total = exam['math'] + exam['english'] + exam['science'],
mean = exam['math'] + exam['english'] + exam['science']) / 3)
# assign()에 np.where() 적용하기
exam.assign(test = np.where(exam['science'] >= 60, 'pass', 'fail'))
# 추가한 변수를 pandas 코드에 바로 활용하기
exam.assign(total = exam['math'] + exam['english'] + exam['science']) \
.sort_values('total') \
.head()
## 6. 집단별로 요약하기
exam.groupby('nclass') \
.agg(mean_math = ('math', 'mean'))
# 각 집단별로 다시 집단 나누기
mpg.groupby(['manufacturer', 'drv']) \
.agg(mean_cty = ('cty', 'mean'))
## 7. 데이터 합치기
pd.merge(test1, test2, how = 'left', on = 'id')# 가로로 합치기
pd.concat([group_a, group_b]) # 세로로 합치기
분석 도전
미국 동북중부 437개 지역의 인구통계 정보를 담고 있는 midwest,csv를 사용해 데이터 분석 문제를 해결해 보세요.
문제1) popadults는 해당 지역의 성인 인구, poptotal은 전체 인구를 나타냄. midwest 데이터에 '전체 인구 대비 미성년 인구 백분율' 변수를 추가해보자.
import pandas as pd
midwest = pd.read_csv('midwest.csv')
midwest = midwest.assign(under_age_per = lambda x: 100 - ((x['popadults'] / x['poptotal']) * 100))
문제2) 미성년 인구 백분율이 가장 높은 상위 5개 county(지역)의 미성년 인구 백분율을 출력해보자.
midwest = midwest[['county', 'under_age_per']].sort_values('under_age_per', ascending = False)
midwest.head()

문제3) 분류표의 기준에 따라 미성년 비율 등급 변수를 추가하고, 각 등급에 몇 개의 지역이 있는지 알아보자.
분류 | 기준 |
large | 40% 이상 |
middle | 30~40% 이상 |
small | 30% 이상 |
import numpy as np
midwest = midwest.assign(level = (np.where(midwest['under_age_per'] >= 40, 'large',
np.where(midwest['under_age_per'] > 30, 'middle', 'small'))))
midwest['level'].value_counts()

문제4) popasian은 해당 지역의 아시아인 인구를 나타냄. '전체 인구 대비 아시아인 인구 백분율' 변수를 추가하고 하위 10개 지역인 state(주), county(지역), 아시아인 인구 백분율을 출력해보자.
tot_asian_per = midwest.assign(tot_asian_per = (midwest['popasian'] / midwest['poptotal']) * 100) \
.sort_values('tot_asian_per')
tot_asian_per[['state', 'county', 'tot_asian_per']].head(10)

- 6장 END -
'데이터 분석(Data Analysis) > 쉽게 배우는 파이썬 데이터 분석' 카테고리의 다른 글
[둘째마당_본격실습! 데이터 갖고 놀기] 08 그래프 만들기 (0) | 2023.12.25 |
---|---|
[둘째마당_본격실습! 데이터 갖고 놀기] 07 데이터 정제 - 빠진 데이터, 이상한 데이터 제거하기 (0) | 2023.12.23 |
[둘째마당_본격실습! 데이터 갖고 놀기] 05 데이터 분석 기초! - 데이터파악하기, 다루기 쉽게 수정하기 (0) | 2023.12.17 |
[둘째마당_본격실습! 데이터 갖고 놀기] 04 데이터 프레임의 세계로 (0) | 2023.12.16 |
[첫째마당_파이썬이랑 친해지기] 03 데이터 분석에 필요한 연장 챙기기 (0) | 2023.12.15 |