이번 포스팅에서는 Python pandas 의 Series, DataFrame의 행(row)과 열(column)에 대해서

 

 - 생성 (creation)

 - 선택 (selection, slicing and indexing)

 - 삭제 (drop, delete)

 

하는 방법에 대해서 알아보겠습니다.

 

외부 데이터셋을 불러오거나 직접 만든 다음에 데이터 전처리하는데 있어 수시로 사용하는 가장 기본이 되는 데이터 조작 기법이 행, 열 생성, 선택, 삭제입니다.

 

그동안의 포스팅을 따라해보신 분이라면 이미 많이 익숙해졌을 텐데요, 체계적으로 정리도 해보고, 복습도 해볼 겸 예를 들어서 설명해보겠습니다.

 

 

 

 

 

  (1) Series 생성 및 Series 원소 선택 (element selection, indexing)

 

pd.Series() 를 써서 별도의 index label 이 없는 간단한 Series 를 만들어 보겠습니다.

(index는 0, 1, 2, ... 정수가 자동 부여됨)

 

 

# importing library

In [1]: import numpy as np


In [2]: import pandas as pd

 


# pd.Series with ndarrary data

In [3]: Seri = pd.Series([0., 1., 2., 3., 4.])


In [4]: Seri

Out[4]:

0    0.0
1    1.0
2    2.0
3    3.0
4    4.0
dtype: float64

 

 

 

 

이제 Series의 index 위치나 조건을 가지고 indexing 을 해보겠습니다.

 

 

# Slicing pd.Series like ndarray-like

In [5]: Seri[0]

Out[5]: 0.0


In [6]: Seri[:3]

Out[6]:

0    0.0
1    1.0
2    2.0
dtype: float64


In [7]: Seri[Seri >= Seri.mean()]

Out[7]:

2    2.0
3    3.0
4    4.0
dtype: float64


In [8]: Seri[[4, 2, 0]]

Out[8]:

4    4.0
2    2.0
0    0.0
dtype: float64

 

 

 

 

다음으로, index에 label을 할당해준 Series를 만들어보고, 특정 index label을 지정해서 indexing을 해보겠습니다.

 

 

# pd.Series with index name passed

In [9]: Seri_ix = pd.Series([0., 1., 2., 3., 4.], index=['a', 'b', 'c', 'd', 'e'])


In [10]: Seri_ix

Out[10]:

a    0.0
b    1.0
c    2.0
d    3.0
e    4.0
dtype: float64

 


# Slicing with index label

In [11]: Seri_ix[['a', 'b', 'e']]

Out[11]:

a    0.0
b    1.0
e    4.0
dtype: float64


In [12]: Seri_ix.get(['a', 'b', 'e']) # get() method

Out[12]:

a    0.0
b    1.0
e    4.0
dtype: float64

 

 

 

 

특정 index label 을 지정해서 값(value)을 할당해보겠습니다.

 

 

# set values by index label
In [13]: Seri_ix['a'] = 100


In [14]: Seri_ix

Out[14]:

a    100.0
b      1.0
c      2.0
d      3.0
e      4.0
dtype: float64

 

 

 

 

특정 index label 이 Series에 들어있는지 아닌지 확인 (boolean True, False) 해보겠습니다.

 

 

# check index label whether it is or is'not in Series

In [15]: 'a' in Seri_ix

Out[15]: True


In [16]: 'x' in Seri_ix

Out[16]: False

 

 

 

 

 

  (2) DataFrame 행과 열 생성, 선택, 삭제 (creation, selection, drop of row and column)

 

예제로 사용할 간단한 DataFrame을 dict 로 칼럼과 값을 매핑하고, index 를 지정해서 만들어보겠습니다. DataFrame.index 로 index 확인, DataFrame.columns 로 칼럼 확인할 수 있습니다.

 

 

# importing library and making an example DataFrame

In [17]: from pandas import DataFrame


In [18]: df = DataFrame({'C1': [0., 1., 2., 3.],

    ...: 'C2': [4., 5., 6., 7.],

    ...: 'C3': [8., 9., 10., np.nan]},

    ...: index=['R1', 'R2', 'R3', 'R4'])

    ...:


In [19]: df

Out[19]:

     C1   C2    C3
R1  0.0  4.0   8.0
R2  1.0  5.0   9.0
R3  2.0  6.0  10.0
R4  3.0  7.0   NaN

 


# the row and column labels

In [20]: df.index # row labels

Out[20]: Index(['R1', 'R2', 'R3', 'R4'], dtype='object')


In [21]: df.columns # column labels

Out[21]: Index(['C1', 'C2', 'C3'], dtype='object')

 

 

 

 

df_2 = DataFrame(df_1, index=['xx', 'xx'], columns=['xx', 'xx']) 형식처럼 기존 df_1에서 행과 열을 선별해서 df_2라는 새로운 DataFrame을 만들 수 있습니다.

 

 

In [22]: df_R1R3 = DataFrame(df, index=['R1', 'R3'])


In [23]: df_R1R3

Out[23]:

     C1   C2    C3
R1  0.0  4.0   8.0
R3  2.0  6.0  10.0


In [24]: df_C1C3 = DataFrame(df, columns=['C1', 'C3'])


In [25]: df_C1C3

Out[25]:

     C1    C3
R1  0.0   8.0
R2  1.0   9.0
R3  2.0  10.0
R4  3.0   NaN


In [26]: df_R3R1_C3C1 = DataFrame(df, index=['R3', 'R1'], columns=['C3', 'C1'])


In [27]: df_R3R1_C3C1

Out[27]:

      C3   C1
R3  10.0  2.0
R1   8.0  0.0

 

 

 

 

DataFrame에서 칼럼 이름을 지정해서 선별하는 방법은 아래 예시 처럼 df[['xx', 'xx']] 처럼 하면 됩니다.

 

 

# selecting columns from DataFrame

In [28]: df

Out[28]:

     C1   C2    C3
R1  0.0  4.0   8.0
R2  1.0  5.0   9.0
R3  2.0  6.0  10.0
R4  3.0  7.0   NaN


In [29]: df[['C1', 'C2']]

Out[29]:

     C1   C2
