0928 - Python

오늘·2022년 9월 28일
0

A

목록 보기
23/46

복습문제

# 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'
    }

Series의 map 메서드

사전류 객체나 어떤 함수를 받을 수 있다.

# 예시로 사용할 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] 

cut

# 그룹으로 나이를 분류해보자

# 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)]

# 결과 값에서 (] 되어있던 괄호가 [) 으로 변화된 것을 확인 할 수 있다.

label 옵션으로 그룹 이름 직접 넘기기

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)]

.sample()

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)

one-hot incoding 보기

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

정규 표현식 298

흔히 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))

pandas의 벡터화된 문자열 함수

문자열과 정규 표현 메서드는 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)

데이터 준비하기

계층적 index

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 메서드

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

데이터 합치기

두 DataFrame 합치기

병합(머지, 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

merge 함수 인자 목록

인자 설명
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

데이터를 합치는 또 다른 방법으로 이어붙이기(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

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)로 새로운 색인을 생성한다.

계층적 index로 재형성하기

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 메서드는 누락데이터를 자동으로 걸러내기 때문에 연산을 쉽게 복구 시킬수 있다.

long frmat ⇄ 넓은 형식

데이터베이스나 CSV 파일에 여러 개의 시계열 데이터를 저장하는 일반적인 방법은 시간 순서대로 나열하는 것이다.

melt : wide -> long

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_namevalue_name 옵션을 이용하여 바꾸기 가능하다.

df = pd.read_excel("excel_exam.xlsx")
df.melt(id_vars=['class'], value_vars=['math', 'english', 'science']\
       , var_name='과목', value_name='점수')

melt 참고

0개의 댓글