# DataFrame을 array로 바꾸려면
df.values()
# 배열을 전치하려면
.T / .transpose
# 아래 data로 df 만들기
data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
import pandas as pd
pd.DataFrame(data)
# 위 DataFrame의 state 명을 바꿔주세요
import numpy as np
df = pd.DataFrame(data)
df['East/West'] = np.where(df['state'] == 'Ohio', 'E', 'W')
# dic = {'one': 1, 'two': 2, 'three': 3} 딕셔너리에서 Series를 만들어달라
pd.Series(dic)
# 아래와 같은 모양으로 만들어 주세요
# english
# two 2.0
# three 3.0
# four NaN
# Name: num, dtype: float64
dic = {'one': 1, 'two': 2, 'three': 3}
s = pd.Series(dic,
index = ['two', 'three', 'four'],
name = 'num')
s.index.name = 'english'
s
# 결측치를 알고 싶다면
s.isna()
s.isnull()
# 결측치의 갯수는?
s.isna().sum()
s.isnull()
# pd.Series(['a','b','c','d'], index=['가','나','다'])
# 가 오류나는 이유는?
갯수가 맞지 않는다. index와 값의 매칭이 맞지 않음
# data = pd.Series([1.0, np.nan, 3.5, np.nan, 7.0])
# nan 제외하고 평균구하기
data.dropna().mean()
# data 결측치를 평균값으로 채워넣어라
data.fillna(value=(data.dropna().mean()))
# s = pd.Series([1,6,7,8,6,3,2]) 의 순위를 매기는 방법은? (중복없이)
s.rank(method = 'first')
# apply(), applymap(), map() 의 차이는?
map() : Series의 메소드
applymap() : DataFrame의 메소드
apply() : DataFrame과 Series 모두에 정의, 통으로 적용
frame = pd.DataFrame(np.random.randn(4,3),
columns=list('bde'),
index = ['utah','ohio','texas','oregon'])
df = frame.copy()
# df 에서 각 열의 합을 구하고 싶다
df.sum(axis = 0)
df.apply(sum, axis=0)
# 위에서 나온 값을 아래에 붙여라
df.loc['sum'] = df.sum(axis = 0)
# [추가 답변]
li = list(df.apply(sum, axis = 0))
df1 = df.T
df1['sum'] = li
df = df1.T
# [추가 답변]
df_sum = df.apply(sum)
df_sum = pd.DataFrame(df_sum, columns = ['sum']).T
pd.concat([df, df_sum])
# 예시로 사용할 DataFrame 생성
data = pd.DataFrame({'food' : ['bacon', 'pulled pork', 'bacon',
'Pastrami', 'corned beef', 'Bacon',
'pastrami', 'honey ham', 'nova lox'],
'ounces' : [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
# 해당 육류가 어떤 동물의 고기인지 알려줄 수 있는 정보 컬럼
# 사전 데이터 작성
meat_to_animal = {
'bacon': 'pig',
'pulled pork' : 'pig',
'pastrami' : 'cow',
'corned beef' : 'cow',
'honey ham' : 'pig',
'nova lox' : 'salmon'
}
사전류 객체나 어떤 함수를 받을 수 있다.
# 예시로 사용할 data에는 육류 이름에 대소문자가 섞여있는 문제가 잇으므로
# animal 칼럼의 값을 모두 소문자로 변경
data['animal'] = lowercased = data['food'].str.lower()
# 람다식을 사용하는 것도 같은 결과
data['food'].map(lambda x:meat_to_animal[x.lower()])
fillna 메서드가 누락된 값을 채우는 값 치환 작업이라면, replace 메서드는 같은 작업에 대해 좀 더 간단하고 유연한 방법을 제공
data = pd.Series([1., -999.,2.,-999.,-1000])
data.replace(-999, np.nan)
rename을 사용하는 방법과 람다식을 사용하여 만드는 방법을 사용해보자
data = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=['Ohio', 'Colorado', 'New York'],
columns=['one', 'two', 'three', 'four'])
람다를 사용하여 새로운 자료구조를 만들지 않고 바로 축 이름을 변경해보자
f = lambda x:x[:4].upper()
f("abcdef") # 'ABCD'
data.index.map(f)
# Index(['OHIO', 'COLO', 'NEW '], dtype='object')
# 이 값을 그대로 대입
data.index = data.index.map(f)
data
# one two three four
# OHIO 0 1 2 3
# COLO 4 5 6 7
# NEW 8 9 10 11
당연히 컬럼 값을 변경할 수도 있다
data.columns = data.columns.map(f)
data
# ONE TWO THRE FOUR
# OHIO 0 1 2 3
# COLO 4 5 6 7
# NEW 8 9 10 11
rename 메서드는 사전 형식의 객체를 이용해 축 이름 중 일부만 변경하는 것도 가능하다.
data.rename(index={'OHIO':'INDIANA'}, inplace=True)
data
# ONE TWO THRE FOUR
# INDIANA 0 1 2 3
# COLO 4 5 6 7
# NEW 8 9 10 11
연속성 데이터는 종종 개별로 분할하거나 분석을 위해 그룹별로 나누기도 한다.
# 나이대
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
# 그룹으로 나이를 분류해보자
# threshold : 경계값, 임계치, 문턱
threshold = [18, 25, 35, 60, 100]
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
threshold = [18, 25, 35, 60, 100]
cats = pd.cut(ages, threshold)
cats
# [(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
# Length: 12
# Categories (4, interval[int64, right]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
ages[0] 은 (18, 25] 사이에, ages[1]은 (18, 25] ... 있다는 결과값이 출력되었다. 이 객체는 그룹의 이름이 담긴 배열이라고 생각하면 되고, 반환된 객체는 Categorical이라는 특수한 객체이다.
cats.codes
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
# 결과에 대한 그룹 수
pd.value_counts(cats)
# (18, 25] 5
# (25, 35] 3
# (35, 60] 3
# (60, 100] 1
# dtype: int64
모양은 다르지만 같은 결과이다. threshold 의 0번에 있는 그룹과 ages[0] 이 매치된다는 것 조금 더 보기 편하다
value_counts 는 각 그룹에 몇 개의 값이 해당되는지 보여준다.
간격을 나타내는 표기법을 잘 보면 ()혹은 [] 의 모양이 아니라 (] 인것을 확인 할 수 있는데, 중괄호 쪽의 값은 포함되지 않고, 대괄호 쪽의 값은 포함한다. (단, right=False 옵션을 이용하여 중괄호는 포함, 대괄호는 미포함 되도록 바꿀 수 있다.)
pd.cut(ages, [18, 26, 36, 61, 100], right = False)
# [[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)]
# Length: 12
# Categories (4, interval[int64, left]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]
# 결과 값에서 (] 되어있던 괄호가 [) 으로 변화된 것을 확인 할 수 있다.
cut의 labels옵션을 사용하여 주면 바로 그룹이름을 넘겨줄 수 있다.
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, threshold, labels=group_names)
# ['Youth', 'Youth', 'Youth', 'YoungAdult', 'Youth', ..., 'YoungAdult', 'Senior', 'MiddleAged', 'MiddleAged', 'YoungAdult']
# Length: 12
# Categories (4, object): ['Youth' < 'YoungAdult' < 'MiddleAged' < 'Senior']
명시적으로 x, 그룹의 개수를 넘겨준다면, 데이터의 최소값과 최대값을 기준으로 균등한 길이의 그룹을 자동 계산한다.
허나 그냥 cut 함수에 이를 사용하면 데이터의 분산에 따라 각각의 그룹마다 데이터 수가 다르게 나뉘는 경우가 많고,,, 이를 위한 가장 적합한 함수로는 qcut이 있다. qcut은 표준 변위치를 사용하기 때문에 적당히 같은 크기의 그룹으로 나눌 수 있다.
# cut에 숫자를 넘겨주는 경우
# precision=2 는 소수점 아래 2자리까지만 보겠다는 옵션을 적용시킨 것이다.
data = np.random.rand(20)
pd.cut(data, 4, precision=2)
# [(0.43, 0.64], (0.64, 0.86], (0.43, 0.64], (0.43, 0.64], (0.22, 0.43], ..., (0.43, 0.64], (0.64, 0.86], (0.00082, 0.22], (0.64, 0.86], (0.00082, 0.22]]
# Length: 20
# Categories (4, interval[float64, right]): [(0.00082, 0.22] < (0.22, 0.43] < (0.43, 0.64] < (0.64, 0.86]]
# qcut 사용
cats = pd.qcut(data, 4) # 4분위로 분류하겠다
cats
# [(0.288, 0.538], (0.288, 0.538], (0.192, 0.288], (0.0382, 0.192], (0.288, 0.538], ..., (0.192, 0.288], (0.538, 0.958], (0.288, 0.538], (0.538, 0.958], (0.192, 0.288]]
# Length: 20
# Categories (4, interval[float64, right]): [(0.0382, 0.192] < (0.192, 0.288] < (0.288, 0.538] < (0.538, 0.958]]
numpy.random.permutaion 함수로 순서를 재배치 해줄 수 있다. 괄호 안에는 바꾸고 싶은 만큼의 길이를 적어준다.값을 뽑는..다..?
df = pd.DataFrame(np.arange(5*4).reshape(5, 4))
df
# 0 1 2 3
# 0 0 1 2 3
# 1 4 5 6 7
# 2 8 9 10 11
# 3 12 13 14 15
# 4 16 17 18 19
np.random.permutation(5)
# array([2, 3, 1, 4, 0])
이 배열은 iloc기반의 색인이나 take 함수에서 사용이 가능하다.
# 잘 섞인거 같다.
df.take(a)
# 0 1 2 3
# 4 16 17 18 19
# 1 4 5 6 7
# 3 12 13 14 15
# 0 0 1 2 3
# 2 8 9 10 11
import pydataset as py
mpg = py.data('mpg')
# mpg 값이 너무 크니까 섞어서 30% 만 가져와라
sample_index = np.random.permutation(len(mpg))
mpg = mpg.take(sample_index)
mpg[:int(len(mpg)*0.3)]
DataFrame이나 Series에서 무작위로 몇 개의 값(레이블)을 출력하는 메서드이다.
df.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None, ignore_index=False)
n : 추출할 갯수. replace가 False면 n의 최댓값은 레이블의 갯수를 넘을수 없다.
frac : 추출할 비율. 1보다 작은값으로 설정하며(예 : 0.3 이면 30%), n과 동시에 사용할 수 없다.
replace : 중복추출의 허용 여부. True로 하면 중복추출이 가능하며 n의 최댓값이 레이블의 갯수보다 커도 된다.
weight : 가중치. 즉 레이블마다 추출될 확률 지정가능. 합계가 1(100%)이 아닐경우 자동으로 1로 연산합니다.
random_state : 랜덤 추출한 값에 시드 설정. 원하는 값을 설정하면, 항상 같은 결과를 출력한다.
axis : {0 : index / 1 : columns} 추출할 레이블 설정
ignore_index : index의 무시 여부. True일경우 출력시 index를 무시하고 숫자로 출력.
# 추출 갯수 70개
mpg.sample(70)
# 추출 비율 30%
mpg.sample(frac=.3)
df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
'data1': range(6)})
df
# key data1
# 0 b 0
# 1 b 1
# 2 a 2
# 3 c 3
# 4 a 4
# 5 b 5
pd.get_dummies(df['key'])
# a b c
# 0 0 1 0
# 1 0 1 0
# 2 1 0 0
# 3 0 0 1
# 4 1 0 0
# 5 0 1 0
흔히 regex라 불리는 단일 표현식은 정규 표현 언어로 구성된 문자열이다.
| 인자 | 설명 |
|---|---|
| findall | 문자열에서 겹치지 않는 모든 발견된 패턴을 리스트로 반환 |
| finditer | findall 가 같지만 발견된 패턴을 이터레이터를 통해 하나씩 반환 |
| match | 문자열의 시작점부터 패턴을 찾고 선택적으로 패턴 컴포넌트를 그룹으로 나눈다. 일치하는 패턴이 있다면 match객체를 반환하고 그렇지 않으면 None을 반환 |
| search | 문자열에서 패턴과 일치하는 내용을 검색하고 match 객체를 반환한다. match 메서드와는 다르게 시작부터 일치하는 내용만 찾지 않고 문자열 어디든 일치하는 내용이 있다면 반환 |
| split | 문자열을 패턴과 일치하는 부분으로 분리한다. |
| sub, subn | 문자열에서 일치하는 모든 패턴(sub) 혹은 처음 n개의 패턴(subn)을 대체표현으로 치환. 대체된 문자열은 \1, \2 와 같은 기호를 사용하여 매치 그룹의 요소를 참조 |
import re
text = "foo bar\t baz \tqux"
reg = re.compile('\s+')
# split
reg.split(text)
# ['foo', 'bar', 'baz', 'qux']
# findall
reg.findall(text)
# [' ', '\t ', ' \t']
reg.match(text)
#
# 문자열의 첫번째만 보는데
# text 첫번째에는 이메일이 아니라 이름이 들어가 있기 때문에
# 아무것도 반환하지 않았다.
reg.search(text)
# <re.Match object; span=(6, 21), match='dave@google.com'>
print(reg.sub('REDACTED', text))
# Dave REDACTED
# Steve REDACTED
# Rob REDACTED
# Ryan REDACTED
# 이메일 부분이 모두 표시된 글자로 치환되었다.
sub에서 \1, \2 같은 특수한 기호를 사용하여 패턴 그룹에 접근할 할 수 있다.
# \1은 첫번째로 찾은 그룹을
# \2는 두번째로 찾은 그룹을 의미한다.
print(reg.sub('Username:\1, domain:\2, Suffix:\3', text))
문자열과 정규 표현 메서드는 data.map을 사용하여 (lambda, 다른 함수를) 넘기며 각 값에 적용시킬 수 있지만 Na를 만나면 실패 할 수 있다. 이때, contains를 이용하여 검사 가능하다.
data = {'Dave' : 'dave@google.com',
'Steve' : 'steve@gmail.com',
'Rob' : 'rob@gmail.com',
'Wes' : np.nan}
data = pd.Series(data)
data.str.contains('gmail')
당연히 정규표현식을 IGNORECASE 같은 옵션과 함께 사용해도 된다
import re
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2, 4})'
data.str.findall(pattern, flags = re.IGNORECASE)
pandas의 중요한 기능인데 축에 대하여 둘 이상의 index 단계를 지정할 수 있도록 해준다. 약간 추상적으로 말하자면 높은 차원의 데이터를 낮은 차원의 형식으로 다룰 수 있게 해주는 기능이며, 데이터를 재형성하고 피벗테이블 같은 생성 그룹 기반의 작업을 할 때 계층적 index는 꽤 중요하게 사용된다.
# 예시로 사용할 Series 하나 생성
data = pd.Series(np.random.randn(9),
index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
[1, 2, 3, 1, 3, 1, 2, 2, 3]])
data
# a 1 -1.293077
# 2 -1.508348
# 3 0.887722
# b 1 -0.340732
# 3 -0.494546
# c 1 1.702489
# 2 1.141758
# d 2 -0.927320
# 3 -0.326255
# dtype: float64
생성한 예제는 MulitIndex를 사용하는 Series인데, 계층이 보인다. 윗단계의 index를 이용하여 하위 계층에 접근할 수 있다.
data.index
# MultiIndex([('a', 1),
# ('a', 2),
# ('a', 3),
# ('b', 1),
# ('b', 3),
# ('c', 1),
# ('c', 2),
# ('d', 2),
# ('d', 3)],
# )
data['b']
# 1 -0.340732
# 3 -0.494546
# dtype: float64
data['b':'c']
# b 1 -0.340732
# 3 -0.494546
# c 1 1.702489
# 2 1.141758
# dtype: float64
# 각 상위 계층에서 loc[:,2]가 되는 모습 확인
data.loc[: ,2]
# a -1.508348
# c 1.141758
# d -0.927320
# dtype: float64
unstack 메서드는 데이터를 새롭게 배열하는 데 사용한다.
data.unstack()
# 1 2 3
# a -1.293077 -1.508348 0.887722
# b -0.340732 NaN -0.494546
# c 1.702489 1.141758 NaN
# d NaN -0.927320 -0.326255
# 반대의 작업을 하는 stack 메서드도 있다.
data.unstack().stack()
# a 1 -1.293077
# 2 -1.508348
# 3 0.887722
# b 1 -0.340732
# 3 -0.494546
# c 1 1.702489
# 2 1.141758
# d 2 -0.927320
# 3 -0.326255
# dtype: float64
병합(머지, merge)이나 조인(join)은 관계형 데이터베이스의 핵심적인 연산으로, 하나 이상의 키(key)를 사용하여 데이터 집합의 로우를 합친다.
# 예시로 사용할 DataFrame 두개 생성
df1 = pd.DataFrame({'key' : ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1' : range(7)})
df2 = pd.DataFrame({'key':['a', 'b', 'd'],
'data2' : range(3)})
df1데이터는 key 컬럼에 여러개의 a, b 로우를 가지고 있고, df2의 데이터는 key컬럼에 유일 로우를 가지고 있다. 이런 경우를 다대일 이라고 하며, merge할 경우 아래와 같이 어떤컬럼을 병합할 것인지 명시하지 않아도 중복된 컬럼 이름을 키로 사용한다. (하지만 명시적으로 지정해주는 습관을 드리자)
pd.merge(df1, df2)
# key data1 data2
# 0 b 0 1
# 1 b 1 1
# 2 b 6 1
# 3 a 2 0
# 4 a 4 0
# 5 a 5 0
# 명시적으로 지정해주는 습관
print(pd.merge(df1, df2, on = 'key'))
# key data1 data2
# 0 b 0 1
# 1 b 1 1
# 2 b 6 1
# 3 a 2 0
# 4 a 4 0
# 5 a 5 0
중복된 컬럼 이름이 하나도 없다면 따로 지정
df1 = pd.DataFrame({'lkey' : ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1' : range(7)})
df2 = pd.DataFrame({'rkey':['z', 'y', 'u'],
'data2' : range(3)})
pd.merge(df1, df2, left_on='lkey', right_on='rkey')
how 인자로 'left', 'right', 'outer'을 넘겨서 각각 조인을 수행할 수도 있다.
df1 = pd.DataFrame({'key' : ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1' : range(7)})
df2 = pd.DataFrame({'key' : ['a', 'b', 'd'],
'data2' : range(3)})
pd.merge(df1, df2, how='outer')
# key data1 data2
# 0 b 0.0 1.0
# 1 b 1.0 1.0
# 2 b 6.0 1.0
# 3 a 2.0 0.0
# 4 a 4.0 0.0
# 5 a 5.0 0.0
# 6 c 3.0 NaN
# 7 d NaN 2.0
| 인자 | 설명 |
|---|---|
| left | 병합하려는 DataFrame 중 왼쪽에 위치한 DataFrame |
| right | 병합하려는 DataFrame 중 오른쪽에 위치한 DataFrame |
| how | join 방법 / 기본값 inner |
| on | 조인하려는 컬럼이름 반드시 두 DataFrame 객체 모두에 존재하는 이름이어야 한다. 만약 명시되지 않고, 다른 조인키도 주어지지 않는다면 left와 right에서 공통되는 컬럼을 조인키로 자동 사용 |
| left_on | 조인키로 사용할 left DataFrame의 컬럼 |
| right_on | 조인키로 사용할 right DataFrame의 컬럼 |
| left_index | 조인키로 사용할 left DataFrame의 index 로우(다중 index일 경우 키) |
| right_index | 조인키로 사용할 right DataFrame의 index 로우(다중 index일 경우 키) |
| sort | 조인키에 따라 병합된 데이터를 사전순으로 정렬 기본값은 True. 대용량 데이터의 경우 false로 하면 성능상의 이득을 얻을 수 있다. |
| suffixes | 컬럼이름이 겹칠 경우 각 컬럼 이름 뒤에 붙일 문자열의 튜플. 기본값은 `('_x', '_y')` 만약 'data'라는 컬럼이름이 양쪽 DataFrame에 같이 존재하면 결과에는 'data_x, data_y'로 보여진다. |
| copy | false일 경우, 예외적인 경우에 데이터가 결과로 복사되지 않도록 한다. 기본값은 항상 복사가 이루어진다. |
| indicator | merge라는 이름의 특별한 컬럼을 추가하여 각 로우의 소스가 어디인지 나타낸다. 'left_only', 'right_only', 'both' 세가지 값을 가진다. |
데이터를 합치는 또 다른 방법으로 이어붙이기(concatenate)이다. 연결(binding) 혹은 적층(stacking) 이라고도 한다. Numpy에서의 ndarray 를 이어 붙이는 함수이다.
arr1 = np.arange(12).reshape(3,4)
arr2 = arr1.copy()
np.concatenate([arr1, arr2])
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
np.concatenate([arr1, arr2], axis=1)
# array([[ 0, 1, 2, 3, 0, 1, 2, 3],
# [ 4, 5, 6, 7, 4, 5, 6, 7],
# [ 8, 9, 10, 11, 8, 9, 10, 11]])
concat함수에 객체를 묶어서 전달하면 값과 색인을 연결해준다. 기본값으로 axis=0 을 가지고 있으며, 겹치는 축이 없다면 외부조인으로 정렬된 합집합을 넘겨준다. 당연히 join 옵션을 사용하여 교집합(join=inner)을 구할 수도 있다.
pd.concat([df1, df2])
# one two three four
# a 0.0 1.0 NaN NaN
# b 2.0 3.0 NaN NaN
# c 4.0 5.0 NaN NaN
# a NaN NaN 5.0 6.0
# c NaN NaN 7.0 8.0
pd.concat([df1, df2], axis=1)
# one two three four
# a 0 1 5.0 6.0
# b 2 3 NaN NaN
# c 4 5 7.0 8.0
concat 함수 인자
| 인자 | 설명 |
|---|---|
| objs | 이어붙일 pandas객체의 사전이나 리스트. 필수인자 |
| axis | 이어붙일 축 방향. 기본값은 0 |
| join | 조인방식. 'inner' (내부조인, 교집합)과 'outer'(외부조인, 합집합) 가 있으며 기본값은 'outer' |
| join_axes | 합집합/교집합을 수행하는 대신 다른 n-1 축으로 사용할 index를 지정 |
| keys | 이어붙일 객체나 이어붙인 축에 대한 계층 색인을 생성하는데 연관된 값이다. 리스트나 임의의 값이 들어있는 배열, 튜플의 배열 또는 배열의 리스트(levles 옵션에 다차원 배열이 넘어온 경우) 가 될 수 있다. |
| levels | 계층 색인 레벨로 사용할 색인을 지정한다. keys가 넘어온 경우 여러개의 색인을 지정한다. |
| names | key나 levels 혹은 둘 다 있을 경우 새엉된 계층 레벨을 위한 이름 |
| verify_interity | 이어붙인 객체에 중복되는 축이 있는지 검사하고 있다면 예외를 발생시킨다. 기본값은 false로 중복을 허용 |
| ignore_index | 이어붙인 축의 색인을 유지하지 않고 range(total_length)로 새로운 색인을 생성한다. |
stack : 데이터의 컬럼을 로우로 피벗(회전) 시킨다
unstack : 로우를 컬럼으로 피벗(회전) 시킨다
# example DataFrame
data = pd.DataFrame(np.arange(6).reshape((2, 3)),
index=pd.Index(['Ohio', 'Colorado'], name='state'),
columns=pd.Index(['one', 'two', 'three'],
name='number'))
data
# number one two three
# state
# Ohio 0 1 2
# Colorado 3 4 5
stack 메서드를 사용하면 컬럼이 로우로 피벗되어 Series 객체를 반환한다
result = data.stack()
result
# state number
# Ohio one 0
# two 1
# three 2
# Colorado one 3
# two 4
# three 5
# dtype: int32
unstack 메서드를 사용시 위 계층적 index를 가진 Series로 부터 다시 DataFrame을 얻을 수 있다.
result.unstack()
# number one two three
# state
# Ohio 0 1 2
# Colorado 3 4 5
stack 과 unstack은 기본적으로 가장 안쪽에 있는 level 부터 끄집어내는데, level 숫자나 이름을 전달하여 단계를 지정할 수 있다. 기본값은 -1
result.unstack(0)
# state Ohio Colorado
# number
# one 0 3
# two 1 4
# three 2 5
print(result.unstack(1))
# number one two three
# state
# Ohio 0 1 2
# Colorado 3 4 5
해당 레벨에 있는 모든 값이 하위 그룹에 속하지 않을경우 unstack을 했을 때 누락데이터가 생길 수 있다. 하지만 stack 메서드는 누락데이터를 자동으로 걸러내기 때문에 연산을 쉽게 복구 시킬수 있다.
데이터베이스나 CSV 파일에 여러 개의 시계열 데이터를 저장하는 일반적인 방법은 시간 순서대로 나열하는 것이다.
df = pd.DataFrame({'key': ['foo', 'bar', 'baz'],
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]})
df
# key A B C
# 0 foo 1 4 7
# 1 bar 2 5 8
# 2 baz 3 6 9
df.melt()
# variable value
# 0 key foo
# 1 key bar
# 2 key baz
# 3 A 1
# 4 A 2
# 5 A 3
# 6 B 4
# 7 B 5
# 8 B 6
# 9 C 7
# 10 C 8
# 11 C 9
모든 칼럼의 값이 주르르륵 녹아 두 개의 컬럼 안에 다 들어간 모습
df.melt(['A', 'B'])
# A B variable value
# 0 1 4 key foo
# 1 2 5 key bar
# 2 3 6 key baz
# 3 1 4 C 7
# 4 2 5 C 8
# 5 3 6 C 9
지정해준 A, B를 기준으로 나머지 값들이 A, B, 인덱스 별로 추출하기에 용이하도록 정리되었다.
df.melt(value_vars=['A', 'B'])
# variable value
# 0 A 1
# 1 A 2
# 2 A 3
# 3 B 4
# 4 B 5
# 5 B 6
df.melt(id_vars=['key'], value_vars=['A', 'B'])
# key variable value
# 0 foo A 1
# 1 bar A 2
# 2 baz A 3
# 3 foo B 4
# 4 bar B 5
# 5 baz B 6
id_vars : 기준이 되는 컬럼 지정
value_vars : 녹여서 값과 같이 행으로 들어갈 컬럼을 의미한다.
그러니까 A기준으로 B행을 녹이겠다(추출하겠다면) 아래와 같이 작성하면 된다.
df.melt(id_vars='A', value_vars='B')
# A variable value
# 0 1 B 4
# 1 2 B 5
# 2 3 B 6
melt한 상태에서 컬럼명을 바꾸고자 한다면, var_name과 value_name 옵션을 이용하여 바꾸기 가능하다.
df = pd.read_excel("excel_exam.xlsx")
df.melt(id_vars=['class'], value_vars=['math', 'english', 'science']\
, var_name='과목', value_name='점수')