R1  0.0  4.0
R2  1.0  5.0
R3  2.0  6.0
R4  3.0  7.0

 

 

 

 

DataFrame에 새로운 칼럼을 만들기때 (1) df['new_column'] = ... 과 (2) df.assign(new_column = ... ) 의 두가지 방법이 있습니다.

 

 

# (1) making a new column

In [30]: df['C4'] = df['C1'] + df['C2']


In [31]: df

Out[31]:

     C1   C2    C3    C4
R1  0.0  4.0   8.0   4.0
R2  1.0  5.0   9.0   6.0
R3  2.0  6.0  10.0   8.0
R4  3.0  7.0   NaN  10.0

 

 

# (2-1) assign() method

In [32]: df = df.assign(C5 = df['C1']*df['C2'])


In [33]: df

Out[33]:

     C1   C2    C3    C4    C5
R1  0.0  4.0   8.0   4.0   0.0
R2  1.0  5.0   9.0   6.0   5.0
R3  2.0  6.0  10.0   8.0  12.0
R4  3.0  7.0   NaN  10.0  21.0

 

# (2-2) the same with the above

In [34]: df.assign(C5 = lambda x: x.C1*x.C2)

Out[34]:

     C1   C2    C3    C4    C5
R1  0.0  4.0   8.0   4.0   0.0
R2  1.0  5.0   9.0   6.0   5.0
R3  2.0  6.0  10.0   8.0  12.0
R4  3.0  7.0   NaN  10.0  21.0

 

 

 

 

DataFrame의 칼럼을 삭제하는 방법에는 (1) df.drop(['xx', 'xx'], 1) 과 (2) del df['xx'] 의 방법이 있습니다.  del df['xx']은 원본 데이터프레임에서 칼럼을 삭제합니다.

 

 

# drop 'C3' column : DataFrame.drop('Column', 1)

In [35]: df_drop_C4C5 = df.drop(['C4', 'C5'], 1)


In [36]: df_drop_C4C5

Out[36]:

     C1   C2    C3
R1  0.0  4.0   8.0
R2  1.0  5.0   9.0
R3  2.0  6.0  10.0
R4  3.0  7.0   NaN

 

 

# delete a column from original DataFrame : del DataFrame['column']

In [37]: df

Out[37]:

     C1   C2    C3    C4    C5
R1  0.0  4.0   8.0   4.0   0.0
R2  1.0  5.0   9.0   6.0   5.0
R3  2.0  6.0  10.0   8.0  12.0
R4  3.0  7.0   NaN  10.0  21.0

 

In [38]: del df['C4']  # delete 'C4' column from the original DataFrame df directly


In [39]: del df['C5']  # delete 'C5' column from the original DataFrame df directly


In [40]: df

Out[40]:

     C1   C2    C3
R1  0.0  4.0   8.0
R2  1.0  5.0   9.0
R3  2.0  6.0  10.0
R4  3.0  7.0   NaN

 

 

 

 

DataFrame의 행(row)과 열(column)을 선택할 때는 df.['xx'][0:2] 를 예를 들어 소개합니다.

 

 

In [42]: df

Out[42]:

     C1   C2    C3
R1  0.0  4.0   8.0
R2  1.0  5.0   9.0
R3  2.0  6.0  10.0
R4  3.0  7.0   NaN

 


# selecting column form DataFrame

In [43]: df['C1']

Out[43]:

R1    0.0
R2    1.0
R3    2.0
R4    3.0
Name: C1, dtype: float64


In [44]: df.C1

Out[44]:

R1    0.0
R2    1.0
R3    2.0
R4    3.0
Name: C1, dtype: float64

 

# selecting row from DataFrame

In [45]: df[0:2]

Out[45]:

     C1   C2   C3
R1  0.0  4.0  8.0
R2  1.0  5.0  9.0

 

# indexing 'column' and 'row' from DataFrame

In [46]: df['C1'][0:2]

Out[46]:

R1    0.0
R2    1.0
Name: C1, dtype: float64


In [47]: df.C1[0:2]

Out[47]:

R1    0.0
R2    1.0
Name: C1, dtype: float64

 

 

 

 

index label을 가지고 행(row) 선택할 때는 df.loc['xx'] 를 사용합니다.

 

 

# Select row by label : df.loc[label]

In [48]: df.loc['R1']

Out[48]:

C1    0.0
C2    4.0
C3    8.0
Name: R1, dtype: float64


In [49]: df.loc[['R1', 'R2']]

Out[49]:

     C1   C2   C3
R1  0.0  4.0  8.0
R2  1.0  5.0  9.0

 

 

 

 

index의 label 이 아니라 정수(integer)로 indexing을 하려면 df.iloc[int] 를 사용해야 합니다.  만약 df.loc[int]를 사용하면 TypeError 가 발생합니다.

 

 

# TypeError: cannot do label indexing on with these indexers [0] of <class 'int'>

In [50]: df.loc[0] # TypeError

TypeError: cannot do label indexing on <class 'pandas.indexes.base.Index'> with these indexers [0] of <class 'int'>

 

 

# Select row by interger location : df.iloc[loc]

In [51]: df.iloc[0]

Out[51]:

C1    0.0
C2    4.0
C3    8.0
Name: R1, dtype: float64


In [52]: df.iloc[0:2]

Out[52]:

     C1   C2   C3
R1  0.0  4.0  8.0
R2  1.0  5.0  9.0

 

 

 

 

DataFrame의 행(row) indexing할 때 df[0:2] 처럼 행의 범위를 ':'로 설정해주어도 됩니다.  df[0] 처럼 정수값을 지정하면 KeyError 납니다(이때는 df.iloc[0] 을 써야 함).

 

 

# KeyError: 0

In [53]: df[0]  # KeyError: 0

KeyError: 0

 

 

# Select rows : df[0:2]

In [54]: df[0:2]

Out[54]:

     C1   C2   C3
R1  0.0  4.0  8.0
R2  1.0  5.0  9.0

 

 

 

 

조건을 부여해서 열을 선택할 수도 있습니다.

 

 

# Select rows by boolean vector : df[bool_vec]

In [55]: df[df['C1'] <= 1.0]

Out[55]:

     C1   C2   C3
