목차



1. 스택&언스택(stack)

pandas 내에 있는 메소드 stackunstack은 R에서 reshape2 패키지의 함수 melt, dcast와 동일한 기능을 가지고 있습니다. 두 함수를 다루기 전에 Wide data와 long data를 알아야 하는데 이 둘에 대한 간단한 설명은 아래 접기 버튼에 수록해놓았습니다. 관련 글마다 이 둘을 지칭하는 용어가 약간씩 다르지만 다루는 개념에는 차이가 없고 구글링을 할 때 지장 있을 정도의 변화가 없습니다. 내용 숙지 후 바로 메소드 사용으로 넘어가봅시다.

Wide data & long data 1. Wide data - 교차 테이블 - 행별, 컬럼별 그룹연산 수행가능하고 조인 연산이 불가능합니다.
지점   1  2  3  4
 A    10 11 12 13
 B    14 15 16 17
 C    18 19 20 21
 D    22 23 24 25
2. Long data - DB, 모델 분석에서 선호하는 방식 - 새로운 데이터(관찰대상)에 대한 추가가 비교적 용이 - group by, JOIN 연산 가능
지점 분기 판매량
 A    1    10
 A    2    11
 ...
D    4    25 


1. 언스택(unstack)

unstack은 long datawide data 로 바꿔줍니다. 즉, 멀티 인덱스를 멀티 컬럼으로 바꾸는 메소드입니다. 옵션에서의 계층(level) 기본값이 -1이기 때문에 레벨을 지정해주지 않으면 가장 하위 레벨의 index를 컬럼화합니다.

# 멀티 인덱스를 가진 시리즈 생성
s1 = Series([1,2,3,4],index=[['A','A','B','B'],['a','b','a','b']])
s1
Out:
A  a    1
   b    2
B  a    3
   b    4

s1.unstack()
Out:
  a b
A 1 2
B 3 4

s1.unstack(level=0)
Out:
  A B
a 1 3
b 2 4

2. 스택(stack)

stack은 wide datalong data로 바꿔줍니다. 즉, 멀티 컬럼을 멀티 인덱스로 바꾸는 메소디입니다. unstack처럼 옵션 level의 기본값이 -1이기 때문에 레벨을 지정하지 않으면 가장 하위 레벨의 index를 컬럼화합니다.

# 위 코드에서 사용한 s1 사용
s2 = s1.unstack(level=0)

s2.stack()
Out:
a A 1
  B 3
b A 2
  B 4

s2.stack(level=0)
Out:
a A 1
  B 3
b A 2
  B 4


# (+) dropna 옵션 : NaN값을 생략하는 옵션, 기본값은 True

s2.loc['b','B'] = NA

s2.stack()
Out:
a A 1.0
  B 3.0
b A 2.0

s2.stack(dropna=False)
Out:
a A 1.0
  B 3.0
b A 2.0
  B NaN  



2. rank

rank 메소드는 변수 내에 있는 value들을 크기에 따라 순위를 부여하는 메소드입니다. 간단한 문법으로 구성되어 있고 순위 부여시 value의 크기가 같아 순위가 같아지는 값들에 대한 순위 부여 방법(method)을 바꾸는 옵션만 업무에 따라 바꿔주시면 됩니다.

s1.rank(
axis=0,           # 축 번호, 순위 부여 방향 선택
method={'average' # 동순위에 대한 평균 값 출력
        'min',    # 동순위에 대한 작은 값 출력
        'max',    # 동순위에 대한 큰 값 출력(서로 같은 순위 부여)
        'first'}, # 동순위중 가장 먼저 배치된 값에 높은 순위(서로 다른 순위 부여)
ascending=True)   # 오름차순 방향


크게 어려운 내용이 없으니 가벼운 예제들을 보고 습득합시다.

  • 시리즈에 rank 메소드 적용
s1 = Series([10,2,5,1,6])
s2 = Series([10,2,5,1,1,6])

s1.rank()
Out:
0 5.0
1 2.0
2 3.0
3 1.0
4 4.0

s2.rank()
Out:
0 6.0
1 3.0
2 4.0
3 1.5
4 1.5
5 5.0

s2.rank(method = 'min')
Out:
0 6.0
1 3.0
2 4.0
3 1.0
4 1.0
5 5.0

s2.rank(method = 'max')
Out:
0 6.0
1 3.0
2 4.0
3 2.0
4 2.0
5 5.0

s2.rank(method = 'first')
Out:
0 6.0
1 3.0
2 4.0
3 1.0
4 2.0
5 5.0


  • 데이터 프레임에 rank 메소드 적용 (순위 축 방향)
df1 = DataFrame({'col1':[4,1,3,5], 'col2':[1,2,3,4]})

df1.rank(axis = 0)
Out:
   col1 col2
0  3.0   1.0
1  1.0   2.0
2  2.0   3.0
3  4.0   4.0

df1.rank(axis = 1)
Out:
   col1 col2
