지난번 포스팅에서는 Python pandas의 Series, DataFrame에서 시계열 데이터 index 의 중복 확인 및 처리하는 방법(https://rfriend.tistory.com/500) 에 대해서 소개하였습니다. 


이번 포스팅에서는 Python pandas에서 일정한 주기의 시계열 데이터(Fixed frequency time series)를 가진 Series, DataFrame 만드는 방법을 소개하겠습니다. 



[ 시계열 데이터의 특징 ]

  • 동일한/ 고정된 간격의 날짜-시간 index (equally spaced time interval, fixed frequency)
  • 중복 없고, 빠진 것도 없는 날짜-시간 index (no redundant values or gaps)
  • 시간 순서대로 정렬 (sequential order)

(* 시계열 데이터가 반드시 동일한/고정된 간격의 날짜-시간을 가져야만 하는 것은 아님. 가령, 주가(stock price) 데이는 장이 열리는 business day에만 존재하며 공휴일은 데이터 없음)





  (1) 동일 간격의 시계열 데이터 Series 만들기 (fixed frequency time series pandas Series)



(1-1) 중간에 날짜가 비어있는 시계열 데이터 Series 만들기 (non-equally spaced time series)


먼저, 예제로 사용할 간단한 시계열 데이터 pandas Series 를 만들어보겠습니다. 의도적으로 '2019-12-04', '2019-12-08' 일의 날짜-시간 index 를 제거(drop)하여 이빨 빠진 날짜-시간 index 를 만들었습니다. 



import pandas as pd


# generate dates from 2019-12-01 to 2019-12-10

date_idx = pd.date_range('2019-12-01', periods=10)

date_idx

[Out]:

DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04', '2019-12-05', '2019-12-06', '2019-12-07', '2019-12-08', '2019-12-09', '2019-12-10'], dtype='datetime64[ns]', freq='D')


# drop 2 dates from DatetimeIndex

date_idx = date_idx.drop(pd.DatetimeIndex(['2019-12-04', '2019-12-08']))

date_idx

[Out]:
DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-05',
               '2019-12-06', '2019-12-07', '2019-12-09', '2019-12-10'],
              dtype='datetime64[ns]', freq=None)

# Time Series with missing dates index

series_ts_missing = pd.Series(range(len(date_idx))

                              , index=date_idx)


series_ts_missing

[Out]:
2019-12-01    0
2019-12-02    1
2019-12-03    2
2019-12-05    3
2019-12-06    4
2019-12-07    5
2019-12-09    6
2019-12-10    7
dtype: int64





(1-2) 이빨 빠진 Time Series를 동일한 간격의 시계열 데이터 pandas Series로 변환하기 

       (fixed frequency, equally spaced time interval time series)


위의 (1-1)에서 만든 Series는 '2019-12-04', '2019-12-08'일의 날짜-시간 index가 빠져있는데요, 이럴 경우 resample('D')를 이용하여 날짜-시간 index는 등간격의 날짜-시간을 채워넣고, 대신 값은 결측값 처리(missing value, NaN, Not a Number)를 해보겠습니다. 



# Create a 1 day Fixed Frequency Time Series using resample('D')

series_ts_fixed_freq = series_ts_missing.resample('D')

series_ts_fixed_freq.first()

[Out]:
2019-12-01    0.0
2019-12-02    1.0
2019-12-03    2.0
2019-12-04    NaN <---
2019-12-05    3.0
2019-12-06    4.0
2019-12-07    5.0
2019-12-08    NaN <---
2019-12-09    6.0
2019-12-10    7.0
Freq: D, dtype: float64




비어있던 '날짜-시간' index 를 등간격 '날짜-시간' index로 채우면서 값(value)에 'NaN'이 생긴 부분을 fillna(0)을 이용하여 '0'으로 채워보겠습니다. 



# fill missing value with '0'

series_ts_fixed_freq.first().fillna(0)

[Out]:
2019-12-01    0.0
2019-12-02    1.0
2019-12-03    2.0
2019-12-04    0.0 <---
2019-12-05    3.0
2019-12-06    4.0
2019-12-07    5.0
2019-12-08    0.0 <---
2019-12-09    6.0
2019-12-10    7.0
Freq: D, dtype: float64

 




이번에는 resample('10T')를 이용하여 '10분 단위의 동일 간격 날짜-시간' index의 시계열 데이터를 만들어보겠습니다. 이때도 원래의 데이터셋에 없던 '날짜-시간' index의 경우 값(value)은 결측값으로 처리되어 'NaN'으로 채워집니다. 



# resampling with 10 minutes frequency (interval)

series_ts_missing.resample('10T').first()

[Out]:

2019-12-01 00:00:00 0.0 2019-12-01 00:10:00 NaN 2019-12-01 00:20:00 NaN 2019-12-01 00:30:00 NaN 2019-12-01 00:40:00 NaN ... 2019-12-09 23:20:00 NaN 2019-12-09 23:30:00 NaN 2019-12-09 23:40:00 NaN 2019-12-09 23:50:00 NaN 2019-12-10 00:00:00 7.0 Freq: 10T, Length: 1297, dtype: float64

 





  (2) 동일 간격의 시계열 데이터 DataFrame 만들기 

       (fixed frequency time series pandas DataFrame)



(2-1) 중간에 날짜가 비어있는 시계열 데이터 DataFrame 만들기 (non-equally spaced time series DataFrame)


pd.date_range() 함수로 등간격의 10일치 날짜-시간 index를 만든 후에, drop(pd.DatetimeIndex()) 로 '2019-12-04', '2019-12-08'일을 제거하여 '이빨 빠진 날짜-시간' index를 만들었습니다. 



import pandas as pd


# generate dates from 2019-12-01 to 2019-12-10

date_idx = pd.date_range('2019-12-01', periods=10)


# drop 2 dates from DatetimeIndex

date_idx = date_idx.drop(pd.DatetimeIndex(['2019-12-04', '2019-12-08']))

date_idx

[Out]:

DatetimeIndex(['2019-12-01', '2019-12-02', '2019-12-03', '2019-12-05',

'2019-12-06', '2019-12-07', '2019-12-09', '2019-12-10'], dtype='datetime64[ns]', freq=None)


df_ts_missing = pd.DataFrame(range(len(date_idx))

                             , columns=['col']

                             , index=date_idx)


df_ts_missing

[Out]:

col
2019-12-010
2019-12-021
2019-12-032
2019-12-053
2019-12-064
2019-12-075
2019-12-096
2019-12-107

 




(2-2) 이빨 빠진 Time Series를 동일한 간격의 시계열 데이터 pandas DataFrame으로 변환하기 

       (fixed frequency, equally spaced time interval time series pandas DataFrame)


resample('D') 를 메소드를 사용하여 '일(Day)' 동일 간격의 '날짜-시간' index를 가지는 시계열 데이터 DataFrame을 만들었습니다. 이때 원래의 데이터에 없던 '날짜-시간' index의 경우 결측값 처리되어 값(value)은 'NaN'으로 처리됩니다. 



df_ts_fixed_freq = df_ts_missing.resample('D').first()

df_ts_fixed_freq

[Out]:

col
2019-12-010.0
2019-12-021.0
2019-12-032.0
2019-12-04NaN <---
2019-12-053.0
2019-12-064.0
2019-12-075.0
2019-12-08NaN <---
2019-12-096.0
2019-12-107.0




