분석을 하다 보면 원본 데이터의 구조가 분석 기법에 맞지 않아서 행과 열의 위치를 바꾼다거나, 특정 요인에 따라 집계를 해서 구조를 바꿔주어야 하는 경우가 있습니다.

 

재구조화(reshaping data)를 위해 사용할 수 있는 Python pandas의 함수들로 아래와 같이 다양한 함수가 있습니다. 

 

 - (1) pivot(), pd.pivot_table()

 - (2) stack(), unstack()

 - (3) melt()

 - (4) wide_to_long()

 - (5) pd.crosstab() 

 

 

이번 포스팅에서는 마지막으로 범주형 변수로 되어있는 요인(factors)별로 교차분석(cross tabulations) 해서, 행, 열 요인 기준 별로 빈도를 세어서 도수분포표(frequency table), 교차표(contingency table) 를 만들어주는 pd.crosstab() 에 대해서 알아보겠습니다.

 

 

 

 

 

먼저 필요한 모듈을 불러오고, 예제로 사용할 (범주형 요인 변수를 가지고 있는) 간단한 데이터셋을 생성해보겠습니다.

 

 

In [1]: import pandas as pd


In [2]: from pandas import DataFrame


In [3]: data = DataFrame({'id': ['id1', 'id1', 'id1', 'id2', 'id2', 'id3'],

   ...: 'fac_1': ['a', 'a', 'a', 'b', 'b', 'b'],

   ...: 'fac_2': ['d', 'd', 'd', 'c', 'c', 'd']})


In [4]: data

Out[4]:

    fac_1   fac_2    id
0     a       d       id1
1     a       d       id1
2     a       d       id1
3     b       c       id2
4     b       c       id2
5     b       d       id3

 

 

 

 

  (1) 교차표(contingency table, frequency table) 만들기 : pd.crosstab(index, columns)

 

pd.crosstab()의 행과 열 위치에는 array 형식의 데이터가 들어갑니다

 

 

# cross tabulations using pd.crosstab => contingency table

In [5]: pd.crosstab(data.fac_1, data.fac_2)

Out[5]:
fac_2  c  d
fac_1     
a      0  3
b      2  1

 

In [6]: pd.crosstab(data.id, data.fac_1)

Out[6]: 
fac_1  a  b
id        
id1    3  0
id2    0  2
id3    0  1

 

In [7]: pd.crosstab(data.id, data.fac_2)

Out[7]:
fac_2  c  d
id        
id1    0  3
id2    2  0
id3    0  1

 

 

 

 

  (2) Multi-index, Multi-level로 교차표 만들기 : pd.crosstab([id1, id2], [col1, col2])

 

 

# cross tabulations using pd.crosstab with Multi-level columns

In [8]: pd.crosstab(data.id, [data.fac_1, data.fac_2])

Out[8]:

fac_1  a  b  
fac_2  d  c  d
id           
id1    3  0  0
id2    0  2  0
id3    0  0  1


In [9]: pd.crosstab([data.fac_1, data.fac_2], data.id)

Out[9]:

id           id1  id2  id3
fac_1 fac_2              
a     d        3    0    0
b     c        0    2    0
      d        0    0    1

 

 

 

 

  (3) 교차표의 행 이름, 열 이름 부여 : pd.crosstab(rownames=['xx'], colnames=['aa'])

 

 

# pd.crosstab(rownames, colnames) : giving rownames, colnames

In [10]: pd.crosstab(data.id, [data.fac_1, data.fac_2],

    ...: rownames=['id_num'],

    ...: colnames=['a_b', 'c_d'])

Out[10]:

a_b     a  b  
c_d     d  c  d
id_num        
id1     3  0  0
id2     0  2  0
id3     0  0  1

 

 

 

 

  (4) 교차표의 행 합, 열 합 추가하기 : pd.crosstab(margins=True)

 

 

# pd.crosstab(margins=True) : adding row/column margins

In [11]: pd.crosstab(data.id, [data.fac_1, data.fac_2],

    ...: margins=True)

Out[11]:

fac_1  a  b    All
fac_2  d  c  d   
id               
id1    3  0  0   3
id2    0  2  0   2
id3    0  0  1   1
All    3  2  1   6

 

 

 

 

 

  (5) 구성비율로 교차표 만들기 : pd.crosstab(normalize=True)

 

# pd.corsstab(normalize=True)
# : Normalize by dividing all values by the sum of values

In [12]: pd.crosstab(data.id, [data.fac_1, data.fac_2],

    ...: normalize=True)

Out[12]:

fac_1    a         b         
fac_2    d         c         d
id                           
id1    0.5  0.000000  0.000000
id2    0.0  0.333333  0.000000
id3    0.0  0.000000  0.166667

 

 

 

 

이상으로 pd.crosstab() 을 이용한 교차표 구하기를 마치겠습니다. 

 

 

교차표는 R이나 SPSS가 깔끔하게 결과를 제시해주는 것 같고요, R이 분석가가 설정할 수 있는 옵션이 조금 더 다양하므로 입맛에 맞게 교차분석도 하고 카이제곱검정도 하고 싶은 분은 아래 링크되어 있는 포스팅을 참고하세요. 

 

 

 

많은 도움이 되었기를 바랍니다.  

 

 

728x90
반응형
Posted by Rfriend
,

분석을 하다 보면 원본 데이터의 구조가 분석 기법에 맞지 않아서 행과 열의 위치를 바꾼다거나, 특정 요인에 따라 집계를 해서 구조를 바꿔주어야 하는 경우가 있습니다.

 

재구조화(reshaping data)를 위해 사용할 수 있는 Python pandas의 함수들에 대해서 아래의 순서대로 나누어서 소개해보겠습니다.

 

 - (1) pivot(), pd.pivot_table()

 - (2) stack(), unstack()

 - (3) melt()

 - (4) wide_to_long()

 - (5) pd.crosstab() 

 

 