0  2.0   1.0
1  1.0   2.0
2  1.5   1.5
3  2.0   1.0

3. 병합(merge)

merge 메소드는 SQL에서의 JOIN과 비슷한 기능을 하는 메소드입니다. JOIN이 특정 속성에 대해서 두 테이블을 붙이는 것처럼 merge는 두 데이터 프레임을 특정 속성에 대해서 붙여줍니다. 비슷한 기능이라는 것은 merge 메소드는 JOIN에 비해 기능이 약하기 때문입니다. JOIN과 달리 merge는 NON EQUI JOIN이 불가능합니다. 또, JOIN이 여러 개의 테이블을 묶을 수 있는 데 반면 merge는 두 개의 데이터 프레임만 붙일 수 있습니다. 그래도 다행히 INNER JOIN, OUTER JOIN은 모두 가능합니다.

  JOIN merge
EQUI JOIN 가능 가능
NON EQUI JOIN 가능 불가능
붙이는 자료 수 제한 없음 2개
INNER JOIN 가능 가능
OUTER JOIN 가능 가능
같은 인덱스에 대해 결합 미지원(변수 추가 필요) 가능


간단히 문법을 살펴본 후 예제를 보도록 하겠습니다. JOIN에 대한 개념이 부족하신 분은 코드를 보아도 이해하기 어려우니 SQL에서 JOIN 다루는 글을 봐주시면 도움이 될 것입니다.

문법

pd.merge(
left,   # 첫 번째 데이터 프레임
right,  # 두 번째 데이터 프레임

how= {'left',  # LEFT (OUTER) JOIN
     'right',  # RIGHT (OUTER) JOIN
     'outer',  # FULL OUTER JOIN
     'inner'}, # INNER JOIN (기본 값)

on=None,       # 조인할 컬럼 이름
(
left_on=None,  # 첫 번째 데이터 프레임의 컬럼 이름
right_on=None, # 두 번째 데이터 프레임의 컬럼 이름
)

left_index=False, # 왼쪽 데이터 (첫번째) index 값으로 조인 여부
right_index=False # 오른쪽 데이터(두번째) index 값으로 조인 여부
)

예제

df1 = DataFrame({'col1':['a','b','c'],
                 'col2':[1,2,3]})
df2 = DataFrame({'col11':['a','b','d'],
                 'col22':[10,20,30]})

# INNER JOIN
pd.merge(df1,df2, left_on = 'col1', right_on = 'col11')
Out:
  col1  col2 col11  col22
0    a     1     a     10
1    b     2     b     20

# LEFT JOIN
pd.merge(df1,df2, how= 'left', left_on = 'col1', right_on = 'col11')
Out[9]: 
  col1  col2 col11  col22
0    a     1     a   10.0
1    b     2     b   20.0
2    c     3   NaN    NaN

# RIGHT JOIN
pd.merge(df1,df2, how= 'right', left_on = 'col1', right_on = 'col11')
Out: 
  col1  col2 col11  col22
0    a   1.0     a     10
1    b   2.0     b     20
2  NaN   NaN     d     30

# FULL OUTER JOIN
pd.merge(df1,df2, how= 'outer', left_on = 'col1', right_on = 'col11')
Out: 
  col1  col2 col11  col22
0    a   1.0     a   10.0
1    b   2.0     b   20.0
2    c   3.0   NaN    NaN
3  NaN   NaN     d   30.0

# INDEX로 JOIN
pd.merge(df1,df2, left_index=True, right_index=True)
Out: 
  col1  col2 col11  col22
0    a     1     a     10
1    b     2     b     20
2    c     3     d     30

4. 피벗 테이블(pivot)

피벗 테이블 기능은 엑셀(MS Excel)에서도 제공하는 강력한 기능입니다. 데이터에 있는 정보들을 보기 좋게 요약해주는 기능인데 값들에 대하여 그룹함수도 사용할 수 있습니다. 피벗 테이블 메소드는 pivotpivot_table이 있는데 요약기능 여부의 차이가 있습니다. 그래서 교육받을 당시에는 요약기능이 있는 pivot_table 메소드를 많이 썼던 기억이 나네요.

# pivot 메소드
data.pivot(index =  , # 인덱스에 넣을 컬럼명
           columns= , # 컬럼에 넣을 컬럼명
           values = ) # 값으로 정할 컬럼명

# pivot_table 메소드
data.pivot_table(index  = ,  # 인덱스에 넣을 컬럼명
                 columns= ,  # 컬럼에 넣을 컬럼명
                 values = ,  # 값으로 정할 컬럼명
                 agg_func = '함수') # 요약함수(sum, mean, count 등)

이제 예제를 통해 피벗 테이블 기능을 사용해봅시다. 파일은 저번 글에 첨부한 subway2.csv로 하겠습니다. 전처리는 되어 있다는 가정하에 진행하겠습니다.