동일 간견 시계열 데이터로 변환하는 과정에서 생긴 'NaN' 결측값 부분을 fillina(0) 메소드를 이용하여 '0'으로 대체하여 채워보겠습니다. 



# fill missing value with '0'

df_ts_fixed_freq = df_ts_fixed_freq.fillna(0)

df_ts_fixed_freq

col
2019-12-010.0
2019-12-021.0
2019-12-032.0
2019-12-040.0 <---
2019-12-053.0
2019-12-064.0
2019-12-075.0
2019-12-080.0 <---
2019-12-096.0
2019-12-107.0




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

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

지난번 포스팅에서는 날짜-시간 시계열 객체(date-time, Timeseries objects)를 문자열(Strings)로 변환하기, 거꾸로 문자열을 날짜-시간 시계열 객체로 변환하는 방법(https://rfriend.tistory.com/498)을 소개하였습니다. 


이번 포스팅에서는 날짜-시간 시계열 데이터(date-time time series) index로 가지는  Python pandas의 Series, DataFrame 에서 특정 날짜-시간을 indexing, slicing, selection, truncation 하는 방법을 소개하겠습니다. 


(1) pandas Series에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법

(2) pandas DataFrame에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법





  (1) pandas Series에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법


먼저, 간단한 예제로 사용하도록 2019년 11월 25일 부터 ~ 2019년 12월 4일까지 10일 기간의 년-월-일 날짜를 index로 가지는 pands Series를 만들어보겠습니다. 


pandas.date_range(시작날짜, periods=생성할 날짜-시간 개수)  함수를 사용하여 날짜-시간 데이터를 생성하였으며, 이를 index로 하여 pandas Series를 만들었습니다. 



import pandas as pd

from datetime import datetime


# DatetimeIndex

ts_days_idx = pd.date_range('2019-11-25', periods=10)

ts_days_idx

[Out]:

DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28', '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02', '2019-12-03', '2019-12-04'], dtype='datetime64[ns]', freq='D')



# Series with time series index

series_ts = pd.Series(range(len(ts_days_idx))

                      , index=ts_days_idx)


series_ts

[Out]:
2019-11-25    0
2019-11-26    1
2019-11-27    2
2019-11-28    3
2019-11-29    4
2019-11-30    5
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64



series_ts.index

[Out]: 
DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28',
               '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02',
               '2019-12-03', '2019-12-04'],
              dtype='datetime64[ns]', freq='D')


series_ts.index[6]

[Out]: Timestamp('2019-12-01 00:00:00', freq='D')




참고로, 아례의 예처럼 pd.date_range(start='시작 날짜-시간', end='끝 날짜-시간') 처럼 명시적으로 시작과 끝의 날짜-시간을 지정해주어도 위의 perieds를 사용한 예와 동일한 결과를 얻을 수 있습니다. 



import pandas as pd


pd.date_range(start='2019-11-25', end='2019-12-04')

[Out]:
DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28',
               '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02',
               '2019-12-03', '2019-12-04'],
              dtype='datetime64[ns]', freq='D')




참고로 하나더 소개하자면요, pandas.date_range('시작날짜-시간', period=생성할 날짜-시간 개수, freq='주기 단위') 에서 freq 옵션을 통해서 'S' 1초 단위, '10S' 10초 단위, 'H' 1시간 단위, 'D' 1일 단위, 'M' 1달 단위(월 말일 기준), 'Y' 1년 단위 (년 말일 기준) 등으로 날짜-시간 시계열 데이터 생성 주기를 설정할 수 있습니다. 매우 편하지요?!


< 1초 단위로 날짜-시간 데이터 10개를 생성한 예 >


# 10 timeseries data points by Second(freq='S')

pd.date_range('2019-11-25 00:00:00', periods=10, freq='S')

[Out]: 
DatetimeIndex(['2019-11-25 00:00:00', '2019-11-25 00:00:01',
               '2019-11-25 00:00:02', '2019-11-25 00:00:03',
               '2019-11-25 00:00:04', '2019-11-25 00:00:05',
               '2019-11-25 00:00:06', '2019-11-25 00:00:07',
               '2019-11-25 00:00:08', '2019-11-25 00:00:09'],
              dtype='datetime64[ns]', freq='S')



< 10초 단위로 날짜-시간 데이터 10개를 생성한 예 >


# 10 timeseries data points by 10 Seconds (freq='10S')

pd.date_range('2019-11-25 00:00:00', periods=10, freq='10S')

[Out]:

DatetimeIndex(['2019-11-25 00:00:00', '2019-11-25 00:00:10', '2019-11-25 00:00:20', '2019-11-25 00:00:30', '2019-11-25 00:00:40', '2019-11-25 00:00:50', '2019-11-25 00:01:00', '2019-11-25 00:01:10', '2019-11-25 00:01:20', '2019-11-25 00:01:30'], dtype='datetime64[ns]', freq='10S')

 



(1-1) 시계열데이터를 index로 가지는 pandas Series에서 특정 날짜-시간 데이터 indexing 하기


먼저 위에서 생성한 series_ts 라는 이름의 시간 순서대로 정렬되어 있는 Series 에서 7번째에 위치한 '2019-12-01' 의 값 '6'을 indexing 해보겠습니다. 


(a), (b)와 같이 위치(position)를 가지고 인덱싱할 수 있습니다. 

또한, (c), (d)와 같이 날짜-시간 문자열(String)을 가지고도 인덱싱(indexing)을 할 수 있습니다. 

(e) 처럼 datetime.datetime(year, month, day) 객체를 사용해서도 인덱싱할 수 있습니다. 



import pandas as pd

from datetime import datetime


# (a) indexing with index number

series_ts[6]

[Out]: 6


# (b) indexing with index number using iloc

series_ts.iloc[6]

[Out]: 6


# (c) indexing with string ['year-month-day']

series_ts['2019-12-01']

[Out]: 6


# (d) indexing with string ['month/day/year']

series_ts['12/01/2019']

[Out]: 6


# (f) indexing with datetime.datetime(year, month, day)

series_ts[datetime(2019, 12, 1)]

[Out]: 6





(1-2) 시계열데이터를 index로 가지는 pandas Series에서 날짜-시간 데이터 Slicing 하기


아래는 '2019-12-01' 일 이후의 값을 모두 slicing 해오는 5가지 방법입니다. 

(a), (b)는 위치(position):위치(position)을 이용하여 날짜를 index로 가지는 Series를 slicing을 하였습니다. 

(c), (d)는 '년-월-일':'년-월-일' 혹은 '월/일/년':'월/일/년' 문자열(string)을 이용하여 slicing을 하였습니다. 

(e)는 datetime.datetime(년, 월, 일):datetime.datetime(년, 월, 일) 을 이용하여 slicing을 하였습니다. 



import pandas as pd

from datetime import datetime


# (a) slicing with position

series_ts[6:]

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64


# (b) slicing with position using iloc

series_ts.iloc[6:]

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

# (c) slicing with string

series_ts['2019-12-01':'2019-12-10']

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

# (d) slicing with string

series_ts['12/01/2019':'12/10/2019']

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

# (e) slicing with datetime

series_ts[datetime(2019, 12, 1):datetime(2019, 12, 10)]

[Out]:

2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64




(1-3) 시계열데이터를 index로 가지는 pandas Series 에서 날짜-시간 데이터 Selection 하기