이번 포스팅에서는 pd.wide_to_long() 에 대해서 알아보겠습니다.

 

필요한 모듈을 불러오고, 실습에 필요한 간단한 예제 데이터셋을 만들어보겠습니다.

 

 

# importing libraries

In [1]: import numpy as np


In [2]: import pandas as pd


In [3]: from pandas import DataFrame


# setting random seed number

In [4]: np.random.seed(10)


# making an example 'wide' DataFrame

In [5]: data_wide = pd.DataFrame({"C1prd1" : {0 : "a", 1 : "b", 2 : "c"},

   ...: "C1prd2" : {0 : "d", 1 : "e", 2 : "f"},

   ...: "C2prd1" : {0 : 2.5, 1 : 1.2, 2 : .7},

   ...: "C2prd2" : {0 : 3.2, 1 : 1.3, 2 : .1},

   ...: "value" : dict(zip(range(3), np.random.randn(3)))

   ...: })

   ...:


In [6]: data_wide["seq_no"] = data_wide.index


In [7]: data_wide

Out[7]:

     C1prd1 C1prd2  C2prd1  C2prd2     value      seq_no
0       a         d         2.5       3.2      1.331587       0
1       b         e         1.2       1.3      0.715279       1
2       c          f         0.7       0.1     -1.545400       2

 

 

 

 

이제 pd.wide_to_long() 함수를 써서 데이터를 재구조화 해보겠습니다.

 

wide_to_long()은 pivot() 이나 stack() 과는 다르게 "칼럼 이름의 앞부분"과 나머지 "칼럼 이름의 뒷부분"을 구분해서, 칼럼 이름의 앞부분을 칼럼 이름으로, 칼럼 이름의 나머지 뒷부분을 행(row)의 원소로 해서 세로로 길게(long~) 쌓아 줍니다.  말로 설명해주기가 참 힘든데요, 아래의 wide_to_long() 적용 전, 후의 변화 이미지를 참고하시기 바랍니다.

 

제가 R, SAS, SPSS 다 써봤는데요, Python의 pd.wide_to_long() 함수 같은거는 본 적이 없습니다. 좀 생소하고, 처음 봤을 때 한눈에 잘 안들어왔던 유형의 데이터 재구조화 함수예요. 

 

 

 

pd.widt_to_long() 함수를 한번 사용해서 가로로 넓은 데이터(wide~)를 세로로 길게(long~) 재구조화 해보겠습니다.

 

 

  (1) pd.wide_to_long(data, ["col_prefix_1", "col_prefix_2"], i="idx_1", j="idx_2")

 

 

# reshaping a 'wide' DataFrame to a 'long' DataFrame

In [8]: data_long = pd.wide_to_long(data_wide, ["C1", "C2"], i="seq_no", j="prd")


In [9]: data_long

Out[9]:

                     value    C1    C2
seq_no prd                 
0        prd1  1.331587      2.5
1        prd1  0.715279   b     1.2
2        prd1 -1.545400   c     0.7
0        prd2  1.331587      3.2
1        prd2  0.715279   e     1.3
2        prd2 -1.545400   f     0.1

 

 

 

 

  (2) pd.wide_to_long()에 의한 index, columns 변화 비교

 

 

# data_wide (original data set) : index, columns

In [10]: data_wide.index

Out[10]: Int64Index([0, 1, 2], dtype='int64')


In [11]: data_wide.columns

Out[11]: Index(['C1prd1', 'C1prd2', 'C2prd1', 'C2prd2', 'value', 'seq_no'], dtype='object')

 


# data_long (reshaped data set) : index, columns

In [12]: data_long.index

Out[12]:

MultiIndex(levels=[[0, 1, 2], ['prd1', 'prd2']],

labels=[[0, 1, 2, 0, 1, 2], [0, 0, 0, 1, 1, 1]],

names=['seq_no', 'type'])


In [13]: data_long.columns

Out[13]: Index(['value', 'C1', 'C2'], dtype='object')

 

 

 

이상으로 pd.wide_to_long() 을 이용한 데이터 재구조화 소개를 마치겠습니다.

 

다음번 포스팅에서는 pd.crosstab() 에 대해서 알아보겠습니다.

 

많은 도움 되었기를 바랍니다.

 

 

728x90
반응형
Posted by Rfriend
,

데이터 재구조화(reshaping data)를 위해 사용할 수 있는 Python pandas의 함수들에 대해서 아래의 순서대로 나누어서 소개해보겠습니다.
 
 - (1) pivot(), pd.pivot_table()
 - (2) stack(), unstack()
 - (3) melt()

 - (4) wide_to_long()
 - (5) pd.crosstab() 
 
 
이번 포스팅에서는 세번째로 pd.melt() 사용법에 대해서 알아보겠습니다.

 

R 사용자라면 reshape package의 melt(), cast() 함수를 생각하면 쉽게 이해할 수 있을 것입니다. melt()는 pivot_table()과 함께 데이터 전처리 단계에서 자주 사용되곤 합니다.

 

melt() 는 ID 변수를 기준으로 원래 데이터셋에 있던 여러개의 칼럼 이름을 'variable' 칼럼에 위에서 아래로 길게 쌓아놓고, 'value' 칼럼에 ID와 variable에 해당하는 값을 넣어주는 식으로 데이터를 재구조화합니다.  말로 설명하자니 좀 어려운데요, 아래의 melt() 적용 전, 후의 이미지를 참고하시기 바랍니다.

 

 

 

 

필요한 라이브러리를 불러오고, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다.

 

 

# importing libraries

In [1]: import numpy as np


In [2]: import pandas as pd


In [3]: from pandas import DataFrame

 

# making an example DataFrame

In [4]: data = DataFrame({'cust_ID' : ['C_001', 'C_001', 'C_002', 'C_002'],

   ...: 'prd_CD' : ['P_001', 'P_002', 'P_001', 'P_002'],

   ...: 'pch_cnt' : [1, 2, 3, 4],

   ...: 'pch_amt' : [100, 200, 300, 400]})

   ...:


