< DataFrame의 결합 : merge >
# Database의 Table join 기능
# DataFrame 두개를 연결시켜서 새로운 DataFrame을 만들어보자
import numpy as np
import pandas as pd
data1 = {'학번' : [1,2,3,4],
'이름' : ['홍길동', '박동훈', '이순신', '강감찬'],
'학년' : [2,4,1,3]}
data2 = {'학번' : [1,2,4,5],
'학과' : ['CS', 'MATH', 'MATH', 'CS'],
'학점' : [3.4, 2.9, 4.5, 1.2]}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
# merge() 함수를 사용해서 결합 수행
# 옵션
# on : 어떤 column을 기준으로 결합할지
# how : 어떤 방식으로 merge 할 것인지
# 1. inner join
# 기준 column에 대해서 교집합
result = pd.merge(df1, df2, on='학번', how='inner')
display(result)
# 2-1. full outer join
# 기준 column에 대해서 합집합
# 데이터가 비는 곳은 NaN으로 처리
result = pd.merge(df1, df2, on='학번', how='outer')
display(result)
# 2-2. left outer join
# 왼쪽 DataFrame 항목을 모두 넣고, 해당하는 오른쪽 DataFrame을 붙임
# 해당하는 항목이 없는 경우 NaN로 처리
result = pd.merge(df1, df2, on='학번', how='left')
display(result)
# 2-3. right outer join
# 오른쪽 DataFrame 항목을 모두 넣고, 해당하는 왼쪽 DataFrame을 붙임
# 해당하는 항목이 없는 경우 NaN로 처리
result = pd.merge(df1, df2, on='학번', how='right')
display(result)
############################################################################
# 만약 column 명이 서로 다르다면?
data1 = {'학번' : [1,2,3,4],
'이름' : ['홍길동', '박동훈', '이순신', '강감찬'],
'학년' : [2,4,1,3]}
# 데이터의 학번 부분이 이름이 다르다면?
data2 = {'학생학번' : [1,2,4,5],
'학과' : ['CS', 'MATH', 'MATH', 'CS'],
'학점' : [3.4, 2.9, 4.5, 1.2]}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
# left_on, right_on 속성으로 각 테이블의 컬럼을 지칭해서 매칭 가능
result = pd.merge(df1, df2, left_on='학번', right_on='학생학번', how='inner')
display(result)
############################################################################
# column과 index를 이용한 merge
data1 = {'학번' : [1,2,3,4],
'이름' : ['홍길동', '박동훈', '이순신', '강감찬'],
'학년' : [2,4,1,3]}
# 이 경우는 학번항목이 아예 없음
data2 = {'학과' : ['CS', 'MATH', 'MATH', 'CS'],
'학점' : [3.4, 2.9, 4.5, 1.2]}
df1 = pd.DataFrame(data1)
# DataFrame을 생성할 때, 학번을 index로 사용한 경우
df2 = pd.DataFrame(data2,
index=[1,2,4,5]) # 학번을 index로 사용
# df1의 '학번' column과 df2의 index를 매칭시켜 주어야 한다.
# _on으로 column을 지칭했다면, _index로 index 사용을 설정할 수 있다.
result = pd.merge(df1, df2, left_on='학번', right_index=True, how='inner')
display(result)
# loc[]는 사용자가 지정한 인덱스가 있다면 그에 대해서 탐색
# 없으면 기본적으로 할당된 숫자 인덱스를 탐색
# 0, 1, 3 뿐이므로 error
# result.loc[2] # error
display(result.loc[3]) # OK - Series
# iloc[]는 기본적으로 할당된 숫자 인덱스를 탐색
# 0, 1, 2 이다.
display(result.iloc[2]) # OK - Series
# result.iloc[3] # error
############################################################################
# index와 index를 이용한 merge
import numpy as np
import pandas as pd
data1 = {'이름' : ['홍길동', '박동훈', '이순신', '강감찬'],
'학년' : [2,4,1,3]}
data2 = {'학과' : ['CS', 'MATH', 'MATH', 'CS'],
'학점' : [3.4, 2.9, 4.5, 1.2]}
df1 = pd.DataFrame(data1, index=[1,2,3,4])
df2 = pd.DataFrame(data2, index=[1,2,4,5])
result = pd.merge(df1, df2, left_index=True, right_index=True, how='inner')
display(result)
< Series/DataFrame 연결 : concatenation >
# Series의 1차원, 2차원 연결
import numpy as np
import pandas as pd
s1 = pd.Series([0, 1], index=['a','c'])
s2 = pd.Series([4,3,2], index=['b','c','e'])
s3 = pd.Series([5,6], index=['f','g'])
# Series는 1차원 vector
# concat() 함수로 연결작업 수행
# 연결할 Series/DataFrame들을 리스트형태로 전달하고, 축방향을 지정
# 1. 행방향으로 연결하는 방법
print(pd.concat([s1, s2, s3], axis=0) # Series를 1차원으로 연결 -> Series
# a 0
# c 1
# b 4
# c 3
# e 2
# f 5
# g 6
# dtype: int64
# 2. 열방향으로 연결하는 방법
# index를 조심해야 한다.
# column 이름은 붙이는 순서에 따라 0, 1, 2... 로 default로 매겨진다.
# concat() 함수에 sort 옵션을 주어 index 기준으로 정렬할 것인지 결정
# 붙였을 때 해당하는 값이 없는 부분은 NaN으로 설정
display(pd.concat([s1,s3,s2], axis=1, sort=True) # -> DataFrame
# 0 1 2
# a 0.0 NaN NaN
# b NaN NaN 4.0
# c 1.0 NaN 3.0
# e NaN NaN 2.0
# f NaN 5.0 NaN
# g NaN 6.0 NaN
######################################################################
# DataFrame의 연결
import numpy as np
import pandas as pd
df1 = pd.DataFrame(np.arange(6).reshape(3,2),
index=['a','c','b'],
columns=['one','two'])
df2 = pd.DataFrame(5 + np.arange(4).reshape(2,2),
index=['a','b'],
columns=['three','four'])
display(df1)
display(df2)
# 열방향으로 데이터추가 (같은 index 끼리는 같은 index 사용 - 별도로 만들지 않음)
result = pd.concat([df1, df2], axis=1, sort=True)
display(result)
# one two three four
# a 0 1 5.0 6.0
# b 4 5 7.0 8.0
# c 2 3 NaN NaN
# sort = False : column 정렬
# sort = True : row 정렬
# ignore_index : True - 설정된 인덱스를 무시하고 숫자 인덱스로 설정
# False - 설정된 인덱스 사용
result = pd.concat([df1, df2], axis=0, ignore_index=True, sort=False)
# one two three four
# 0 0.0 1.0 NaN NaN
# 1 2.0 3.0 NaN NaN
# 2 4.0 5.0 NaN NaN
# 3 NaN NaN 5.0 6.0
# 4 NaN NaN 7.0 8.0
< 결측치 처리 >
일반적으로 NaN으로 표현되는 결측치가 데이터 있을 수 있다.
결측치를 처리하는 방법은 두 가지가 있다.
- 결측치가 들어가 있는 데이터를 무조건 삭제
- 결측치를 다른 값으로 대체해서 사용
데이터가 얼마되지 않으면 삭제하는게 좋지만, 일반적으로는 결측치를 다른 값으로 대체해서 사용해야 한다.
< 결측치 처리 예제 >
import numpy as np
import pandas as pd
np.random.seed(0)
# 0 이상 10 미만의 정수형 난수를 균등분포로 추출해서 6 x 4 DataFrame 생성
df = pd.DataFrame(np.random.randint(0,10,(6,4)))
# index : 날짜를 이용. 2020-01-01 부터 1 일씩 증가
df.index = pd.date_range('20200101', '20200106')
# column 또한 설정
df.columns = ['A', 'B', 'C', 'D']
# NaN 값을 포함하는 새로운 column 'E'를 추가
df['E'] = [7, np.nan, 4, np.nan, 2, np.nan]
# 1. 결측치 제거 - dropna() 함수 사용
# (how = 'any') : NaN 이 하나라도 행에 포함되어 있으면 행 삭제
# (how = 'all') : 행의 모든 열이 NaN 인 경우 행을 삭제
new_df = df.dropna(how='any', inplace=False)
display(new_df)
# 2. 결측치를 다른 값으로 대체 - fillna() 함수 사용
# (value = 변경할 값) 형태로 전달
new_df = df.fillna(value=0)
display(new_df)
# isnull() : 값이 NaN인 값들에 대해서 True인 boolean mask 생성
my_mask = df['E'].isnull() # boolean mask
display(my_mask)
# 이 boolean mask를 활용해서 boolean indexing 가능
# 값이 NaN인 항목을 추출할 수 있다.
display(df.loc[my_mask,:])
< 중복되는 행 처리 : duplicate >
import numpy as np
import pandas as pd
# ['one', 'one', 'one', 'two', 'two', 'two', 'two' ]
df = pd.DataFrame({'k1' : ['one'] * 3 + ['two'] * 4,
'k2' : [1,1,2,3,3,4,4]})
# duplicated() : 중복행에 대한 boolean mask 추출
print(df.duplicated())
# 0 False
# 1 True
# 2 False
# 3 False
# 4 True
# 5 False
# 6 True
# dtype: bool
# drop_duplicates() : 중복행을 찾아서 지우고, 결과 DataFrame을 return
display(df.drop_duplicates())
# k1 k2
# 0 one 1
# 2 one 2
# 3 two 3
# 5 two 4
###############################################################
df = pd.DataFrame({'k1' : ['one'] * 3 + ['two'] * 4,
'k2' : [1,1,2,3,3,4,4],
'k3' : np.arange(7)})
# 인자로 column list를 전달해서 해당 column 쌍에 대한 중복확인 가능
display(df.drop_duplicates(['k1', 'k2']))
# k1 k2 k3
# 0 one 1 0
# 2 one 2 2
# 3 two 3 3
# 5 two 4 5
< 값의 교체 : replace >
import numpy as np
import pandas as pd
np.random.seed(100)
df = pd.DataFrame(np.random.randint(0,10,(6,4)),
columns=['A','B','C','D'])
df['E'] = [7, np.nan, 4, np.nan, 2, np.nan]
display(df)
result = df.replace(8, -100) # 값이 8인 것을 -100으로 전부 치환
display(result)
# A B C D E
# 0 -100 -100 3 7 7.0
# 1 7 0 4 2 NaN
# 2 5 2 2 2 4.0
# 3 1 0 -100 4 NaN
# 4 0 9 6 2 2.0
# 5 4 1 5 3 NaN
< Grouping >
import numpy as np
import pandas as pd
my_dict = { '학과' : ['컴퓨터','체육교육과','컴퓨터','체육교육과','컴퓨터'],
'학년' : [1, 2, 3, 2, 3],
'이름' : ['홍길동', '김연아', '강감찬', '이순신', '신사임당'],
'학점' : [1.5, 4.4, 3.7, 4.5, 3.8]}
df = pd.DataFrame(my_dict)
# 1. Series에 대한 Grouping
# 학점 값을 추출해서 학과를 기준으로 Grouping
# ex) 컴퓨터 : [1.5, 3.7, 3.8]
# 체육교육과 : [4.4, 4.5]
dept = df['학점'].groupby(df['학과'])
# display() 함수로 출력할 수 없다!!
display(deft)
# 결과 : <pandas.core.groupby.generic.SeriesGroupBy object at 0x0000014CCC9EDB08>
# group 내의 데이터를 확인하고 싶은경우 get_group() 함수 사용
print(dept.get_group('컴퓨터')) # Series
# 0 1.5
# 2 3.7
# 4 3.8
# Name: 학점, dtype: float64
# 각 group의 요소수를 확인하고 싶은 경우 size() 함수 사용
print(dept.size()) # Series 형태로 return
# 학과
# 체육교육과 2
# 컴퓨터 3
# Name: 학점, dtype: int64
# mean() : 각 그룹별 평균값을 계산하여 Series로 return
print(dept.mean())
# 학과
# 체육교육과 4.45
# 컴퓨터 3.00
# Name: 학점, dtype: float64
# 2단계 grouping - (학과, 학년)을 기준으로 grouping
# Series와 DataFrame은 index와 column에 multi index 개념을 지원함
dept_year = df['학점'].groupby([df['학년'], df['학과']])
print(dept_year.mean())
# 학년 학과
# 1 컴퓨터 1.50
# 2 체육교육과 4.45
# 3 컴퓨터 3.75
# Name: 학점, dtype: float64
# unstack() : 최하위 index를 column으로 설정 => 차원을 늘림
display(dept_year.mean().unstack())
# 학과 체육교육과 컴퓨터
# 학년
# 1 NaN 1.50
# 2 4.45 NaN
# 3 NaN 3.75
# 2. DataFrame에 대한 Grouping
df_group_dept = df.groupby(df['학과'])
display(df_group_dept.get_group('컴퓨터')) # DataFrame
# 학과 학년 이름 학점
# 0 컴퓨터 1 홍길동 1.5
# 2 컴퓨터 3 강감찬 3.7
# 4 컴퓨터 3 신사임당 3.8
# 학과별 각 column의 평균
display(df_group_dept.mean())
# 학년 학점
# 학과
# 체육교육과 2.000000 4.45
# 컴퓨터 2.333333 3.00
df_dept_year = df['학점'].groupby([df['학과'], df['학년']]) # DataFrame
# 학점
# 학과 학년
# 체육교육과 2 4.45
# 컴퓨터 1 1.50
# 3 3.75
display(df_dept_year.mean().unstack())
# 학점
# 학년 1 2 3
# 학과
# 체육교육과 NaN 4.45 NaN
# 컴퓨터 1.5 NaN 3.75
# 간단한 연습
# 1. 학과별 평균 학점은?
print(df['학점'].groupby(df['학과']).mean()) # Series
# 학과
# 체육교육과 4.45
# 컴퓨터 3.00
# Name: 학점, dtype: float64
# 2. 학과별 몇명이 존재하는가?
print(df.groupby(df['학과']).size()) # Series
# 학과
# 체육교육과 2
# 컴퓨터 3
# dtype: int64
< Group에 대한 for loop >
import numpy as np
import pandas as pd
my_dict = { '학과' : ['컴퓨터','체육교육과','컴퓨터','체육교육과','컴퓨터'],
'학년' : [1, 2, 3, 2, 3],
'이름' : ['홍길동', '김연아', '강감찬', '이순신', '신사임당'],
'학점' : [1.5, 4.4, 3.7, 4.5, 3.8]}
df = pd.DataFrame(my_dict)
# 학과를 기준으로 Grouping한 후 for loop를 이용해서 반복처리 해보자
# 각 학과에 대해 ('학과명', '해당 학과의 DataFrame')의 tuple 형태로 가져옴
for (dept, group) in df.groupby(df['학과']):
print(dept)
display(group)
# 두 가지 이상의 data를 가지고 Grouping 했다면, tuple 형태로 가져옴
# DataFrame에 대해서 Grouping 할 때는 list 형태로 column 명만 전달해주어도 된다.
# 물론 [df['학과'],df['학년']] 형태로 전달해도 정상 동작함
for ((dept, year), group) in df.groupby(['학과','학년']):
print(dept)
print(year)
display(group)
'Python > Data Analysis' 카테고리의 다른 글
Data Analysis / ML / Basic Concept(2) (0) | 2020.09.22 |
---|---|
Data Analysis / ML / Basic Concept(1) (0) | 2020.09.22 |
Data Analysis / pandas / DataFrame(3) (0) | 2020.09.11 |
Data Analysis / pandas / DataFrame(2) (0) | 2020.09.11 |
Data Analysis / Pandas / DataFrame(1) (0) | 2020.09.09 |