R1  0.0  4.0  8.0
R2  1.0  5.0  9.0

 

 

 

 

 

선택할 칼럼을 벡터 객체로 만들어 놓고, DataFrame에서 벡터 객체에 들어있는 칼럼만 선별해올 수도 있겠지요. 분석 프로세스를 자동화하려고 할 때 선행 분석 결과를 받아서 벡터 객체로 만들어 놓고, 이를 받아서 필요한 변수만 선별할 때 종종 사용하곤 합니다.

 


# Select columns by column vector : df[col_bool_vec]

In [56]: df_col_selector = ['C1', 'C2']


In [57]: df[df_col_selector]

Out[57]:

     C1   C2
R1  0.0  4.0
R2  1.0  5.0
R3  2.0  6.0
R4  3.0  7.0

 

 

 

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

 

 

 

 

 

 

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

이번 포스팅에서는 데이터 프레임, 튜플, 리스트를 특정한 기준에 따라서 정렬, 재배치하는 방법에 대해서 알아보겠습니다.

 

오름차순 혹은 내림차순으로 정렬을 한 후에 상위 n개 (or 하위 n개), 혹은 첫번째 행 (or 마지막 행) 을 선택해야할 필요가 있을 때 사용할 수 있는 method, function 입니다.

 

DataFrame, Tuple, List 정렬 순서대로 소개해겠습니다.

 

 - (1) DataFrame 정렬 : DataFrame.sort_values()

 

 - (2) Tuple 정렬 : sorted(tuple, key)

 

 - (3) List 정렬 : list.sort(), sorted(list)

 

 

 

 

 

  (1) DataFrame 정렬 : DataFrame.sort_values()

 

먼저 필요한 모듈을 불러오고, 예제 DataFrame을 만들어보겠습니다.

 

 

In [1]: import pandas as pd


In [2]: personnel_df = pd.DataFrame({'sequence': [1, 3, 2],

   ...: 'name': ['park', 'lee', 'choi'],

   ...: 'age': [30, 20, 40]})


In [3]: personnel_df

Out[3]:

   age  name  sequence
0   30  park         1
1   20   lee         3
2   40  choi         2

 

 

 

 

(1-1) 'sequence' 열(by='sequence')을 기준으로 index(axis=0) 오름차순 정렬하기

 

 

# sorting index of DataFrame by a specific column : axis=0, columns

In [4]: personnel_df.sort_values(by=['sequence'], axis=0)

Out[4]:

   age  name  sequence
0
   30  park         1
2   40  choi         2
1
   20   lee          3

 

 

 

 

(1-2) 내림차순(descending)으로 정렬하기 : ascending=False

 

 

# sorting index of dataFrame in descending order : ascending=False

In [5]: personnel_df.sort_values(by=['sequence'], axis=0, ascending=False)

Out[5]:

   age  name  sequence
1   20   lee         3
2   40  choi         2
0   30  park         1

 

 

 

 

(1-3) 열 이름을 (알파벳 순서로) 정렬하기 :  axis=1

 

 

# sorting columns of DataFrame : axis=1

In [6]: personnel_df.sort(axis=1)

Out[6]:

   age  name  sequence
0   30  park         1
1   20   lee         3
2   40  choi         2

 

# sorting columns of DataFrame in descending order : axis=1, ascending=False

In [7]: personnel_df.sort(axis=1, ascending=False

Out[7]:

   sequence  name  age
0         1  park   30
1         3   lee   20
2         2  choi   40

 

 

 

 

(1-4) DataFrame 자체 내에서 정렬된 상태로 다시 저장하기 : inplace=True

 

 

In [8]: personnel_df

Out[8]:

age name sequence

0 30 park 1

1 20 lee 3

2 40 choi 2


# sorting DataFarme in-place : inplace=True

In [9]: personnel_df.sort_values(by=['sequence'], axis=0, inplace=True)


In [10]: personnel_df

Out[10]:

age name sequence

0 30 park 1

2 40 choi 2

1 20 lee 3

 

 

 

 

(1-5) 결측값을 처음에(na_position='first'), 혹은 마지막(na_position='last') 위치에 정렬하기

 

 

# putting NaN to DataFrame

In [11]: import numpy as np


In [12]: personnel_df = pd.DataFrame({'sequence': [1, 3, np.nan],

    ...: 'name': ['park', 'lee', 'choi'],

    ...: 'age': [30, 20, 40]})

    ...:


In [13]: personnel_df

Out[13]:

   age  name  sequence
0   30  park       1.0
1   20   lee       3.0
2   40  choi       NaN

 


# first puts NaNs at the beginning : na_position='first'

In [14]: personnel_df.sort_values(by=['sequence'], axis=0, na_position='first')

Out[14]:

   age  name  sequence
2   40  choi       NaN
0   30  park       1.0
1   20   lee       3.0

 


# last puts NaNs at the end : na_position='last'

In [15]: personnel_df.sort_values(by=['sequence'], axis=0, na_position='last')

Out[15]:

   age  name  sequence
0   30  park       1.0
1   20   lee       3.0
2   40  choi       NaN

 

 

 

 

  (2) Tuple 정렬하기 : sorted(tuple, key) method

 

 

# making a tuple

In [16]: personnel_tuple = [(1, 'park', 30),

    ...: (3, 'lee', 20),

    ...: (2, 'choi', 40)]


In [17]: personnel_tuple

Out[17]: [(1, 'park', 30), (3, 'lee', 20), (2, 'choi', 40)]

 


# use 'key' parameter to specify a function to be called on

# sort by sequence number

In [18]: sorted(personnel_tuple, key=lambda personnel: personnel[0])

Out[18]: [(1, 'park', 30), (2, 'choi', 40), (3, 'lee', 20)]


# sort by name

In [19]: sorted(personnel_tuple, key=lambda personnel: personnel[1])

Out[19]: [(2, 'choi', 40), (3, 'lee', 20), (1, 'park', 30)]

 

# sort by age

In [20]: sorted(personnel_tuple, key=lambda personnel: personnel[2])

Out[20]: [(3, 'lee', 20), (1, 'park', 30), (2, 'choi', 40)]

 

 

 

내림차순(descending order)으로 정렬하고 싶으면 'reverse=True' 옵션을 설정해주면 됩니다.

 

 

# sorting tuple in descending order by age : reverse=True

In [21]: sorted(personnel_tuple, reverse=True, key=lambda personnel: personnel[2])

Out[21]: [(2, 'choi', 40), (1, 'park', 30), (3, 'lee', 20)]

 

 

 

 

  (3) List 정렬하기 : sorted(list), or list.sort()

 

 

 

# making a list

In [23]: my_list = [0, 1, 2, 3, 4, 9, 8, 7, 6, 5]

 


# (1) sorting a list : sort(list) function

In [24]: sorted(my_list)

Out[24]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

 

# (2) sorting a list : list.sort() method

In [25]: my_list.sort()


In [26]: my_list

Out[26]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 


# sorting a list in descending order : reverse=True

In [27]: sorted(my_list, reverse=True)

Out[27]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]