In [5]: data

Out[5]:

  cust_ID  pch_amt  pch_cnt prd_CD
0   C_001      100        1  P_001
1   C_001      200        2  P_002
2   C_002      300        3  P_001
3   C_002      400        4  P_002

 

 

 

 

  (1) pd.melt(data, id_vars=['id1', 'id2', ...]) 를 사용한 데이터 재구조화

 

 

# melt()

In [6]: pd.melt(data, id_vars=['cust_ID', 'prd_CD'])

Out[6]:

   cust_ID prd_CD variable     value
0   C_001  P_001  pch_amt    100
1   C_001  P_002  pch_amt    200
2   C_002  P_001  pch_amt    300
3   C_002  P_002  pch_amt    400
4   C_001  P_001  pch_cnt      1
5   C_001  P_002  pch_cnt      2
6   C_002  P_001  pch_cnt      3
7   C_002  P_002  pch_cnt      4

 

 

 

 (2) pd.melt() 의 variable 이름, value 이름 부여하기 : var_name, value_name

 

 

# # melt : assigning name of variable and value

In [7]: pd.melt(data, id_vars=['cust_ID', 'prd_CD'],

   ...: var_name='pch_CD', value_name='pch_value')

Out[7]:

  cust_ID  prd_CD  pch_CD      pch_value
0   C_001  P_001   pch_amt        100
1   C_001  P_002   pch_amt        200
2   C_002  P_001   pch_amt        300
3   C_002  P_002   pch_amt        400
4   C_001  P_001   pch_cnt          1
5   C_001  P_002   pch_cnt          2
6   C_002  P_001   pch_cnt          3
7   C_002  P_002   pch_cnt          4

 

 

 

 

  (3) data vs. pd.melt() vs. pd.pivot_table() 비교해보기

 

melt()와 pivot_table() 이 비슷한거 같은데.... 뭔가 다른거 같기도 하고... 왜 비슷한것들을 여러개 만들어놔서 사람을 헷갈리게 하는 것일까 의아할 것 같습니다.

 

아래에 똑같은 data에 대해서 melt()와 pivot_table() 을 적용해보았습니다.  melt()와 pivot_table()을 적용하고 난 이후의 index와 columns 을 유심히 살펴보시기 바랍니다.  melt()는 ID가 칼럼으로 존재하는 반면에, pivot_table()은 ID가 index로 들어갔습니다.

 

 

# comparison among (a) data vs. (b) pd.melt() vs. pd.pivot_table()

 

# (a) data

In [8]: data

Out[8]:

  cust_ID  pch_amt  pch_cnt prd_CD
0   C_001      100        1  P_001
1   C_001      200        2  P_002
2   C_002      300        3  P_001
3   C_002      400        4  P_002

 


# (b) melt()

In [9]: data_melt = pd.melt(data, id_vars=['cust_ID', 'prd_CD'],

   ...: var_name='pch_CD', value_name='pch_value')


In [10]: data_melt

Out[10]:

  cust_ID prd_CD   pch_CD  pch_value
0   C_001  P_001  pch_amt        100
1   C_001  P_002  pch_amt        200
2   C_002  P_001  pch_amt        300
3   C_002  P_002  pch_amt        400
4   C_001  P_001  pch_cnt          1
5   C_001  P_002  pch_cnt          2
6   C_002  P_001  pch_cnt          3
7   C_002  P_002  pch_cnt          4


In [11]: data_melt.index

Out[11]: RangeIndex(start=0, stop=8, step=1)


In [12]: data_melt.columns

Out[12]: Index(['cust_ID', 'prd_CD', 'pch_CD', 'pch_value'], dtype='object')

 


# (c) pd.pivot_table()

In [13]: data_melt_pivot = pd.pivot_table(data_melt, index=['cust_ID', 'prd_CD'],

    ...: columns='pch_CD', values='pch_value',

    ...: aggfunc=np.mean)


In [14]: data_melt_pivot

Out[14]:

pch_CD             pch_amt   pch_cnt
cust_ID prd_CD                 
C_001   P_001       100        1
           P_002       200        2
C_002   P_001       300        3
           P_002       400        4


In [15]: data_melt_pivot.index

Out[15]:

MultiIndex(levels=[['C_001', 'C_002'], ['P_001', 'P_002']],

labels=[[0, 0, 1, 1], [0, 1, 0, 1]],

names=['cust_ID', 'prd_CD'])


In [16]: data_melt_pivot.columns

Out[16]: Index(['pch_amt', 'pch_cnt'], dtype='object', name='pch_CD')

 

 

 

이상으로 Pytho pandas의 melt() 함수를 가지고 데이터 재구조화하는 방법에 대한 소개를 마치겠습니다.

 

다음번 포스팅에서는 wide_to_long() 함수에 대해서 알아보겠습니다.

 

 

 

728x90
반응형
Posted by Rfriend
,

데이터 재구조화(reshaping data)를 위해 사용할 수 있는 Python pandas의 함수들에 대해서 아래의 순서대로 나누어서 소개해보겠습니다.

 

 - (1) pivot(), pd.pivot_table()

 - (2) stack(), unstack()

 - (3) melt()

 - (4) wide_to_long()

 - (4) pd.crosstab() 

 

 

이번 포스팅에서는 두번째로 pd.DataFrame.stack(), pd.DataFrame.unstack()에 대해서 알아보겠습니다.

 


 

stack을 영어사전에서 찾아보면 뜻이

stack[stӕk]

~ (sth) (up) (깔끔하게 정돈하여) 쌓다[포개다]; 쌓이다, 포개지다
~ sth (with sth) (어떤 곳에 물건을 쌓아서) 채우다

 

라는 뜻입니다.

 