'날짜-시간' 문자열(String)을 이용하여 특정 '년', '월'의 모든 데이터를 선택할 수도 있습니다. 꽤 편리하고 재미있는 기능입니다. 


< '2019'년 모든 데이터 선택하기 예 >


# selection with year string

series_ts['2019']

[Out]:

2019-11-25 0 2019-11-26 1 2019-11-27 2 2019-11-28 3 2019-11-29 4 2019-11-30 5 2019-12-01 6 2019-12-02 7 2019-12-03 8 2019-12-04 9 Freq: D, dtype: int64

 



< '2019년 12월' 모든 데이터 선택하기 예 >


# selection with year-month string

series_ts['2019-12']

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

 




(1-4) 시계열 데이터를 index로 가지는 pandas Series에서 날짜-시간 데이터 잘라내기 (Truncate)


truncate() methods를 사용하면 잘라내기(truncation)를 할 수 있습니다. before, after 옵션으로 잘라내기하는 범위 기간을 설정할 수 있는데요, 해당 날짜 포함 여부를 유심히 살펴보기 바랍니다. 


< '2019년 12월 1일' 이전(before) 모든 데이터 잘라내기 예 >

('2019년 11월 30일'까지의 모든 데이터 삭제하며, '2019-12-01'일 데이터는 남아 있음)


# truncate before

series_ts.truncate(before='2019-12-01')

[Out]:
2019-12-01    6
2019-12-02    7
2019-12-03    8
2019-12-04    9
Freq: D, dtype: int64

 



< '2019년 11월 30일' 이후(after) 모든 데이터 잘라내기 예 >

(''2019년 12월 1일' 부터의 모든 데이터 삭제하며, '2019-11-30'일 데이터는 남아 있음)


# truncate after

series_ts.truncate(after='2019-11-30')

[Out]:
2019-11-25    0
2019-11-26    1
2019-11-27    2
2019-11-28    3
2019-11-29    4
2019-11-30    5
Freq: D, dtype: int64

 





  (2) pandas DataFrame에서 시계열 데이터 indexing, slicing, selection, truncation 하는 방법


위의 (1)번에서 소개했던 pandas Series의 시계열 데이터 indexing, slicing, selection, truncation 방법을 동일하게 pandas DataFrame에도 사용할 수 있습니다.  


년-월-일 날짜를 index로 가지는 간단한 pandas DataFrame 예제를 만들어보겠습니다. 



import pandas as pd

from datetime import datetime


# DatetimeIndex

ts_days_idx = pd.date_range('2019-11-25', periods=10)

ts_days_idx

[Out]:
DatetimeIndex(['2019-11-25', '2019-11-26', '2019-11-27', '2019-11-28',
               '2019-11-29', '2019-11-30', '2019-12-01', '2019-12-02',
               '2019-12-03', '2019-12-04'],
              dtype='datetime64[ns]', freq='D')


# DataFrame with DatetimeIndex

df_ts = pd.DataFrame(range(len(ts_days_idx))

                     , columns=['col']

                     , index=ts_days_idx)


df_ts

col
2019-11-250
2019-11-261
2019-11-272
2019-11-283
2019-11-294
2019-11-305
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 




(2-1) 시계열데이터를 index로 가지는 pandas DataFrame에서 특정 날짜-시간 데이터 indexing 하기


위의 (1-1) Series indexing과 거의 유사한데요, DataFrame에서는 df_ts[6], df_ts[datetime(2019, 12, 1)] 의 두가지 방법은 KeyError 가 발생해서 사용할 수 없구요, 아래의 3가지 방법만 indexing에 사용 가능합니다. 


(a) iloc[integer] 메소드를 사용하여 위치(position) 로 indexing 하기

(b), (c) loc['label'] 메소드를 사용하여 이름('label')로 indexing 하기



# (a) indexing with index position integer using iloc[]

df_ts.iloc[6]

[Out]:
col    6
Name: 2019-12-01 00:00:00, dtype: int64


# (b) indexing with index labels ['year-month-day']

df_ts.loc['2019-12-01']

[Out]:
col    6
Name: 2019-12-01 00:00:00, dtype: int64


# (c) indexing with index labels ['month/day/year']

df_ts.loc['12/01/2019']

[Out]:
col    6
Name: 2019-12-01 00:00:00, dtype: int64





(2-2) 시계열데이터를 index로 가지는 pandas DataFrame에서 날짜-시간 데이터 Slicing 하기


아래는 '2019-12-01' 일 이후의 값을 모두 slicing 해오는 4가지 방법입니다. 

(a) 위치(position):위치(position)을 이용하여 날짜를 index로 가지는 Series를 slicing을 하였습니다. 

(b), (c)는 loc['년-월-일']:loc['년-월-일'] 혹은 loc['월/일/년']:loc['월/일/년'] 문자열(string)을 이용하여 slicing을 하였습니다. 

(d) 는 loc[datetime.datetime(year, month, day):datetime.datetime(year, month, day)] 로 slicing을 한 예입니다. 



# (a) slicing DataFrame with position integer

df_ts[6:10]

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 


# (b) silcing using date strings 'year-month-day'

df_ts.loc['2019-12-01':'2019-12-10']

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049


# (c) slicing using date strings 'month/day/year'

df_ts.loc['12/01/2019':'12/10/2019']

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049


# (d) slicing using datetime objects

from datetime import datetime

df_ts.loc[datetime(2019, 12, 1):datetime(2019, 12, 10)]

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049






(2-3) 시계열데이터를 index로 가지는 pandas DataFrame 에서 날짜-시간 데이터 Selection 하기


'년', '년-월' 날짜 문자열을 df.loc['year'], df.loc['year-month'] 에 입력하면 해당 년(year), 월(month)의 모든 데이터를 선택할 수 있습니다. 


< '2019년'의 모든 데이터 선택 예 >


# selection of year '2019'

df_ts.loc['2019'] # df_ts['2019']

col
2019-11-250
2019-11-261
2019-11-272
2019-11-283
2019-11-294
2019-11-305
2019-12-016
2019-12-027
2019-12-038
2019-12-049




< '2019년 12월'의 모든 데이터 선택 예 >


# selection of year-month '2019-12'

df_ts.loc['2019-12']

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 




(2-4) 시계열 데이터를 index로 가지는 pandas DataFrame에서 날짜-시간 데이터 잘라내기 (Truncate)


truncate() 메소드를 사용하면 before 이전 기간의 데이터를 잘라내거나 after 이후 기간의 데이터를 잘라낼 수 있습니다. 


< '2019-12-01' 일 이전(before) 기간 데이터 잘라내기 예 >

('2019-12-01'일은 삭제되지 않고 남아 있음)


# truncate before

df_ts.truncate(before='2019-12-01') # '2019-12-01' is not removed

col
2019-12-016
2019-12-027
2019-12-038
2019-12-049

 



< '2019-11-30'일 이후(after) 기간 데이터 잘라내기 예 >

('2019-11-30'일은 삭제되지 않고 남아 있음)


# truncate after

df_ts.truncate(after='2019-11-30') # '2019-11-30' is not removed

col
2019-11-250
2019-11-261
2019-11-272
2019-11-283
2019-11-294
2019-11-305

 



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

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 Python pandas DataFrame을 만들려고 할 때 "ValueError: If using all scalar values, you must pass an index" 에러 해결 방안 4가지를 소개하겠습니다.

아래의 예처럼 dictionary로 키, 값 쌍으로 된 데이터를 pandas DataFrame으로 만들려고 했을 때, 모든 값이 스칼라 값(if using all scalar values) 일 경우에 "ValueError: If using all scalar values, you must pass an index" 에러가 발생합니다. 

import pandas as pd

df = pd.DataFrame({'col_1': 1, 

                  'col_2': 2})

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-73d6f192ba2a> in <module>()
      1 df = pd.DataFrame({'col_1': 1, 
----> 2                   'col_2': 2})

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in __init__(self, data, index, columns, dtype, copy)
    273                                  dtype=dtype, copy=copy)
    274         elif isinstance(data, dict):