In [28]: my_list.sort(reverse=True)


In [29]: my_list

Out[29]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

 

 

 

 

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

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

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

 

재구조화(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이 분석가가 설정할 수 있는 옵션이 조금 더 다양하므로 입맛에 맞게 교차분석도 하고 카이제곱검정도 하고 싶은 분은 아래 링크되어 있는 포스팅을 참고하세요. 

 

 

 

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

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

데이터 재구조화(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() 을 이용한 데이터 재구조화를 소개하겠습니다.

 

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

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

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

 

이번 포스팅부터는 이처럼 데이터 재구조화(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()을 이용한 데이터 재구조화에 대해서 알아보겠습니다.

 

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

 

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

지난번 포스팅에서는 중복값 확인, 중복값 처리에 대해서 알아보았습니다.

 

이번 포스팅에서는 유일한 값(unique value)을 찾고 개수도 세어보기 위해서 Python pandas의

 

 - pd.Series.unique() 를 이용한 유일한 값 찾기

   (Return np.ndarray of unique values in the object)

 

 - pd.Series.value_counts() 를 이용한 유일한 값별 개수 세기

   (Returns object containing counts of unique values)

 

를 소개하겠습니다. 

 

 

 

데이터 전처리 및 탐색적 데이터 분석 단계에서 종종 사용하니 알아두면 좋겠습니다.

 

 

먼저, 필요한 모듈 불러오고, 예제 DataFrame을 만들어보겠습니다.

 

 

In [1]: import pandas as pd


In [2]: import numpy as np


In [3]: df = pd.DataFrame({'A': ['A1', 'A1', 'A2', 'A2', 'A3', 'A3'],

   ...: 'B': ['B1', 'B1', 'B1', 'B1', 'B2', np.nan],

   ...: 'C': [1, 1, 3, 4, 4, 4]})


In [4]: df

Out[4]:

    A    B  C
0  A1   B1  1
1  A1   B1  1
2  A2   B1  3
3  A2   B1  4
4  A3   B2  4
5  A3  NaN  4

 

 

 

 

  (1) 유일한 값 찾기 : pd.Series.unique()

 

pd.Series.unique()는 np.ndarray를 반환합니다. DataFrame의 각 칼럼별로 indexing해서 unique()를 적용한 결과는 아래와 같습니다.

 

'B' 칼럼에 'NaN'도 unique()에 포함되었습니다.

 

 

In [5]: df['A'].unique()

Out[5]: array(['A1', 'A2', 'A3'], dtype=object)


In [6]: df['B'].unique()

Out[6]: array(['B1', 'B2', nan], dtype=object)


In [7]: df['C'].unique()

Out[7]: array([1, 3, 4], dtype=int64)

 

 

 

 

  (2) 유일한 값별로 개수 세기 : pd.Series.value_counts()

 

 

# eturns object containing counts of unique values

pd.Series.value_counts(normalize=False, # False면 개수, True면 상대 비율 구함

                              sort=True, # True면 개수 기준 정렬, False면 유일한 값 기준 정렬

                              ascending=False, # False면 내림차순 정렬, True면 오름차순 정렬

                          bins=None, # None이면 유일값 기준 개수, None아니면 Bins Group별 개수

                              dropna=True # True면 NaN 무시, False면 유일값에 NaN 포함)

 

 

 

 

In [4]: df

Out[4]:

    A    B  C
0  A1   B1  1
1  A1   B1  1
2  A2   B1  3
3  A2   B1  4
4  A3   B2  4
5  A3  NaN  4

 

# returns of pd.Series.value_counts() by default setting

In [8]: df['A'].value_counts()

Out[8]:

A3    2
A2    2
A1    2

Name: A, dtype: int64


In [9]: df['B'].value_counts()

Out[9]:

B1    4
B2    1

Name: B, dtype: int64


In [10]: df['C'].value_counts()

Out[10]:

4    3
1    2
3    1

Name: C, dtype: int64

 

 

 

 

  (2-1) 유일 값 별 상대적 비율 : pd.Series.value_counts(normalize=True)

 

 

In [4]: df

Out[4]:

    A    B   C
0  A1   B1  1
1  A1   B1  1
2  A2   B1  3
3  A2   B1  4
4  A3   B2  4
5  A3  NaN  4

 

In [11]: df['C'].value_counts(normalize=True)

Out[11]:

4    0.500000
1    0.333333
3    0.166667

Name: C, dtype: float64

 

 

 

 

  (2-2) 유일한 값 기준 정렬 : pd.Series.value_counts(sort=True, ascending=True)

 

sort=True, False 와 ascending=True, False 의 조합은 아래 예시의 3가지 경우의 수가 있습니다. 

 

[12] : 유일한 값의 개수 기준 내림차순 정렬 예시 (sort descending order by value_counts)

[13] : 유일한 값의 개수 기준 오름차순 정렬 예시 (sort ascending order by value_counts)

[14] : 유일한 값 기준 오름차순 정렬 예시 (유일한 값의 개수 기준 정렬은 없음)

 

 

In [4]: df

Out[4]:

    A    B   C
0  A1   B1  1
1  A1   B1  1
2  A2   B1  3
3  A2   B1  4
4  A3   B2  4
5  A3  NaN  4

 

# sort descending order by value_counts

In [12]: df['C'].value_counts(sort=True, ascending=False) # by default

Out[12]:

4    3
1    2
3    1

Name: C, dtype: int64

 

# sort ascending order by value_counts

In [13]: df['C'].value_counts(sort=True, ascending=True)

Out[13]:

3    1
1    2
4    3

Name: C, dtype: int64

 

# Don't sort by value_counts, but sort by unique value

In [14]: df['C'].value_counts(sort=False)

Out[14]:

1    2
3    1
4    3

Name: C, dtype: int64

 

 

 

 

  (2-3) 결측값을 유일한 값에 포함할지 여부 : pd.Series.value_counts(dropna=True)

 

 

In [4]: df

Out[4]:

    A    B   C
0  A1   B1  1
1  A1   B1  1
2  A2   B1  3
3  A2   B1  4
4  A3   B2  4
5  A3  NaN  4

 

# dropna=True : Don’t include counts of NaN

In [15]: df['B'].value_counts(dropna=True) # by default

Out[15]:

B1    4
B2    1

Name: B, dtype: int64

 

# dropna=False : Include counts of NaN

In [16]: df['B'].value_counts(dropna=False)

Out[16]:

B1     4
B2     1
NaN    1

Name: B, dtype: int64

 

 

 

 

  (2-4) Bins Group별 값 개수 세기 : pd.Series.value_counts(bins=[ , , ,])

 

 

In [4]: df

Out[4]:

    A    B   C
0  A1   B1  1
1  A1   B1  1
2  A2   B1  3
3  A2   B1  4
4  A3   B2  4
5  A3  NaN  4

 

In [17]: df['C'].value_counts(bins=[0, 1, 2, 3, 4, 5], sort=False)

Out[17]:

0 2

1 0

2 1

3 3

4 0

Name: C, dtype: int64

 

 

 

아래의 pd.cut(Series, bins=[ , , , ]) 와 위의 결과가 동일하며, 위의 Series.value_counts(bins=[ , , , ])가 조금 더 사용하기 편리합니다.

 

 

In [18]: out = pd.cut(df['C'], bins=[0, 1, 2, 3, 4, 5])


In [19]: pd.value_counts(out)

Out[19]:

(3, 4] 3

(0, 1] 2

(2, 3] 1

(4, 5] 0

(1, 2] 0

Name: C, dtype: int64

 

 

 

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

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

데이터를 수집하는 과정 중의 오류, 데이터를 병합하는 과정에서의 오류 등으로 인해서 중복되지 않아야 할 데이터가 중복이 되는 경우가 생길 수 있습니다.

 

특히, unique 한 'key' 값을 관리해야 하는 경우 중복(duplicates)이 발생하면 분석에 심각한 영향을 끼칠 수도 있습니다.  (관계형 DB라면 아례 데이터가 들어가지도 않겠지요...)

 

따라서 본격적인 데이터 분석에 들어가기 전에 반드시, 꼭, 예외없이, 무조건, Seriously 중복 데이터를 확인하고 처리하는 전처리 작업이 필요합니다.  (가령, 두 개의 DataFrame을 Left Join 으로 Merge했더니 row의 개수가 늘어났다 하면 그건 병합하는 DataFrame의 'key' 값에 중복이 있다는 얘기거든요.)

 

데이터 개수가 몇 개 안되면 눈으로 확인하고 중복 데이터 위치 indexing 해서 처리하면 되는데요, 데이터 개수가 많으면 육안으로 일일이 확인한다는게 사실상 불가능해집니다.

 

이때 중복이 존재하는지 확인할 때 사용할 수 있는 것이 Python pandas의 duplicated() method 입니다.  그리고 중복값을 처리하는 것이 drop_duplicates() method 이구요.

 

 - 중복 여부 확인 : DataFrame.duplicated()

 - 중복이 있으면 처음과 마지막 값 중 무엇을 남길 것인가? : keep = 'first', 'last', False

 - 중복값 처리(unique한 1개의 key만 남기고 나머지 중복은 제거) : DataFrame.drop_duplicates()

 

 

 

 

먼저 pandas 모듈을 불러오고, 예제로 사용할 '중복(duplicate entries)'이 있는 DataFrame을 만들어보겠습니다.

 

 

In [1]: import pandas as pd


In [2]: data = {'key1':['a', 'b', 'b', 'c', 'c'],

   ...: 'key2':['v', 'w', 'w', 'x', 'y'],

   ...: 'col':[1, 2, 3, 4, 5]}


In [3]: df = pd.DataFrame(data, columns=['key1','key2','col'])


In [4]: df

Out[4]:

  key1 key2  col
0    a    v    1
1    b    w    2
2    b    w    3
3    c    x    4
4    c    y    5

 

 

 

 

  (1) 중복 데이터가 있는지 확인하기 : DataFrame.duplicated()

 

'key1'을 기준으로 하면 index 1, 2 ('b')가 중복, index 3, 4 ('c')가 서로 중복입니다.

'key1'과 'key2'를 동시에 기준으로 하면 index 1, 2 ('b', 'w') 가 서로 중복입니다.

 

DataFrame.duplicated() method는 True, False 의 boolean 형태의 Series를 반환합니다.

 

 

In [5]: df.duplicated(['key1'])

Out[5]:

0    False
1    False
2     True
3    False
4     True
dtype: bool


In [6]: df.duplicated(['key1', 'key2'])

Out[6]:

0    False
1    False
2     True
3    False
4    False
dtype: bool

 

 

 

 

  (2) 중복이 있으면 처음과 끝 중 무슨 값을 남길 것인가? : keep = 'first', 'last', False

 

keep='first'가 default 이며, 중복값이 있으면 첫번째 값을 duplicated 여부를 False로 반환하고, 나머지 중복값에 대해서는 True를 반환하게 됩니다. keep='last'는 그 반대이겠지요.

 

keep=False는 처음이나 끝값인지 여부는 고려를 안하고 중복이면 무조건 True를 반환합니다 (=> 나중에 drop_duplicates() 에서 keep 할 생각이 없다는 뜻입니다).

 

 

In [7]: df.duplicated(['key1'], keep='first') # by default

Out[7]:

0    False
1    False
2     True
3    False
4     True
dtype: bool


 

In [8]: df.duplicated(['key1'], keep='last')

Out[8]:

0    False
1     True
2    False

3     True
4    False
dtype: bool

 

 

In [9]: df.duplicated(['key1'], keep=False)

Out[9]:

0    False
1     True
2     True
3     True
4     True

dtype: bool

 

 

 

 

  (3) 중복값 처리(unique한 1개의 key만 남기고 나머지 중복은 제거) 

       : DataFrame.drop_duplicates()

 

drop_duplicates() method는 중복값을 keep='first', 'last', False argument에 따라서 unique한 1개의 key값만 남기고 나머지 중복은 제거를 한 후의 DataFrame을 반환합니다.

 

 

# drop duplic

In [10]: df.drop_duplicates(['key1'], keep='first')

Out[10]:

  key1 key2  col
0    a    v    1
1    b    w    2
3    c    x    4


In [11]: df.drop_duplicates(['key1'], keep='last')

Out[11]:

  key1 key2  col
0    a    v    1
2    b    w    3
4    c    y    5


In [12]: df.drop_duplicates(['key1'], keep=False)

Out[12]:

  key1 key2  col
0    a    v    1

 

 

 

이상으로 중복 여부 확인, 중복값 처리를 위한 Python pandas의 DataFrame.duplicated(), DataFrame.drop_duplicates() method에 대한 소개를 마치겠습니다.

 

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

 

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

지난번 포스팅에서는 Python pandas의

 

 - fillna() method를 사용한 결측값 대체

 - dropna() method를 사용한 결측값 있는 행, 열 제거

 - interpolate() method를 사용한 결측값 보간

 

하는 방법을 알아보았습니다.

 

이번 포스팅에서는 Python pandas의 replace() method를 사용해서

 

 - 결측값 혹은 원래의 값을 다른 값으로 교체(replacing generic values)

 - List 를 다른 List로 교체

 - mapping dict 로 교체

 - DataFrame의 특정 칼럼 값 교체

 

하는 방법을 소개하겠습니다.

 

fillna() 를 사용한 결측값 대체와 replace()를 사용한 결측값 교체가 유사한면이 있구요, 다만 replace() 는 결측값 말고도 모든 값을 대상으로 할 수 있고, list, mapping dict 등으로 좀더 유연하고 포괄적으로 사용할 수 있는 장점이 있습니다.

 

 

 

 

replace() method를 가지고 결측값, 실측값 모두를 대상으로 교체(replacement)하는 예를 들어보겠습니다.  필요한 모듈을 importing하고, 결측값을 포함한 간단한 Series 먼저 시작하겠습니다.

 

 

In [1]: import pandas as pd


In [2]: import numpy as np


In [3]: from pandas import DataFrame, Series

 

In [4]: ser = Series([1, 2, 3, 4, np.nan])

 

In [5]: ser

Out[5]:

0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
dtype: float64

 

 

 

 

  (1) 결측값, 실측값을 다른 값으로 교체하기 : replace(old_val, new_val)

 

 

# replacing a value with other value

In [6]: ser.replace(2, 20)

Out[6]:

0     1.0
1    20.0
2     3.0
3     4.0
4     NaN
dtype: float64

 

# replacing missing value(NaN) with other value

In [7]: ser.replace(np.nan, 5)

Out[7]:

0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
dtype: float64

 

 

 

 

 

  (2) list 를 다른 list 값으로 교체하기 : replace([old1, old2, ...], [new1, new2, ...])

 

 

In [8]: ser

Out[8]:

0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
dtype: float64

 

In [9]: ser.replace([1, 2, 3, 4, np.nan], [6, 7, 8, 9, 10])

Out[9]:

0     6.0
1     7.0
2     8.0
3     9.0
4    10.0
dtype: float64

 

 

 

 

  (3) mapping dict 로 원래 값, 교체할 값 매핑 : replace({old1 : new1, old2: new2})

 

 

In [10]: ser

Out[10]:

0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
dtype: float64

 

In [11]: ser.replace({1: 6,

    ...: 2: 7,

    ...: 3: 8,

    ...: 4: 9,

    ...: np.nan: 10})

Out[11]:

0     6.0
1     7.0
2     8.0
3     9.0
4    10.0
dtype: float64

 

 

 

 

  (4) DataFrame의 특정 칼럼 값 교체하기 : df.replace({'col1': old_val}, {'col1': new_val})

 

DataFrame 의 값을 교체하려면 dict { } 안에 원래와 교체할 'column name'과 'value' 를 짝을 이루어서 매핑해주면 됩니다.

 

 

In [13]: df = DataFrame({'C1': ['a_old', 'b', 'c', 'd', 'e'],

    ...: 'C2': [1, 2, 3, 4, 5],

    ...: 'C3': [6, 7, 8, 9, np.nan]})


In [14]: df

Out[14]:

      C1  C2   C3
a_old   1  6.0
1      b   2  7.0
2      c   3  8.0
3      d   4  9.0
4      e   5  NaN

 

In [15]: df.replace({'C1': 'a_old'}, {'C1': 'a_new'})

Out[15]:

      C1  C2   C3
a_new   1  6.0
1      b   2  7.0
2      c   3  8.0
3      d   4  9.0
4      e   5  NaN


In [16]: df.replace({'C3': np.nan}, {'C3': 10})

Out[16]:

      C1  C2    C3
0  a_old   1   6.0
1      b   2   7.0
2      c   3   8.0
3      d   4   9.0
4      e   5  10.0

 

 

 

이상으로 Python pandas의 replace() method를 사용해서 결측값, 원래 값을 다른 값으로 교체하기 소개를 마치겠습니다.

 

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

 

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

지난번 포스팅에서는 Python pandas 의 dropna() method를 사용해서

 

 - 결측값이 들어있는 행 전체 제거하기

 - 결측값이 들어있는 열 전체 제거하기

 

방법을 알아보았습니다.

 

 

이번 포스팅에서는 Python pondas의 interpolate() method를 사용해서 결측값을 보간하는 방법(interpolation of missing values)에 대해서 소개하겠습니다.

 

 - (1) 시계열데이터의 값에 선형으로 비례하는 방식으로 결측값 보간

       (interpolate TimeSeries missing values linearly)

 - (2) 시계열 날짜 index를 기준으로 결측값 보간

       (interploate TimeSeries missing values along with Time-index)

 - (3) DataFrame 값에 선형으로 비례하는 방식으로 결측값 보간

       (interpolate DataFrame missing values linearly)

 - (4) 결측값 보간 개수 제한하기

       (limit the number of consecutive interpolation)

 

시계열 데이터 분석이라든지 이미지 분석 등에서 사용하면 매우 유용하고 편리한 method 입니다.

 

이전 포스팅의 결측값 대체는 '특정의 동일 값'으로 채우는 방식(filling, imputation)이었던 반면에, 이번 포스팅의 결측값 보간(interploation)은 실측값과 실측값 사이의 결측값을 마치 '그라데이션(gradation)' 기법으로 색깔을 조금씩 변화시켜가면서 부드럽게 채워나가는 방법이라고 이해하시면 되겠습니다.

 

 

 

자, 그럼 필요한 모듈을 import 하고, 결측값이 들어있는 TimeSeries 데이터를 만든 후에, 결측값을 보간해보겠습니다.

 

  (1) 시계열데이터의 값에 선형으로 비례하는 방식으로 결측값 보간

       (interpolate TimeSeries missing values linearly) : ts.interpolate()

 

 

# importing modules 

In [1]: import pandas as pd


In [2]: import numpy as np


In [3]: from pandas import DataFrame, Series


In [4]: from datetime import datetime

 

# making TimeSeries with missing values

In [5]: datestrs = ['12/1/2016', '12/03/2016', '12/04/2016', '12/10/2016']


In [6]: dates = pd.to_datetime(datestrs)


In [7]: dates

Out[7]: DatetimeIndex(['2016-12-01', '2016-12-03', '2016-12-04', '2016-12-10'], dtype='datetime64[ns]', freq=None)


In [8]: ts = Series([1, np.nan, np.nan, 10], index=dates)

 

In [9]: ts

Out[9]:

2016-12-01     1.0
2016-12-03     NaN
2016-12-04     NaN

2016-12-10    10.0
dtype: float64

 

 

 

ts.interpolote() 는 default 가 interpolate(method='values')로서 선형으로 비례하여 결측값을 보간해줍니다.

 

 

# interpolate missing values linearly 

In [10]: ts_intp_linear = ts.interpolate()

 

In [11]: ts_intp_linear

Out[11]:

2016-12-01     1.0
2016-12-03     4.0
2016-12-04     7.0
2016-12-10    10.0

 

 

 

 

  (2) 시계열 날짜 index를 기준으로 결측값 보간

       (interploate TimeSeries missing values along with Time-index)

       : ts.interploate(method='time')

 

위의 ts.interpolate() 결과와 아래의 ts.interpolate(method='time') 보간 결과를 유심히 비교해보시기 바랍니다.

 

 

In [12]: ts

Out[12]:

2016-12-01     1.0
2016-12-03     NaN
2016-12-04     NaN
2016-12-10    10.0

dtype: float64

 

In [13]: ts_intp_time = ts.interpolate(method='time')

 

In [14]: ts_intp_time

Out[14]:

2016-12-01     1.0
2016-12-03     3.0
2016-12-04     4.0
2016-12-10    10.0
dtype: float64

 

 

 

 

  (3) DataFrame 값에 선형으로 비례하는 방식으로 결측값 보간

      (interpolate DataFrame missing values linearly)

 

 

In [15]: df = DataFrame({'C1': [1, 2, np.nan, np.nan, 5],

    ...: 'C2': [6, 8, 10, np.nan, 20]})

    ...:

 

In [16]: df

Out[16]:

    C1    C2
0  1.0   6.0
1  2.0   8.0
NaN  10.0
NaN   NaN
4  5.0  20.0

 

In [17]: df_intp_values = df.interpolate(method='values')

 

In [18]: df_intp_values

Out[18]:

    C1    C2
0  1.0   6.0
1  2.0   8.0
3.0  10.0
4.0  15.0
4  5.0  20.0

 

# the same with : df.interpolate(method='values') = df.interpolate()

In [19]: df.interpolate() # by default : method='values'

Out[19]:

    C1    C2
0  1.0   6.0
1  2.0   8.0
3.0  10.0
4.0  15.0
4  5.0  20.0

 

 

 

 

 (4) 결측값 보간 개수 제한하기 (limit the number of consecutive interpolation) : limit

 

연속적으로 결측값을 보간하는 개수를 'limit=1'을 사용해서 제한해보겠습니다.  아래 예시처럼 df.ix[3, 'C1']의 NaN이 보간되지 않고 NaN 그대로 남아있습니다.

 

 

In [20]: df.interpolate(method='values', limit=1)

Out[20]:

    C1    C2
0  1.0   6.0
1  2.0   8.0
3.0  10.0
NaN  15.0
4  5.0  20.0

 

 

 

 

limit_direction='backward' 로 설정해주면 보간해주는 방향이 밑에서 위로 올라가면서 해주게 됩니다.

(아무런 설정을 안해주면 default 로 limit_direction='forward' 설정됨.  limit_direction='both' 도 있음)

 

 

In [29]: df.interpolate(method='values', limit=1, limit_direction='backward')

Out[29]:

    C1    C2
0  1.0   6.0
1  2.0   8.0
NaN  10.0
4.0  15.0
4  5.0  20.0

 

 

 

이상으로 Python pandas 의 interpolate() method를 활용한 결측값 보간법을 마치도록 하겠습니다.

 

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

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend

지난번 포스팅에서는 Python pandas의 fillna() method를 사용한

 

 - 결측값 여부 확인하기

 - 결측값 채우기, 결측값 대체하기

 

에 대해서 알아보았습니다.

 

이번 포스팅에서는 Python pandas의 dropna() method를 사용해서

 

 - 결측값이 들어있는 행 전체 제거

   (delete row with missing values)

 - 결측값이 들어있는 열 전체를 제거

   (delete column with missing values)

 - 특정 행 또는 열 만을 대상으로 결측값이 들어있으면 제거

   (delete specific row or column with missing values)

 

하는 방법을 소개하겠습니다.

 

관측값이 아주 많고 결측값이 별로 없는 경우에는 결측값이 들어있는 행 전체를 삭제하고 분석을 진행해도 무리가 없고 편리할 수 있습니다.

 

혹은 특정 변수의 결측값 비율이 매우 높고, 결측값을 채워넣을 만한 마땅한 방법이 없는 경우에는 분석의 신뢰성 확보를 위해서 그 변수(행, 칼럼)을 삭제하고 분석을 진행할 필요도 있습니다.

 

이때 dropna() method 를 사용하면 됩니다.

 

 

 

 

먼저, 필요한 모듈을 불러오고, 결측값(None 또는 np.nan)이 들어있는 DataFrame을 만들어보겠습니다.

 

 

# importing modules

In [1]: import pandas as pd


In [2]: import numpy as np


In [3]: from pandas import DataFrame

 

# making DataFrame

In [4]: df = DataFrame(np.random.randn(5, 4),

   ...: columns=['C1', 'C2', 'C3', 'C4'])


In [5]: df

Out[5]:

         C1        C2        C3        C4
0 -0.336659 -0.738247  0.598380  0.727832
1 -0.900631  0.073079  1.182290 -0.138224
2 -1.521049 -1.422940  0.330948 -0.275343
3  0.554677 -0.530208 -0.397182  0.990026
4 -0.332384 -1.979684  0.560655  0.833487

 

# inserting missing values

In [6]: df.ix[[0,1], 'C1'] = None


In [7]: df.ix[2, 'C2'] = np.nan


In [8]: df

Out[8]:

         C1        C2        C3        C4
0       NaN -0.738247  0.598380  0.727832
1       NaN  0.073079  1.182290 -0.138224
2 -1.521049       NaN  0.330948 -0.275343
3  0.554677 -0.530208 -0.397182  0.990026
4 -0.332384 -1.979684  0.560655  0.833487

 

 

 

 

 

이제 dropna() method를 사용해서 결측값이 들어있는 행 전체(axis = 0) 삭제, 혹은 결측값이 들어있는 열 전체(axis=1) 삭제해 보겠습니다.

 

 

  (1) 결측값이 들어있는 행 전체 삭제하기(delete row with NaN) : df.dropna(axis=0)

 

 

# delete row with missing values

In [9]: df_dr

df_dop_row = df.dropna(axis=0)


In [10]: df_drop_row

Out[10]:

C1 C2 C3 C4

         C1        C2        C3        C4
3  0.554677 -0.530208 -0.397182  0.990026
4 -0.332384 -1.979684  0.560655  0.833487

 

 

 

 

  (2) 결측값이 들어있는 열 전체 삭제하기 (delete column with NaN) : df.dropna(axis=1)

 

 

In [8]: df

Out[8]:

         C1        C2        C3        C4
0       NaN -0.738247  0.598380  0.727832
1       NaN  0.073079  1.182290 -0.138224
2 -1.521049       NaN  0.330948 -0.275343
3  0.554677 -0.530208 -0.397182  0.990026
4 -0.332384 -1.979684  0.560655  0.833487

 

# delete column with missing values

In [11]: df_drop_column = df.dropna(axis=1)


In [12]: df_drop_column

Out[12]:

         C3        C4
0  0.598380  0.727832
1  1.182290 -0.138224
2  0.330948 -0.275343
3 -0.397182  0.990026
4  0.560655  0.833487

 

 

 

 

  (3) 특정 행 또는 열을 대상으로 결측값이 들어있으면 제거

       (delete specific row or column with missing values) : df[ ].dropna()

 

DataFrame의 행 또는 열을 indexing 한 후에 dropna() method를 적용하면 됩니다.  dropna() 와 dropna(axis=0)은 동일합니다 (즉, axis=0 은 생략 가능).

 

아래에 4가지의 예를 들어보았습니다.  원래의 df DataFrame을 어떻게 indexing 하느냐에 따라 결측값이 들어있는 행과 열이 제거되는지 유심히 살펴보시면 금방 이해가 될거예요.

 

 

In [8]: df

Out[8]:

         C1        C2        C3        C4
0       NaN -0.738247  0.598380  0.727832
1       NaN  0.073079  1.182290 -0.138224
2 -1.521049       NaN  0.330948 -0.275343
3  0.554677 -0.530208 -0.397182  0.990026
4 -0.332384 -1.979684  0.560655  0.833487

 

# delete specific column with missing values

In [13]: df['C1'].dropna()

Out[13]:

2   -1.521049
3    0.554677
4   -0.332384
Name: C1, dtype: float64


In [14]: df[['C1', 'C2', 'C3']].dropna() # the same with dropna(axis=0)

Out[14]:

         C1        C2        C3
3  0.554677 -0.530208 -0.397182
4 -0.332384 -1.979684  0.560655


In [15]: df[['C1', 'C2', 'C3']].dropna(axis=0) # by default, the same with dropna()

Out[15]:

         C1        C2        C3
3  0.554677 -0.530208 -0.397182
4 -0.332384 -1.979684  0.560655


In [16]: df[['C1', 'C2', 'C3']].dropna(axis=1)

Out[16]:

         C3
0  0.598380
1  1.182290
2  0.330948
3 -0.397182
4  0.560655

 

 

In [17]: df.ix[[2, 4], ['C1', 'C2', 'C3']].dropna(axis=0)

Out[17]:

         C1        C2        C3
4 -0.332384 -1.979684  0.560655

 

 

 

마지막으로 첨언하자면, 결측값이 들어있는 행 전체, 혹은 열 전체를 삭제하는 것은 데이터 소실, 혹은 데이터 모집단 왜곡의 위험도 있는 만큼 분석에 영향도를 한번 생각해보고 나서 결정하시면 좋겠습니다.

 

그리고 만약을 대비해서 원본 데이터 (source data)는 그대로 남겨 놓은 상태에서 행 삭제 혹은 열 삭제 후의 DataFrame은 다른 이름으로 copy 해서 사용하실 것을 권합니다. 

 

다음번 포스팅에서는 결측값 보간(interpolation)에 대해서 알아보겠습니다.

 

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

 

 

 

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by R Friend R_Friend


티스토리 툴바