stack이 (위에서 아래로 길게, 높게) 쌓는 것이면, unstack은 쌓은 것을 옆으로 늘어놓는것(왼쪽에서 오른쪽으로 넓게) 라고 연상이 될 것입니다.

 

Python pandas의 stack(), unstack() 실습에 필요한 모듈을 불러오고, 예제로 사용할 hierarchical index를 가진 DataFrame을 만들어보겠습니다.  

 

 

 

In [1]: import numpy as np


In [2]: import pandas as pd


In [3]: from pandas import DataFrame


In [4]: mul_index = pd.MultiIndex.from_tuples([('cust_1', '2015'), ('cust_1', '2016'),

   ...: ('cust_2', '2015'), ('cust_2', '2016')])

   ...:


In [5]: data = DataFrame(data=np.arange(16).reshape(4, 4),

   ...: index=mul_index,

   ...: columns=['prd_1', 'prd_2', 'prd_3', 'prd_4'],

   ...: dtype='int')

   ...:


In [6]: data

Out[6]:

                 prd_1  prd_2  prd_3  prd_4
cust_1 2015      0       1      2      3
         2016      4       5      6      7
cust_2 2015      8       9     10     11
         2016     12     13     14     15


 

 

 

 

 

stack() method 를 사용해서 위의 예제 데이터셋을 위에서 아래로 길게(높게) 쌓아(stack) 보겠습니다.  칼럼의 level은 1개 밖에 없으므로 stack(level=-1) 을 별도로 명기하지 않아도 됩니다.

 

 

  (1) pd.DataFrame.stack(level=-1, dropna=True)

 

DataFrame을 stack() 후에 index를 확인해보고, indexing 해보겠습니다.

DataFrame을 stack() 하면 Series 를 반환합니다.

 

 

# stack()

In [7]: data_stacked = data.stack()

 

# DataFrame.stack() => returns Series

In [8]: data_stacked

Out[8]:

cust_1  2015  prd_1     0
                  prd_2     1
                  prd_3     2
                  prd_4     3
          2016  prd_1     4
                  prd_2     5
                  prd_3     6
                  prd_4     7
cust_2  2015  prd_1     8
                  prd_2     9
                  prd_3    10
                  prd_4    11
          2016  prd_1    12
                  prd_2    13
                  prd_3    14
                  prd_4    15

dtype: int32

 


# MultiIndex(levels) after stack()

In [9]: data_stacked.index

Out[9]:

MultiIndex(levels=[['cust_1', 'cust_2'], ['2015', '2016'], ['prd_1', 'prd_2', 'prd_3', 'prd_4']],

labels=[[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1], [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]])

 


# indexing

In [10]: data_stacked['cust_2']['2015'][['prd_1', 'prd_2']]

Out[10]:

prd_1 8

prd_2 9

dtype: int32

 

 

 

 

결측값이 있는 데이터셋을 stack() 할 때 결측값을 제거할지(dropna=True), 아니면 결측값을 NaN으로 유지할지(dropna=False) 설정할 수 있는 stack(dropna=True, False)를 예를 들어 설명해보겠습니다.

 

 

# # putting NaN to DataFrame

In [11]: data.ix['cust_2', 'prd_4'] = np.nan


In [12]: data

Out[12]:

             prd_1  prd_2  prd_3  prd_4
cust_1 2015      0      1      2    3.0
         2016      4      5      6    7.0
cust_2 2015      8      9     10    NaN
         2016     12     13     14    NaN

 


# stack with 'dropna=False' argument

In [13]: data.stack(dropna=False)

Out[13]:

cust_1  2015  prd_1     0.0
                  prd_2     1.0
                  prd_3     2.0
                  prd_4     3.0
          2016  prd_1     4.0
                  prd_2     5.0
                  prd_3     6.0
                  prd_4     7.0
cust_2  2015  prd_1     8.0
                  prd_2     9.0
                  prd_3    10.0
                  prd_4     NaN
          2016  prd_1    12.0
                  prd_2    13.0
                  prd_3    14.0
                  prd_4     NaN

dtype: float64

 


# stack with 'dropna=True' argument

In [14]: data.stack(dropna=True) # by default

Out[14]:

cust_1  2015  prd_1     0.0
                  prd_2     1.0
                  prd_3     2.0
                  prd_4     3.0
          2016  prd_1     4.0
                  prd_2     5.0
                  prd_3     6.0
                  prd_4     7.0
cust_2  2015  prd_1     8.0
                  prd_2     9.0
                  prd_3    10.0
          2016  prd_1    12.0
                  prd_2    13.0
                  prd_3    14.0

dtype: float64

 

 

 

 

 

stack()으로 위에서 아래로 길게(높게) 쌓아 올린 데이터셋을 이번에는 거꾸로 왼쪽으로 오른쪽으로 넓게 unstack()으로 풀어보겠습니다. 

 

stack() 후의 data_stacked 데이터셋이 아래에 보는 것처럼 level이 3개 있는 MultiIndex 입니다. 이럴 경우 unstack(level=-1), unstack(level=0), unstack(level=1) 별로 어떤 level이 칼럼으로 이동해서 unstack() 되는지 유심히 살펴보시기 바랍니다. 

 

  (2) pd.DataFrame.unstack(level=-1, fill_value=None)

 

 

In [15]: data_stacked

Out[15]:

cust_1  2015  prd_1     0
                  prd_2     1
                  prd_3     2
                  prd_4     3
          2016  prd_1     4
                  prd_2     5
                  prd_3     6
                  prd_4     7
cust_2  2015  prd_1     8
                  prd_2     9
                  prd_3    10
                  prd_4    11
          2016  prd_1    12
                  prd_2    13
                  prd_3    14
                  prd_4    15

dtype: int32


In [16]: data_stacked.unstack(level=-1)

Out[16]:

                 prd_1  prd_2  prd_3  prd_4
cust_1 2015      0      1      2      3
         2016      4      5      6      7