--> 275             mgr = self._init_dict(data, index, columns, dtype=dtype)
    276         elif isinstance(data, ma.MaskedArray):
    277             import numpy.ma.mrecords as mrecords

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in _init_dict(self, data, index, columns, dtype)
    409             arrays = [data[k] for k in keys]
    410 
--> 411         return _arrays_to_mgr(arrays, data_names, index, columns, dtype=dtype)
    412 
    413     def _init_ndarray(self, values, index, columns, dtype=None, copy=False):

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in _arrays_to_mgr(arrays, arr_names, index, columns, dtype)
   5494     # figure out the index, if necessary
   5495     if index is None:
-> 5496         index = extract_index(arrays)
   5497     else:
   5498         index = _ensure_index(index)

~/anaconda3/envs/py3.5_tf1.4/lib/python3.5/site-packages/pandas/core/frame.py in extract_index(data)
   5533 
   5534         if not indexes and not raw_lengths:
-> 5535             raise ValueError('If using all scalar values, you must pass'
   5536                              ' an index')
   5537 

ValueError: If using all scalar values, you must pass an index




이 에러를 해결하기 위한 4가지 방법을 차례대로 소개하겠습니다. 


 (1) 해결방안 1 : 인덱스 값을 설정해줌 (pass an index)

에러 메시지에 "you must pass an index" 라는 가이드라인대로 인덱스 값을 추가로 입력해주면 됩니다. 


# (1) pass an index

df = pd.DataFrame({'col_1': 1, 

                  'col_2': 2}

                  index = [0])


df

col_1col_2
012

 


물론 index 에 원하는 값을 입력해서 설정해줄 수 있습니다. index 에 'row_1' 이라고 해볼까요?

df = pd.DataFrame({'col_1': 1, 

                   'col_2': 2}

                  index = ['row_1'])


df

col_1col_2
row_112



 (2) 스칼라 값 대신 리스트 값을 입력 (use a list instead of scalar values)

입력하는 값(values)에 대괄호 [ ] 를 해주어서 리스트로 만들어준 값을 사전형의 값으로 사용하면 에러가 발생하지 않습니다. 

# (2) use a list instead of scalar values

df2 = pd.DataFrame({'col_1': [1]

                    'col_2': [2]})


df2

col_1col_2
012



 (3) pd.DataFrame.from_records([{'key': value}]) 를 사용해서 DataFrame 만들기

이때도 [ ] 로 해서 리스트 값을 입력해주어야 합니다. ( [ ] 빼먹으면 동일 에러 발생함)

# (3) use pd.DataFrame.from_records() with a list

df3 = pd.DataFrame.from_records([{'col_1': 1, 

                                  'col_2': 2}])


df3 

col_1col_2
012



 (4) pd.DataFrame.from_dict([{'key': value}]) 를 사용하여 DataFrame 만들기

(3)과 거의 유사한데요, from_records([]) 대신에 from_dict([]) 를 사용하였으며, 역시 [ ] 로 해서 리스트 값을 입력해주면 됩니다. 

# (4) use pd.DataFrame.from_dict([]) with a list

df4 = pd.DataFrame.from_dict([{'col_1': 1, 

                              'col_2': 2}])


df4

col_1col_2
012


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

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 구분자(delimiter, separator)를 포함한 문자열 칼럼을 구분자를 기준으로 여러개의 칼럼으로 나누어서 DataFrame을 만드는 방법을 소개하겠습니다. 


그리고 PoestgreSQL, Greenplum DB에서도 구분자를 포함한 칼럼을 구분자를 기준으로 여러개의 칼럼으로 나누는 방법도 이어서 소개하겠습니다. 






 (1) pandas DataFrame 내 문자열 칼럼을 구분자로 분리하여 여러개의 칼럼 만들기


먼저 간단한 예를 들기 위해 ':' 구분자(delimiter, separator)를 가진 'col' 이라는 이름의 칼럼을 가진 pandas DataFrame을 만들어보겠습니다. 


import pandas as pd


df = pd.DataFrame({'col': ['a:1:20.3:S', 'b:2:10.5:C', 'c:3:51.9:A']})


df

col
0a:1:20.3:S
1b:2:10.5:C
2c:3:51.9:A




이제 원래의 'df' 라는 이름의 DataFrame 에 'col'변수를 그대로 둔 채로, ':' 구분자를 기준으로 'col' 문자열 칼럼을 분리(split) 하여 'group', 'id', 'value', 'grade' 라는 새로운 4개의 칼럼을 생성하여 추가해보겠습니다. split() 문자열 메소드는 split(separator, maxsplit) 의 형식으로 사용합니다. 



df[['group', 'id', 'value', 'grade']] = pd.DataFrame(df.col.str.split(':', 3).tolist())


df

colgroupidvaluegrade
0a:1:20.3:Sa120.3S
1b:2:10.5:Cb210.5C
2c:3:51.9:Ac351.9A

 



원래의 'col' 이름의 칼럼이 필요 없을 경우 원래의 DataFrame을 덮어쓰거나, 아니면 'col'을 포함하지 않는 새로운 DataFrame을 만들어주면 됩니다. 



df2 = pd.DataFrame(df.col.str.split(':', 3).tolist()

                   columns = ['group', 'id', 'value', 'grade'])


df2

groupidvaluegrade
0a120.3S
1b210.5C
2c351.9A

 



문자열(string)을 분리(split)해서 만든 새로운 칼럼들은 전부 문자열(string) 데이터 형식입니다. 이중에서 'id'와 'value' 칼럼을 숫자형(numeric)으로 변경하는 방법은 https://rfriend.tistory.com/470 포스팅을 참고하세요. 



df2.dtypes

group    object
id       object
value    object
grade    object
dtype: object

 





 (2) PostgreSQL, GPDB에서 문자열 칼럼을 구분자로 분리하여 여러개 칼럼 만들기


PostgreSQL, Greenplum DB에서는 split_part(string_column, separator, field_number) 의 형식으로 문자열 칼럼을 나눌 수 있습니다. 




위의 Python pandas DataFrame에서 사용했던 것과 동일한 예제 Table을 만들어서, 'col' 문자열 칼럼을 'group', 'id', value', 'grade'의 4개의 문자열(string)을 가진 새로운 Table을 만들어보겠습니다. 


-- make a table

DROP TABLE IF EXISTS grp_val_grade;

CREATE TABLE grp_val_grade (

col varchar(100) NOT NULL

);


INSERT INTO grp_val_grade VALUES ('a:1:20.3:S');

INSERT INTO grp_val_grade VALUES('b:2:10.5:C');

INSERT INTO grp_val_grade VALUES('c:3:51.9:A');


SELECT * FROM grp_val_grade; 





다음으로 split_part(string_column, separator, field_number) 함수를 사용해서 문자열 칼럼을 ':' 구분자를 기준으로 나누어서 새로운 칼럼을 만들어보겠습니다. 