예제) 인덱스에 전체를, 컬럼에 구분(승차/하차)을, 그리고 값에는 9시 시간대로 설정하여 피벗 테이블을 만드시오.

data2.pivot(index = '전체', columns = '구분', values = '9시')

이런 식으로 하면 됩니다.

5.str 모듈

pandas 내부의 str 모듈은 문자열 처리 메소드를 가지고 있는데 벡터연산이 가능하여 원소마다 적용이 가능합니다.

1) split : 구분자를 기준으로 각 원소의 문자열을 나눕니다.

s1.str.split(';')
Out[74]: 
0    [a, b, c]
1    [A, B, C]
dtype: object
1-1) 특정 위치 색인

str.split으로 나눈 후 특정 위치의 원소를 색인하는 기능으로 두 가지 메소드가 있습니다.(기능은 동일)

s1.str.split(';').str[0]
s1.str.split(';').str.get(0)
Out:
0    a
1    A
dtype: object

2) replace : 각 원소 내의 문자중 첫 번째 인자를 두 번째 인자로 바꿉니다.

s1.str.replace(';','|')
Out[75]: 
0    a|b|c
1    A|B|C
dtype: object

​ 3) 대소치환 : 대/소문자 치환 메소드입니다.

s1.str.upper()
Out: 
0    A;B;C
1    A;B;C
dtype: object

s1.str.lower()
Out: 
0    a;b;c
1    a;b;c
dtype: object

s1.str.title() # SQL의 INITCAP과 동일, 첫 글자만 대문자
Out: 
0    A;B;C
1    A;B;C
dtype: object

4) 패턴여부 : 입력한 문자가 존재하는지 여부

# a로 시작하는지 여부
s1.str.startswith('a')
Out: 
0     True
1    False
dtype: bool

# c로 끝나는지 여부
s1.str.endswith('c')
Out: 
0     True
1    False
dtype: bool

# a를 포함하는지 여부
s1.str.contains('a')
Out: 
0     True
1    False
dtype: bool

5) 개수 및 길이

# a가 몇 개 있는가
s1.str.count('a')
Out: 
0    1
1    0
dtype: int64

# 원소들의 글자의 갯수
s1.str.len()
Out: 
0    5
1    5
dtype: int64

6) 제거함수 : 괄호 안 인자와 일치하는 문자 제거. 제거 방식은 replace 방식이 아니라 SQL의 TRIM과 같습니다. 기본 인자는 공백' '입니다.

# 좌우로 공백이 포함된 원소가 들어있는 새 시리즈 생성
s2 =Series([' ab ',' AB '])
s2.str.len()
Out: 
0    4
1    4
dtype: int64
# 양 옆 공백으로 인해 글자 길이가 4

# 양쪽 공백 제거 후 길이
s2.str.strip().str.len()
Out: 
0    2
1    2
dtype: int64

# 오른쪽 공백 제거 후 길이
s2.str.rstrip().str.len()
Out: 
0    3
1    3
dtype: int64

# 왼쪽 공백 제거 후 길이
s2.str.lstrip('a')
Out: 
0    3
1    3
dtype: int64

6. 중복 처리 메소드(duplicate)

1) duplicated

중복이면 True, 중복값이 없으면 False를 출력하는 메소드입니다.

# 시리즈 생성
s1 = Series([1,1,2,3,4])

# duplicated 실행
s1.duplicated()
Out: 
0    False
1     True
2    False
3    False
4    False
dtype: bool

# 메소드를 이용하여 중복인 값만 출력
s1[s1.duplicated()]
Out: 
1    1
dtype: int64

2) drop_duplicates

이전 key들중에서 중복된 값이 있는 경우 제외시키고 출력합니다. 데이터프레임에 사용시 옵션에 중복 제거를 실행할 컬럼을 지정할 수 있습니다.

# 시리즈에 적용
s1.drop_duplicates() # = s1[~s1.duplicated()]
Out:
0 1
2 2
3 3
4 4

# 데이터 프레임에 적용
df1 = DataFrame({'col1':[1,1,2,3,4],
                 'col2':[5,6,7,7,8]})

# 모든 컬럼이 중복인 행 제거
df1.drop_duplicates()
Out: 
   col1  col2
0     1     5
1     1     6
2     2     7
3     3     7
4     4     8

# 'col' 컬럼이 중복인 행 제거
df1.drop_duplicates('col1')
Out: 
   col1  col2
0     1     5
2     2     7
3     3     7
4     4     8

python으로 할 수 있는 전처리는 거의 다 끝나갑니다. 다음 글에서는 구간 분할, 날짜 모듈과 그에 관련된 내용, groupby 등 이번 글에서 다루지 않은 내용이나 보다 심화된 내용을 다룰 예정입니다. R에 대한 내용까지 다룬 이후에는 모듈을 이용한 ML, DL 사용법, 글의 맞춤법 교정 및 난이도 조정이 계획되어 있습니다.