cust_2 2015      8      9     10     11
         2016     12     13     14     15


In [17]: data_stacked.unstack(level=0)

Out[17]:

                cust_1  cust_2
2015 prd_1       0       8
       prd_2       1       9
       prd_3       2      10
       prd_4       3      11
2016 prd_1       4      12
       prd_2       5      13
       prd_3       6      14
       prd_4       7      15

 

In [18]: data_stacked.unstack(level=1)

Out[18]:

                  2015  2016
cust_1 prd_1     0     4
         prd_2     1     5
         prd_3     2     6
         prd_4     3     7
cust_2 prd_1     8    12
         prd_2     9    13
         prd_3    10    14
         prd_4    11    15

 

 

 

 

unstack() 한 후의 데이터셋도 역시 Series 인데요, 이것을 DataFrame으로 변환해보겠습니다.

 

 

# converting Series to DataFrame

In [19]: data_stacked_unstacked = data_stacked.unstack(level=-1)


In [20]: data_stacked_unstacked

Out[20]:

                prd_1  prd_2  prd_3  prd_4
cust_1 2015      0      1      2      3
         2016      4      5      6      7
cust_2 2015      8      9     10     11
         2016     12     13     14     15

 

# converting index to columns

In [21]: data_stacked_unstacked_df = data_stacked_unstacked.reset_index()

 

# changing columns' name

In [22]: data_stacked_unstacked_df.rename(columns={'level_0' : 'custID',

    ...: 'level_1' : 'year'}, inplace=True)

    ...:


In [23]: data_stacked_unstacked_df

Out[23]:

    custID  year  prd_1  prd_2  prd_3  prd_4
0  cust_1  2015      0      1      2      3
1  cust_1  2016      4      5      6      7
2  cust_2  2015      8      9     10     11
3  cust_2  2016     12     13     14     15

 

 

 

 

이상으로 stack(), unstack()을 이용한 데이터 재구조화에 대해서 알아보았습니다.  

 

다음번 포스팅에서는 melt(), wide_to_long() 을 이용한 데이터 재구조화를 소개하겠습니다.

 

많은 도움 되었기를 바랍니다.

 

 

728x90
반응형
Posted by Rfriend
,

분석을 하다 보면 원본 데이터의 구조가 분석 기법에 맞지 않아서 행과 열의 위치를 바꾼다거나, 특정 요인에 따라 집계를 해서 구조를 바꿔주어야 하는 경우가 있습니다.

 

이번 포스팅부터는 이처럼 데이터 재구조화(reshaping data)를 위해 사용할 수 있는 Python pandas의 함수들에 대해서 아래의 순서대로 나누어서 소개해보겠습니다.

 

 - (1) pivot(), pd.pivot_table()

 - (2) stack(), unstack()

 - (3) melt()

 - (4) wide_to_long()

 - (5) pd.crosstab() 

 

 

이번 포스팅에서는 첫번째로 data.pivot(), pd.pivot_table(data)에 대해서 알아보겠습니다.

 

 

 

 

먼저, 필요한 모듈을 불러오고, 간단한 예제 데이터셋을 만들어보겠습니다.  고객ID(cust_id), 상품 코드(prod_cd), 등급(grade), 구매금액(pch_amt) 의 4개 변수로 이루어진 데이터 프레임입니다.

 

 

# importing libraries

 

In [1]: import numpy as np


In [2]: import pandas as pd


In [3]: from pandas import DataFrame

 

 

# making an example DataFrame

In [4]: data = DataFrame({'cust_id': ['c1', 'c1', 'c1', 'c2', 'c2', 'c2', 'c3', 'c3', 'c3'],

   ...: 'prod_cd': ['p1', 'p2', 'p3', 'p1', 'p2', 'p3', 'p1', 'p2', 'p3'],

   ...: 'grade' : ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B'],

   ...: 'pch_amt': [30, 10, 0, 40, 15, 30, 0, 0, 10]})

   ...:


In [5]: data

Out[5]:

  cust_id grade  pch_amt prod_cd
0      c1     A       30      p1
1      c1     A       10      p2
2      c1     A        0      p3
3      c2     A       40      p1
4      c2     A       15      p2
5      c2     A       30      p3
6      c3     B        0      p1
7      c3     B        0      p2
8      c3     B       10      p3

 

 

 

 

위의 data 예제처럼 위에서 아래로 길게 늘어서 있는 데이터셋을 행(row)에는 고객ID(cust_id), 열(column)에는 상품코드(prd_cd), 행과 열이 교차하는 칸에는 구매금액(pch_amt)이 위치하도록 데이터를 구조를 바꿔보겠습니다.  말로 설명해도 이해가 잘 안될 수 있는데요, 아래 data.pivot(index, columns, values) 예시를 보시지요.

 

 

  (1) 데이터 재구조화 : data.pivot(index, columns, values)

 

 

# reshaping DataFrame by pivoting

 

In [6]: data_pivot = data.pivot(index='cust_id', columns='prod_cd', values='pch_amt')


In [7]: data_pivot

Out[7]:

prod_cd  p1  p2  p3
cust_id           
c1       30  10   0
c2       40  15  30
c3        0   0  10

 

 

 

 

 

  (2) 데이터 재구조화 : pd.pivot_table(data, index, columns, values, aggfunc)

 

위의 data.pivot() 과 동일한 결과가 나오도록 데이터를 재구조화하는데 pd.pivot_table()을 사용할 수도 있습니다.

 

 

# pd.pivot_table(data, index, columns, values, aggfunc)

 

In [8]: pd.pivot_table(data, index='cust_id', columns='prod_cd', values='pch_amt')

Out[8]:

prod_cd  p1  p2  p3
cust_id           
c1       30  10   0
c2       40  15  30
c3        0   0  10

 

 

 

 