-- split a column by delimeter and make 4 columns

DROP TABLE IF EXISTS grp_val_grade2;

CREATE TABLE grp_val_grade2 AS (

SELECT 

col

, split_part(col, ':', 1) AS group

, split_part(col, ':', 2) AS id 

, split_part(col, ':', 3) AS value

, split_part(col, ':', 4) AS grade

FROM grp_val_grade

);


SELECT * FROM grp_val_grade2;




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


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 그룹 별로 변수 간 상관관계 분석 (correlation with columns by groups) 하는 방법을 소개하겠습니다. 


Python pandas는 Pearson Correlation Coefficient를 구할 수 있는 corr(), corrwith() 함수를 제공합니다. 이들 함수를 groupby() 와 apply(lambda func)를 함께 사용함으로써 그룹 별 변수 간 상관계수를 구할 수 있습니다. 



[ 피어슨 상관계수 ( Pearson Correlation Coefficient) ]



먼저 예제로 사용할 'group 1'과 'group 2'의 2개의 그룹을 가진 간단한 DataFrame을 만들어보겠습니다. 




import numpy as np

import pandas as pd

from pandas import DataFrame


# making DataFrame with 4 random variables

np.random.seed(123) # for reproducibility

df= DataFrame(np.random.randn(10, 4), 

                    columns=['a', 'b', 'c', 'd'])

# setting index with 2 group, 'grp1' and 'grp2'

df['group'] = ['grp1', 'grp1', 'grp1', 'grp1', 'grp1', 

                  'grp2', 'grp2', 'grp2', 'grp2', 'grp2']


df = df.set_index('group')


df

abcd
group
grp1-1.0856310.9973450.282978-1.506295
grp1-0.5786001.651437-2.426679-0.428913
grp11.265936-0.866740-0.678886-0.094709
grp11.491390-0.638902-0.443982-0.434351
grp12.2059302.1867861.0040540.386186
grp20.7373691.490732-0.9358341.175829
grp2-1.253881-0.6377520.907105-1.428681
grp2-0.140069-0.861755-0.255619-2.798589
grp2-1.771533-0.6998770.927462-0.173636
grp20.0028460.688223-0.8795360.283627







  (1) 'd' 변수와 나머지 모든 변수 간 그룹 별 상관계수 구하기 : x.corrwith(x['d'])



# correlation with columns: corrwith()

corr_with_d = lambda x: x.corrwith(x['d'])


grouped = df.groupby('group')


grouped.apply(corr_with_d) 

abcd
group
grp10.8468220.0994170.0892051.0
grp20.3074770.832473-0.3904691.0





  (2) 'a'변수와 'd'변수 간 그룹 별 상관계수 구하기 : g['a'].corr[g['d'])



# inter-column correlation: corr()

corr_a_d = lambda g: g['a'].corr(g['d'])


grouped = df.groupby('group')


DataFrame(grouped.apply(corr_a_d))

0
group
grp10.846822
grp20.307477

 



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


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



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 groupby() 를 사용할 때 


(1) pd.cut()으로 동일 길이로 나누어서 범주를 만든 후 GroupBy()로 그룹별 통계량 구하기

(2) pd.qcut()으로 동일 개수로 나누어서 범주를 만든 후 GroupBy()로 그룹별 통계량 구하기


를 해보겠습니다. 




먼저, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



import numpy as np

import pandas as pd

from pandas import DataFrame


np.random.seed(123)

df = DataFrame({'col_1': np.random.randint(20, size=20), 

                      'col_2': np.random.randn(20)})


df

col_1col_2
0131.730024
121.232650
22-0.823598
36-0.118201
417-0.576103
5191.695731
610-0.074394
71-1.900637
80-0.777655
9171.313462
10151.804458
119-0.965550
120-1.316480
1314-0.625785
140-0.326946
1515-0.308209
16190.827117
17141.070781
184-3.055577
1901.005932

 




  (1) pd.cut : 동일 길이로 나누어서 범주 만들기(equal-length buckets categorization)


'col_1' 칼럼에 대해서 4개의 동일한 길이로 범주를 만들어보겠습니다. 

카테고리의 구간이 [(-0.019, 4.75] < (4.75, 9.5] < (9.5, 14.25] < (14.25, 19.0]] 로서 4개의 각 구간의 길이가 동일함을 알 수 있습니다.



factor_col_1 = pd.cut(df.col_1, 4)

factor_col_1

0 (9.5, 14.25]

1     (-0.019, 4.75]
2     (-0.019, 4.75]
3        (4.75, 9.5]
4      (14.25, 19.0]
5      (14.25, 19.0]
6       (9.5, 14.25]
7     (-0.019, 4.75]
8     (-0.019, 4.75]
9      (14.25, 19.0]
10     (14.25, 19.0]
11       (4.75, 9.5]
12    (-0.019, 4.75]
13      (9.5, 14.25]
14    (-0.019, 4.75]
15     (14.25, 19.0]
16     (14.25, 19.0]
17      (9.5, 14.25]
18    (-0.019, 4.75]
19    (-0.019, 4.75]
Name: col_1, dtype: category
Categories (4, interval[float64]): [(-0.019, 4.75] < (4.75, 9.5] < (9.5, 14.25] < (14.25, 19.0]]



이제 'factor_col_1'이라는 'col_1' 칼럼에 대한 4개 구간의 범주를 GroupBy() 에 넣어서 각 범주의 그룹별로 agg() 함수로 개수(count), 평균(mean), 표준편차(std), 최소값(min), 최대값(max) 값을 계산해보겠습니다. 



grouped_col_1 = df.col_1.groupby(factor_col_1)

grouped_col_1.agg(['count', 'mean', 'std', 'min', 'max'])

countmeanstdminmax
col_1
(-0.019, 4.75]81.1251.45773804
(4.75, 9.5]27.5002.12132069
(9.5, 14.25]412.7501.8929691014
(14.25, 19.0]617.0001.7888541519



위와 동일한 결과를 아래 처럼 통계집계를 하는 사용자정의함수와 apply() 를 사용해서 구할 수도 있습니다. 


 

def summary_func(group):

    return {'count': group.count()

             'mean': group.mean()

             'std': group.std()

             'min': group.min()

             'max': group.max()}


grouped_col_1.apply(summary_func)

col_1                
(-0.019, 4.75]  count     8.000000
                max       4.000000
                mean      1.125000
                min       0.000000
                std       1.457738
(4.75, 9.5]     count     2.000000
                max       9.000000
                mean      7.500000
                min       6.000000
                std       2.121320
(9.5, 14.25]    count     4.000000
                max      14.000000
                mean     12.750000
                min      10.000000
                std       1.892969
(14.25, 19.0]   count     6.000000
                max      19.000000
                mean     17.000000
                min      15.000000
                std       1.788854
Name: col_1, dtype: float64




위의 결과를 좀더 보기에 좋도록 unstack()를 사용해서 길게(long) 제시된 결과를 옆으로 넓게(wide) 표형식으로 만들어보겠습니다. 



grouped_col_1.apply(summary_func).unstack()

countmaxmeanminstd
col_1
(-0.019, 4.75]8.04.01.1250.01.457738
(4.75, 9.5]2.09.07.5006.02.121320
(9.5, 14.25]4.014.012.75010.01.892969
(14.25, 19.0]6.019.017.00015.01.788854

 