data.pivot() 로 하면 에러가 나서 안되고, pivot_table(data) 을 사용해야만 하는 경우가 몇 가지 있습니다.  그러므로 여러가지 외우는거 싫고, 헷갈리는거 싫어하는 분이라면 pivot_table() 사용법만 잘 숙지하는 것도 좋은 방법입니다.

 

아래에 pivot()으로는 안되고 pivot_table()은 되는 경우를 나란히 이어서 제시해보겠습니다.

 

(a) index 가 2개 이상인 경우입니다.

 

 

# pivot() with 2 indices :ValueError

 

In [9]: data.pivot(index=['cust_id', 'grade'], columns='prod_cd', values='pch_amt')

ValueError: Wrong number of items passed 9, placement implies 2

 

 

# pd.pivot_table() with 2 indices : works well!

 

In [10]: pd.pivot_table(data, index=['cust_id', 'grade'], columns='prod_cd', values='pch_amt')

Out[10]:

prod_cd        p1  p2  p3
cust_id grade           
c1      A      30  10   0
c2      A      40  15  30
c3      B       0   0  10

 

 

 

 

(b) columns 가 2개 이상인 경우 입니다.

 

 

# pivot() with 2 columns : KeyError

 

In [11]: data.pivot(index='cust_id', columns=['grade', 'prod_cd'], values='pch_amt')

KeyError: 'Level grade not found'

 

 

# pd.pivot_table() with 2 columns : works well!

 

In [12]: pd.pivot_table(data, index='cust_id', columns=['grade', 'prod_cd'], values='pch_amt')

Out[12]:

grade A B

grade       A                B          
prod_cd    p1    p2    p3   p1   p2    p3
cust_id                                 
c1       30.0  10.0   0.0  NaN  NaN   NaN
c2       40.0  15.0  30.0  NaN  NaN   NaN
c3        NaN   NaN   NaN  0.0  0.0  10.0

 

 

 

 

pivot() 함수는 중복값이 있을 경우 ValueError를 반환합니다.  반면에, pd.pivot_table()은 aggfunc=np.sum 혹은 aggfunc=np.mean 과 같이 집계(aggregation)할 수 있는 함수를 제공함에 따라 index 중복값이 있는 경우에도 문제가 없습니다.

 

 

# pivot() with index which contains duplicate entries: ValueError

In [13]: data.pivot(index='grade', columns='prod_cd', values='pch_amt')

ValueError: Index contains duplicate entries, cannot reshape

 

 

# pd.pivot_table() with aggfunc : works well!

 

In [14]: pd.pivot_table(data, index='grade', columns='prod_cd',

    ...: values='pch_amt', aggfunc=np.sum)

Out[14]:

prod_cd  p1  p2  p3
grade             
A        70  25  30
B         0   0  10

 

In [15]: pd.pivot_table(data, index='grade', columns='prod_cd',

    ...: values='pch_amt', aggfunc=np.mean)

Out[15]:

prod_cd    p1    p2    p3
grade                   
A        35.0  12.5  15.0
B         0.0   0.0  10.0


# pivot_table(aggfunc=np.mean), by default

In [16]: pd.pivot_table(data, index='grade', columns='prod_cd', values='pch_amt')

Out[16]:

prod_cd    p1    p2    p3
grade                   
A        35.0  12.5  15.0
B         0.0   0.0  10.0

 

 

 

 

pd.pivot_table()은 margins=True 옵션을 설정해주면 행과 열을 기준으로 합계(All, row sum, column sum)를 같이 제시해주기 때문에 꽤 편리합니다

 

 

# pd.pivot_table : margins=True
  # special All columns and rows will be added with partial group aggregates
  # across the categories on the rows and columns

 

In [17]: pd.pivot_table(data, index='grade', columns='prod_cd',

    ...: values='pch_amt', aggfunc=np.sum, margins=True)

Out[17]:

prod_cd    p1    p2    p3    All
grade                          
A        70.0  25.0  30.0  125.0
B         0.0   0.0  10.0   10.0
All      70.0  25.0  40.0  135.0

 

In [18]: pd.pivot_table(data, index='grade', columns='prod_cd',

    ...: values='pch_amt', aggfunc=np.mean, margins=True)

Out[18]:

prod_cd         p1         p2         p3        All
grade                                             
A        35.000000  12.500000  15.000000  20.833333
B         0.000000   0.000000  10.000000   3.333333
All      23.333333   8.333333  13.333333  15.000000

 

 

이상으로 data.pivot(), pd.povit_table(data)를 활용한 데이터 재구조화 소개를 마치겠습니다.

 

다음번 포스팅에서는 stack(), unstack()을 이용한 데이터 재구조화에 대해서 알아보겠습니다.

 

많은 도움 되었기를 바랍니다.

 

 

 

728x90
반응형
Posted by Rfriend
,

DB에 들어있는 데이터를 내렸을 때, 설문조사 데이터를 받았을 때, 원천 거래 데이터를 받았을 때, 혹은 분석 과정 중의 데이터 셋이 분석가가 하고자 하는 통계분석, 데이터마이닝 분석이나 ggplot2 등의 그래프/시각화를 위해 필요한 데이터 구조로 딱 맞아떨어지지 않는 경우가 굉장히 많습니다.

 

이때 필요한 것이 데이터를 분석 목적, 기법에 맞게 자유자재로 변형하여 재구조화하는 일입니다.  

 

엑셀에서 Pilvot Table 생성하는 것이 제일 이해하기 쉬운 예가 될거 같습니다.  세로로 길게 늘어서 있는 원 데이터를 가로와 세로로 틀을 잡아 주고, 가운데에는 value 값에 대해서 개수를 센다든지, 합계를 낸다든지, 평균을 구한다든지 하는 함수를 적용하는 것을 해보셨을 겁니다. 

 

비유를 해보자면, 레고블록으로 높이 세워진 탑을 모양과 색깔별로 레고 블록을 잘 분해한 다음에, 이를 재료로 해서 가로와 세로로 넓게 펴고 높이는 낮추어서 새로운 탑을 만드는 것이라고 생각해보는 것도 이해하는데 도움이 될거 같습니다.

 

이때 데이터 재구조화하는 기준 변수를 무엇으로 하느냐, 가로로 길게 늘어뜨릴 변수는 또 무엇으로 하느냐, 가운데에 값을 볼 때 무슨 함수를 사용해서 값을 계산해서 볼 것이냐에 따라서 다양한 경우의 수가 발생하게 됨에 따라 말로 모든 것을 설명하는 것이 무리가 있습니다.  따라서 아래에 reshape 패키지의 melt(), cast() 함수를 몇 가지 경우의 수에 적용해 보면서 원래 값이 이후에 어떻게 바뀌는지를 유심히 보시고, 필요한 상황에 맞는 R script를 참고하시면 되겠습니다.

 

 

[ reshape 패키지 내 melt()함수, cast() 함수 사용 데이터 재구조화 예시 ]

 

 

 

 

예제로 사용할 데이터는 MASS 패키지에 내장되어 있는 Cars93 데이터 프레임의 차종(Type), 제조국(Origin), 도시 연비(MPG.city), 고속도로 연비(MPG.highway) 4개의 변수를 사용해서 몇가지의 경우의 수를 조합해 가면서 데이터를 녹였다가 (melt) 재구조화 (cast) 를 해보겠습니다.

 

> library(MASS) > str(Cars93) 'data.frame': 93 obs. of 27 variables: $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ... $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ... $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ... $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ... $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ... $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ... $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ... $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ... $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ... $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ... $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ... $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ... $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ... $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ... $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ... $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ... $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ... $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ... $ Length : int 177 195 180 193 186 189 200 216 198 206 ... $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ... $ Width : int 68 71 67 70 69 69 74 78 73 73 ... $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ... $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ... $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ... $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ... $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ... $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...

 

 

데이터 양이 너무 많으면 melt(), cast()를 적용했을 때 데이터 구조가 변화하는 모양을 보기가 쉽지 않기 때문에 차종(Type) 중에서 개수가 적은 "Compact"와 "Van" 만 선별해서 예제로 사용하겠습니다.

 

 

> table(Cars93$Type)

Compact   Large Midsize   Small  Sporty     Van 
     16      11      22      21      14       9 
> 
> Cars93_sample <- subset(Cars93, 
+                         select = c(Type, Origin, MPG.city, MPG.highway), 
+                         subset = (Type %in% c("Compact", "Van"))) 
> 
> Cars93_sample
      Type  Origin MPG.city MPG.highway
3  Compact non-USA       20          26
12 Compact     USA       25          36
13 Compact     USA       25          34
16     Van     USA       18          23
17     Van     USA       15          20
21 Compact     USA       23          28
25 Compact     USA       22          27
26     Van     USA       17          21
33 Compact     USA       22          27
36     Van     USA       15          20
43 Compact non-USA       24          31
55 Compact non-USA       26          34
56     Van non-USA       18          24
58 Compact non-USA       20          29
65 Compact non-USA       24          30
66     Van non-USA       17          23
68 Compact     USA       24          31
70     Van     USA       18          23
74 Compact     USA       23          31
78 Compact non-USA       20          26
82 Compact non-USA       23          30
87     Van non-USA       18          22
89     Van non-USA       17          21
90 Compact non-USA       21          30
92 Compact non-USA       21          28
> dim(Cars93_sample)
[1] 25  4

 

 



  reshape 패키지의 melt(), cast() 함수를 이용한 데이터 재구조화


 

R의 reshape 패키지는 별도의 설치가 필요합니다. 

 

# reshape package installation, library
> install.packages("reshape")

> library(reshape)

 

 

 

이제 melt(data, id.vars, measure.vars) 함수를 사용해서 기존 데이터셋을 녹여보도록 하겠습니다. 

 

> # melt()
> Cars93_sample_melt <- melt(data = Cars93_sample, 
+                            id.vars = c("Type", "Origin"), 
+                            measure.vars = c("MPG.city", "MPG.highway"))
> 
> Cars93_sample_melt
      Type  Origin    variable value
1  Compact non-USA    MPG.city    20
2  Compact     USA    MPG.city    25
3  Compact     USA    MPG.city    25
4      Van     USA    MPG.city    18
5      Van     USA    MPG.city    15
6  Compact     USA    MPG.city    23
7  Compact     USA    MPG.city    22
8      Van     USA    MPG.city    17
9  Compact     USA    MPG.city    22
10     Van     USA    MPG.city    15
11 Compact non-USA    MPG.city    24
12 Compact non-USA    MPG.city    26
13     Van non-USA    MPG.city    18
14 Compact non-USA    MPG.city    20
15 Compact non-USA    MPG.city    24
16     Van non-USA    MPG.city    17
17 Compact     USA    MPG.city    24
18     Van     USA    MPG.city    18
19 Compact     USA    MPG.city    23
20 Compact non-USA    MPG.city    20
21 Compact non-USA    MPG.city    23
22     Van non-USA    MPG.city    18
23     Van non-USA    MPG.city    17
24 Compact non-USA    MPG.city    21
25 Compact non-USA    MPG.city    21
26 Compact non-USA MPG.highway    26
27 Compact     USA MPG.highway    36
28 Compact     USA MPG.highway    34
29     Van     USA MPG.highway    23
30     Van     USA MPG.highway    20
31 Compact     USA MPG.highway    28
32 Compact     USA MPG.highway    27
33     Van     USA MPG.highway    21
34 Compact     USA MPG.highway    27
35     Van     USA MPG.highway    20
36 Compact non-USA MPG.highway    31
37 Compact non-USA MPG.highway    34
38     Van non-USA MPG.highway    24
39 Compact non-USA MPG.highway    29
40 Compact non-USA MPG.highway    30
41     Van non-USA MPG.highway    23
42 Compact     USA MPG.highway    31
43     Van     USA MPG.highway    23
44 Compact     USA MPG.highway    31
45 Compact non-USA MPG.highway    26
46 Compact non-USA MPG.highway    30
47     Van non-USA MPG.highway    22
48     Van non-USA MPG.highway    21
49 Compact non-USA MPG.highway    30
50 Compact non-USA MPG.highway    28
> dim(Cars93_sample_melt)
[1] 50  4

 

 

 

 