위의 결과의 'count' 개수 부분을 보면 각 범주 구간  [(-0.019, 4.75] < (4.75, 9.5] < (9.5, 14.25] < (14.25, 19.0]] 그룹 별로 개수가 8개, 2개, 4개, 6개로서 각각 다릅니다. 이는 랜덤 숫자에 대해서 구간별 길이를 동일하게 했기 때문에 구간 그룹별 개수가 다르게 된 것입니다.


그러면, 다음으로 구간별 '동일한 개수(equal-size)'로 범주 바구니(bucket categorization)를 만들어보겠습니다.




  (2) pd.qcut() : 동일 개수로 나누어서 범주 만들기 (equal-size buckets categorization)


pd.qcut() 함수를 사용하여 'col_2'에 대해서 각 범주 바구니별로 동일하게 4개의 개수를 가지도록 범주를 만들어보겠습니다. 이때 labels=False 로 설정하여 label이 0, 1, 2, 3 이런 식으로 0부터 순차적으로 1씩 증가하게 하였습니다. 



bucket_qcut_col_2 = pd.qcut(df.col_2, 4, labels=False)

bucket_qcut_col_2

0     3
1     3
2     0
3     2
4     1
5     3
6     2
7     0
8     1
9     3
10    3
11    0
12    0
13    1
14    1
15    1
16    2
17    2
18    0
19    2
Name: col_2, dtype: int64




아래처럼 labels=np.arange(4, 0, -1)로 직접 지정을 해주면 label이 4, 3, 2, 1 이런식으로 4부터 1씩 줄어드는 순서로 할당이 됩니다. 위의 label 과 정 반대로 할당이 되었습니다. 



bucket_qcut_label_col_2 = pd.qcut(df.col_2, 4, labels=np.arange(4, 0, -1))

bucket_qcut_label_col_2

0     1
1     1
2     4
3     2
4     3
5     1
6     2
7     4
8     3
9     1
10    1
11    4
12    4
13    3
14    3
15    3
16    2
17    2
18    4
19    2
Name: col_2, dtype: category
Categories (4, int64): [4 < 3 < 2 < 1]




그럼 [4 < 3 < 2 < 1] 순서로 동일 개수로 나눈 4개의 그룹별 통계량을 계산해보겠습니다. 



grouped = df.col_2.groupby(bucket_qcut_label_col_2)

grouped.apply(summary_func).unstack()

countmaxmeanminstd
col_2
45.0-0.823598-1.612369-3.0555770.907474
35.0-0.308209-0.522940-0.7776550.201746
25.01.0707810.542247-0.1182010.589903
15.01.8044581.5552651.2326500.262163



'count' 개수가 4개의 각 그룹별로 모두 '5'로서 동일한 것을 알 수 있습니다. 


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


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 Python pandas의 GroupBy operation을 이용해서 그룹 별 가중평균(Group weighted average)을 구하는 방법을 소개하겠습니다. 


앞서 GroupBy 연산자의 원리에서 소개드렸던 것처럼, Split => Apply => Combine 의 절차를 거치면서 각 그룹별 GroupBy 연산을 실행하게 됩니다. 


[GroupBy 를 이용한 그룹별 가중 평균 구하기 절차]




예제로 사용할 'a'와 'b'의 두 개 그룹별로 'value 값'과 'weight 가중치'를 가지고 있는 간단한 데이터 프레임을 만들어보겠습니다. 



import pandas as pd

import numpy as np


df = pd.DataFrame({'grp_col' : ['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b'], 

                   'val' : np.arange(10)+1,                   

                   'weight' : [0.0, 0.1, 0.2, 0.3, 0.4, 0.0, 0.1, 0.2, 0.3, 0.4]})


df

grp_colvalweight
0a10.0
1a20.1
2a30.2
3a40.3
4a50.4
5b60.0
6b70.1
7b80.2
8b90.3
9b100.4



  (1) GroupBy 를 활용하여 그룹 별 가중 평균 구하기


이제 (1) GroupBy 객체를 만들고, (2) 가중평균을 구하는 lambda 함수를 정의한 후에, (3) grouped.apply(function) 로 그룹별 가중평균을 구해보겠습니다. 


 

# group weighted average by category

grouped = df.groupby('grp_col')

weighted_avg_func = lambda g:np.average(g['val'], weights=g['weight'])

grouped.apply(weighted_avg_func)

grp_col
a    4.0
b    9.0
dtype: float64




 (2) 수작업으로 그룹 별 가중 평균 구하기 (Split -> Apply -> Combine) 


위에서 처럼 GroupBy 를 사용하지 않는 다면 아래에 소개한 것처럼 각 그룹별로 하나씩 Split -> Apply 하고 마지막에 Combine 을 해주는 단순 반복작업을 그룹의 개수만큼 해주어야 합니다. 


그룹의 개수가 적으면 할만 한데요, 그룹의 개수가 많아지면 수고롭기도 하고, 자칫 실수도 유발할 수 있으니 위의 (1)번 GroupBy 연산자를 사용하는 방법이 좀더 추천할만 하다고 하겠습니다. 비교를 위해서 소개합니다. 


  • (a) Split


# split

df_a = df[df['grp_col']=='a']

df_b = df[df['grp_col']=='b']

 


 df_a

grp_colvalweight
0a10.0
1a20.1
2a30.2
3a40.3
4a50.4

 df_b

grp_colvalweight
5b60.0
6b70.1
7b80.2
8b90.3
9b100.4



  • (b) Apply


# apply

weighted_avg_a = sum((df_a['val']*df_a['weight']))/sum(df_a['weight'])

weighted_avg_b = sum((df_b['val']*df_b['weight']))/sum(df_b['weight'])



 weighted_avg_a

4.0

 weighted_avg_b

9.0



  • (c) Combine


# combine

weighted_avg_ab = pd.DataFrame({'grp_col': ['a', 'b'], 

                               'weighted_average': [weighted_avg_a, weighted_avg_b]})


weighted_avg_ab

grp_colweighted_average
0a4.0
1b

9.0




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



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 Python pandas의 DataFrame에서 문자열 변수들을 가지고 일부 포맷 변형을 한 후에 새로운 변수를 만드는 방법을 소개하겠습니다. 이게 얼핏 생각하면 쉬울 것 같은데요, 또 한번도 본적이 없으면 어렵습니다. ^^; lambda, apply() 함수와 문자열 처리 메소드 등에 대해서 알고 있으면 이해가 쉽습니다. 



(1) 'id' 변수가 전체 5개 자리가 되도록 왼쪽에 비어있는 부분에 '0'을 채워서 새로운 변수 'id_2' 만들기

    (Left padding with zeros so that make 5 positions)


(2) 새로 만든 'id_2' 변수와 'name' 변수를 각 원소별로 합쳐서 데이터프레임 안에 새로운 변수 'id_name' 만들기

    (element-wise string concatenation with multiple inputs array in pandas DataFrame)






먼저, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 


 

In [1]: import pandas as pd