이렇게 melt()함수를 사용해 녹인 데이터를 cast()함수를 사용해서 재구조화 해보겠습니다.  세로와 가로에 무슨 변수를 넣을지가 결정되었다면 아래의 예제를 참고해서 원하는 구조에 맞게 R script를 작성하시면 되겠습니다. (말로 설명하기가 쉽지가 않습니다 ^^;) function 란에는 R에서 사용할 수 있는 통계량 함수를 사용하면 되며, 이번 예제에서는 평균(mean) 함수를 사용하였습니다. 

 

> # cast()
> options(digits=3) # 소숫점 너무 밑에 까지 나오지 않도록 설정
> 
> # 한개의 id.var 기준(세로) & variable(가로) 조합의 value 값에 mean 함수 적용
> cast(data = Cars93_sample_melt, Type ~ variable, fun = mean)
     Type MPG.city MPG.highway
1 Compact     22.7        29.9
2     Van     17.0        21.9 
> cast(data = Cars93_sample_melt, Origin ~ variable, fun = mean)
   Origin MPG.city MPG.highway
1     USA     20.6        26.8
2 non-USA     20.7        27.2
> 

 > # 두개의 id.var 기준(세로) & variable(가로) 조합의 value 값에 mean 함수 적용

> cast(data = Cars93_sample_melt, Type + Origin ~ variable, fun = mean)
     Type  Origin MPG.city MPG.highway
1 Compact     USA     23.4        30.6
2 Compact non-USA     22.1        29.3
3     Van     USA     16.6        21.4
4     Van non-USA     17.5        22.5
> 

 > # 한개의 id.var 기준(세로) & 다른 id.var + variable (가로) 조합의 value 값에 mean 함수 적용

> cast(data = Cars93_sample_melt, Type ~ Origin + variable, fun = mean)
     Type USA_MPG.city USA_MPG.highway non-USA_MPG.city non-USA_MPG.highway
1 Compact         23.4            30.6             22.1                29.3
2     Van         16.6            21.4             17.5                22.5
> cast(data = Cars93_sample_melt, Origin ~ Type + variable, fun = mean)
   Origin Compact_MPG.city Compact_MPG.highway Van_MPG.city Van_MPG.highway
1     USA             23.4                30.6         16.6            21.4
2 non-USA             22.1                29.3         17.5            22.5
> 

 > # 한개의 id.var + variable (세로) & 다른 id.var (가로) 조합의 value 값에 mean 함수 적용

> cast(data = Cars93_sample_melt, Type + variable ~ Origin, fun = mean)
     Type    variable  USA non-USA
1 Compact    MPG.city 23.4    22.1
2 Compact MPG.highway 30.6    29.3
3     Van    MPG.city 16.6    17.5
4     Van MPG.highway 21.4    22.5

 > cast(data = Cars93_sample_melt, Origin + variable ~ Type, fun = mean)

   Origin    variable Compact  Van
1     USA    MPG.city    23.4 16.6
2     USA MPG.highway    30.6 21.4
3 non-USA    MPG.city    22.1 17.5
4 non-USA MPG.highway    29.3 22.5

 

 

 


  reshape2 패키지의 acast() 함수를 이용한 데이터 재구조화



> library(gcookbook)

> data(cabbage_exp)

> cabbage_exp

  Cultivar Date Weight        sd  n         se

1      c39  d16   3.18 0.9566144 10 0.30250803

2      c39  d20   2.80 0.2788867 10 0.08819171

3      c39  d21   2.74 0.9834181 10 0.31098410

4      c52  d16   2.26 0.4452215 10 0.14079141

5      c52  d20   3.11 0.7908505 10 0.25008887

6      c52  d21   1.47 0.2110819 10 0.06674995

> #install.packages("reshape2")

> library(reshape2)

> cabbage_exp_cast <- acast(data = cabbage_exp

+                           , Cultivar ~ Date

+                           , value.var = 'Weight'

+                           , fun.aggregate = mean

+                           #, fill = 0 # if you want to fill the missing value with '0'

+                           , drop = TRUE)

> cabbage_exp_cast

     d16  d20  d21

c39 3.18 2.80 2.74

c52 2.26 3.11 1.47

 




  tidyverse 패키지의 spread() 함수를 이용한 데이터 재구조화



> # load data

> library(gcookbook)

> data(cabbage_exp)

> cabbage_exp

  Cultivar Date Weight        sd  n         se

1      c39  d16   3.18 0.9566144 10 0.30250803

2      c39  d20   2.80 0.2788867 10 0.08819171

3      c39  d21   2.74 0.9834181 10 0.31098410

4      c52  d16   2.26 0.4452215 10 0.14079141

5      c52  d20   3.11 0.7908505 10 0.25008887

6      c52  d21   1.47 0.2110819 10 0.06674995

> # reshape data using tidyverse

> library(tidyverse)

> # melt first (long format)

> cabbage_exp_melt <- cabbage_exp %>% 

+   group_by(Cultivar, Date) %>%

+   summarise(Weight = mean(Weight))

> # cast second (wide format)

> cabbage_exp_cast <- spread(cabbage_exp_melt, key = c("Date"), value = "Weight", fill = 0)

> print(cabbage_exp_cast)

# A tibble: 2 x 4

# Groups:   Cultivar [2]

  Cultivar   d16   d20   d21

  <fct>    <dbl> <dbl> <dbl>

1 c39       3.18  2.8   2.74

2 c52       2.26  3.11  1.47

 


 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

728x90
반응형
Posted by Rfriend
,