In [2]: df = pd.DataFrame({'id': [1, 2, 10, 20, 100, 200], 

   ...:                    'name': ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']})


In [3]: df

Out[3]: 

    id name

0    1  aaa

1    2  bbb

2   10  ccc

3   20  ddd

4  100  eee

5  200  fff





  (1) 'id' 변수가 전체 5개 자리가 되도록 왼쪽에 비어있는 부분에 '0'을 채워서 새로운 변수 'id_2' 만들기

     (Left padding with zeros so that make 5 positions)


lambda 로 format() 함수를 만들어서 apply() 로 적용을 하여 5자리 중에서 빈 자리를 '0'으로 채웠습니다.



In [4]: df['id_2'] = df['id'].apply(lambda x: "{:0>5d}".format(x))


In [5]: df

Out[5]: 

    id      name   id_2

0      1   aaa      00001

1      2   bbb     00002

2    10   ccc     00010

3    20  ddd    00020

4  100  eee     00100

5  200  fff      00200



다양한 숫자 포맷(number format) 함수는 https://mkaz.blog/code/python-string-format-cookbook/ 를 참고하세요. 




 (2) 새로 만든 'id_2' 변수와 'name' 변수를 각 원소별로 합쳐서 데이터프레임 안에

새로운 변수 'id_name' 만들기

    (element-wise string concatenation with multiple inputs array in pandas DataFrame)


그리고 역시 lambda 로 '_'를 중간 구분자로 해서 두 변수의 문자열을 결합('_'.join)하는 함수를 정의한 후에 apply() 로 적용하였습니다, 'axis = 1'을 설정해준 점 주의하시기 바랍니다. 


 

In [6]: df['id_name'] = df[['id_2', 'name']].apply(lambda x: '_'.join(x), axis=1)


In [7]: df

Out[7]: 

       id    name   id_2         id_name

0      1    aaa      00001    00001_aaa

1      2    bbb     00002    00002_bbb

2    10    ccc     00010     00010_ccc

3    20   ddd    00020    00020_ddd

4  100   eee    00100     00100_eee

5  200   fff     00200     00200_fff 





여기서 끝내면 좀 허전하고 아쉬우니 몇 가지 데이터 포맷 변경을 더 해보겠습니다.



(3) 'id' 변수의 값을 소숫점 두번째 자리까지 나타내는 새로운 변수 'id_3' 만들기


(4) 'name' 변수의 문자열을 전부 대문자로 바꾼 새로운 변수 'name_3' 만들기


(5) 데이터프레임 안의 'id_3'와 'name_3' 변수를 합쳐서 새로운 변수 'id_name_3' 만들기



(3) 'id' 변수의 값을 소숫점 두번째 자리까지 나타내는 새로운 변수 'id_3' 만들기


"{:.2f}".format() 함수를 사용하여 소숫점 두번째 자리까지 표현하였습니다. 



In [8]: df['id_3'] = df['id'].apply(lambda x: "{:.2f}".format(x))


In [9]: df





  (4) 'name' 변수의 문자열을 전부 대문자로 바꾼 새로운 변수 'name_3' 만들기


upper() 문자열 내장 메소드를 사용하여 소문자를 대문자로 변경하였습니다. 



In [10]: df['name_3'] = df['name'].apply(lambda x: x.upper())


In [11]: df

 




  (5) 데이터프레임 안의 'id_3'와 'name_3' 변수를 합쳐서 새로운 변수 'id_name_3' 만들기



In [14]: df['id_name_3'] = df[['id_3', 'name_3']].apply(lambda x: ':'.join(x), axis=1)


In [15]: df

 



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

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 강산에 2019.12.20 14:24  댓글주소  수정/삭제  댓글쓰기

    예제2번을 해보니 아래와 같이 에러가 발생합니다.
    대괄호에 에러 있다고 하는데 무슨 문제인가요?

    File "<ipython-input-23-a8a9b84b10d5>", line 1
    df['키전산번호']=df[['도면번호','전산번호']].apply(lamda x: ''.join(x),axis=1)
    ^
    SyntaxError: invalid syntax

이번 포스팅에서는 Python pandas의 DataFrame에서 범주형 변수의 항목(class)을 기준 정보(mapping table, reference table)를 이용하여 일괄 변환하는 방법을 소개하겠습니다. 


(1) 범주형 변수의 항목 매핑/변환에 사용한 기준 정보를 dict 자료형으로 만들어 놓고, 


(2) dict.get() 함수를 이용하여 매핑/변환에 사용할 사용자 정의 함수를 만든 후에 


(3) map() 함수로 (2)번에서 만든 사용자 정의 함수를 DataFrame의 범주형 변수에 적용하여 매핑하기



차근차근 예를 들어서 설명해보겠습니다. 


먼저, 간단한 예제 데이터프레임을 만들어보겠습니다. 



import pandas as pd

from pandas import DataFrame


df = DataFrame({'name': ['kim', 'KIM', 'Kim', 'lee', 'LEE', 'Lee', 'wang', 'hong'], 

                'value': [1, 2, 3, 4, 5, 6, 7, 8], 

                'value_2': [100, 300, 200, 100, 100, 300, 50, 80]

               })


df

namevaluevalue_2
0kim1100
1KIM2300
2Kim3200
3lee4100
4LEE5100
5Lee6300
6wang750
7hong880

 




위의 df 라는 이름의 DataFrame에서, name 변수의 (kim, KIM, Kim) 를 (kim)으로, (lee, LEE, Lee)를 (lee)로, 그리고 (wang, hong)을 (others) 라는 항목으로 매핑하여 새로운 변수 name_2 를 만들어보려고 합니다. 



  (1) 범주형 변수의 항목 매핑/변환에 사용할 기준 정보를 dict 자료형으로 만들기



name_mapping = {

    'KIM': 'kim',

    'Kim': 'kim', 

    'LEE': 'lee', 

    'Lee': 'lee', 

    'wang': 'others', 

    'hong': 'others'

}


name_mapping

 {'KIM': 'kim',

 'Kim': 'kim',
 'LEE': 'lee',
 'Lee': 'lee',
 'hong': 'others',
 'wang': 'others'}




  (2) dict.get() 함수를 이용하여 매핑/변환에 사용할 사용자 정의 함수 만들기


dict 자료형에 대해 dict.get() 함수를 사용하여 정의한 아래의 사용자 정의 함수 func는 '만약 매핑에 필요한 정보가 기준 정보 name_mapping dict에 있으면 그 정보를 사용하여 매핑을 하고, 만약에 기준정보 name_mapping dict에 매핑에 필요한 정보가 없으면 입력값을 그대로 반환하라는 뜻입니다. 'lee', 'kim'의 경우 위의 name_mapping dict 기준정보에 매핑에 필요한 정보항목이 없으므로 그냥 자기 자신을 그대로 반환하게 됩니다. 



func = lambda x: name_mapping.get(x, x)

 




  (3) map() 함수로 매핑용 사용자 정의 함수를 DataFrame의 범주형 변수에 적용하여 매핑/변환하기


위의 기준정보 name_mapping dict를 사용하여 'name_2' 라는 이름의 새로운 범주형 변수를 만들어보았습니다. 



df['name_2'] = df.name.map(func)


df

namevaluevalue_2name_2
0kim1100kim
1KIM2300kim
2Kim3200kim
3lee4100lee
4LEE5100lee
5Lee6300lee
6wang750others
7hong880others

 




  (4) groupby() 로 범주형 변수의 그룹별로 집계하기


범주형 변수에 대해서 항목을 매핑/변환하여 새로운 group 정보를 만들었으니, groupby() operator를 사용해서 새로 만든 name_2 변수별로 연속형 변수들('value', 'value_2')의 합계를 구해보겠습니다. 



# aggregation by name

df.groupby('name_2').sum()

valuevalue_2
name_2
kim6600
lee15500
others15130

 




'name_2'와 'name' 범주형 변수 2개를 groupby()에 함께 사용하여 두개 범주형 변수의 계층적인 인덱스(hierarchical index) 형태로 'value_2' 연속형 변수에 대해서만 합계를 구해보겠습니다. (아래의 결과에 대해 unstack()을 하면 name 변수를 칼럼으로 올려서 cross-tab 형태로 볼 수도 있겠습니다.)



df.groupby(['name_2', 'name'])['value_2'].sum()

name_2  name
kim     KIM     300
        Kim     200
        kim     100
lee     LEE     100
        Lee     300
        lee     100
others  hong     80
        wang     50
Name: value_2, dtype: int64

 



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


Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 GroupBy 를 사용하여 그룹별로 반복 작업(iteration over groups)하는 방법을 소개하겠습니다. 

pandas의 GroupBy 객체는 for loop 반복 시에 그룹 이름과 그룹별 데이터셋을 2개의 튜플로 반환합니다. 이러한 특성을 잘 활용하면 그룹별로 for loop 반복작업을 하는데 유용하게 사용할 수 있습니다. 


[ GroupBy로 그룹별로 반복 작업하기 ]




예제로 사용할 데이터는 UCI machine learning repository에 등록되어 있는 abalone 공개 데이터셋입니다. 



import numpy as np

import pandas as pd


abalone = pd.read_csv("/Users/ihongdon/Documents/Python/abalone.txt", 

                      sep=",", 

                      names = ['sex', 'length', 'diameter', 'height', 

                               'whole_weight', 'shucked_weight', 'viscera_weight', 

                               'shell_weight', 'rings'], 

                      header = None)



abalone['length_cat'] = np.where(abalone.length > np.median(abalone.length), 

                                 'length_long', 

                                 'length_short')



abalone.head()

sexlengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightringslength_cat
0M0.4550.3650.0950.51400.22450.10100.15015length_short
1M0.3500.2650.0900.22550.09950.04850.0707length_short
2F0.5300.4200.1350.67700.25650.14150.2109length_short
3M0.4400.3650.1250.51600.21550.11400.15510length_short
4I0.3300.2550.0800.20500.08950.03950.0557length_short





위의 abalone 데이터셋을 '성별(sex)'로 GroupBy를 한 후에, for loop을 돌려서 그룹 이름(sex: 'F', 'I', 'M')별로 데이터셋을 프린트해보겠습니다. 



for sex, group_data in abalone[['sex', 'length_cat', 'whole_weight', 'rings']].groupby('sex'):

    print sex

    print group_data[:5]

 

F    sex    length_cat  whole_weight  rings

2 F length_short 0.6770 9 6 F length_short 0.7775 20 7 F length_short 0.7680 16 9 F length_long 0.8945 19 10 F length_short 0.6065 14

I    sex    length_cat  whole_weight  rings
4    I  length_short        0.2050      7
5    I  length_short        0.3515      8
16   I  length_short        0.2905      7
21   I  length_short        0.2255     10
42   I  length_short        0.0700      5

M    sex    length_cat  whole_weight  rings
0    M  length_short        0.5140     15
1    M  length_short        0.2255      7
3    M  length_short        0.5160     10
8    M  length_short        0.5095      9
11   M  length_short        0.4060     10





이번에는 두 개의 범주형 변수(sex, length_cat)를 사용하여 for loop 반복문으로 그룹 이름 (sex와 leggth_cat 의 조합: F & length_long, F & length_short, I & length_long, I & length_short, M & length_long, M & length_short)과 각 그룹별 데이터셋을 프린트해보겠습니다. 


참고로, 아래 코드에서 '\' 역슬래쉬는 코드를 한줄에 전부 다 쓰기에 너무 길 때 다음줄로 코드를 넘길 때 사용합니다. 



for (sex, length_cat), group_data in abalone[['sex', 'length_cat', 'whole_weight', 'rings']]\

.groupby(['sex', 'length_cat']):

    print sex, length_cat

    print group_data[:5]

 

F length_long
   sex   length_cat  whole_weight  rings
9    F  length_long        0.8945     19
22   F  length_long        0.9395     12
23   F  length_long        0.7635      9
24   F  length_long        1.1615     10
25   F  length_long        0.9285     11
F length_short
   sex    length_cat  whole_weight  rings
2    F  length_short        0.6770      9
6    F  length_short        0.7775     20
7    F  length_short        0.7680     16
10   F  length_short        0.6065     14
13   F  length_short        0.6845     10
I length_long
    sex   length_cat  whole_weight  rings
509   I  length_long        0.8735     16
510   I  length_long        1.1095     10
549   I  length_long        0.8750     11
550   I  length_long        1.1625     17
551   I  length_long        0.9885     13
I length_short
   sex    length_cat  whole_weight  rings
4    I  length_short        0.2050      7
5    I  length_short        0.3515      8
16   I  length_short        0.2905      7
21   I  length_short        0.2255     10
42   I  length_short        0.0700      5
M length_long
   sex   length_cat  whole_weight  rings
27   M  length_long        0.9310     12
28   M  length_long        0.9365     15
29   M  length_long        0.8635     11
30   M  length_long        0.9975     10
32   M  length_long        1.3380     18
M length_short
   sex    length_cat  whole_weight  rings
0    M  length_short        0.5140     15
1    M  length_short        0.2255      7
3    M  length_short        0.5160     10
8    M  length_short        0.5095      9
11   M  length_short        0.4060     10





다음으로, 성별(sex)로 GroupBy를 해서 성별 그룹('F', 'I', 'M')을 key로 하고, 데이터셋을 value로 하는 dict를 만들어보겠습니다. 



abalone_sex_group = dict(list(abalone[:10][['sex', 'length_cat', 'whole_weight', 'rings']]

                              .groupby('sex')))

 

abalone_sex_group


{'F':   sex    length_cat  whole_weight  rings
 2   F  length_short        0.6770      9
 6   F  length_short        0.7775     20
 7   F  length_short        0.7680     16
 9   F   length_long        0.8945     19,
 'I':   sex    length_cat  whole_weight  rings
 4   I  length_short        0.2050      7
 5   I  length_short        0.3515      8,
 'M':   sex    length_cat  whole_weight  rings
 0   M  length_short        0.5140     15
 1   M  length_short        0.2255      7
 3   M  length_short        0.5160     10
 8   M  length_short        0.5095      9}





이렇게 그룹 이름을 key로 하는 dict 를 만들어놓으면 그룹 이름을 가지고 데이터셋을 indexing하기에 편리합니다.  예로 성별 중에 'M'인 데이터셋을 indexing해보겠습니다. 



abalone_sex_group['M'] 

sexlengthdiameterheightwhole_weightshucked_weightviscera_weightshell_weightringslength_cat
0M0.4550.3650.0950.51400.22450.10100.15015length_short
1M0.3500.2650.0900.22550.09950.04850.0707length_short
3M0.4400.3650.1250.51600.21550.11400.15510length_short
8M0.4750.3700.1250.50950.21650.11250.1659length_short




물론 abalone[:10][abalone['sex'] == 'M']  처럼 원래의 처음 abalone 데이터프레임에 boolean 형태로 indexing을 해도 됩니다. 대신에 dict 로 만들어놓으면 데이터셋 indexing 하는 속도가 더 빠를겁니다. 


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

Posted by R Friend R_Friend

댓글을 달아 주세요