본문 바로가기
엑셈 경쟁력/시계열 데이터처리 AI 알고리즘

Chapter 2-2. 시계열 데이터 전처리

by EXEM 2022. 9. 28.

Chapter 2-2. 시계열 데이터 전처리

 

앞서 Chapter 2에서는 numpypandas를 이용하여 데이터 구조를 변형하는 방법에 대하여 알아보았다. 이번 챕터에서는 pandas에서 제공하는 시계열 도구와 가장 널리 쓰이는 타임스탬프(timestamp)라는 구조를 사용하여 설명할 것이다.

 

먼저, Datetime이라는 파이썬 라이브러리에서 제공하는 datetime을 사용하여 현재 날짜 및 시간을 알아보고, timedelta를 사용하여 시간적 차이를 표현해보자.

 

In [1]:

from datetime import datetime

now = datetime.now()

now

Out[1]:

datetime.datetime(2022, 5, 20, 11, 42, 5, 90657)

 

Timedelta를 이용하여 우리는 시간을 더하거나 뺄 수 있다.

 

In[2]:

from datetime import timedelta

start = datetime(2022, 5, 20)

start + timedelta(2)

 Out[2]:

datetime.datetime(2022, 5, 22, 0, 0)

 

In[3]:

start - 4 * timedelta(3)

Out[3]:

datetime.datetime(2022, 5, 8, 0, 0)

 

-      문자열을 datetime으로, datetime을 문자열로 변환하기

 

str 메서드나 strftime 메서드를 이용하여 우리는 datetime 또는 timestamp 객체를 문자열로 표현할 수 있다.


위에서 살펴보았듯이, now datetime 메서드를 이용하여 현재의 날짜로 지정한 후, 이를 str 또는 strftime 메서드를 사용하여 문자열로 나타내 보자.

 

In[4]:

now = datetime(2022, 5, 20)

str(now)

Out[4]:

'2022-5-20 00:00:00'

 

In[5]:

now = dateitme(2022, 5, 20)

now.strftime('%Y-%m-%d')

Out[5]:

'2022-05-20'

 

사용하는 데이터의 형식이 문자열일 때, 우리는 datetime.strptime을 사용하여 문자열을 날짜 형식으로 변환할 수 있다.

 

In[6]:

now = '2022-05-20'

datetime.strptime(now, '%Y-%m-%d')

Out[6]:

datetime.datetime(2022, 5, 20, 0 , 0)

 

문자열을 날자로 변환하는 다른 방법으로는 dateutilparser.parse 메서드를 사용하는 것이다.

 

In[7]:

from dateutil.parser import parse

parse('2022-05-20')

Out[7]:

datetime.datetime(2022, 5, 20, 0, 0)

 

dateutil 혹은 pandas.to_datetime에서 dayfirstdefaultFalse로 되어있는데, dayfirst=True는 일 단위의 날짜가 먼저 올 때 YYYY-MM-DD 형식으로 파싱한다.

 

In[8]:

parse('20/5/2022', dayfirst=True)

Out[9]:

datetime.datetime(2022, 5, 20, 0 , 0)


Pandasdatetime객체로 표현된 list_of_dates라는 타임스템프 리스트를 생성한 후에, 이를 random_ts라는 pandas.Series 객체에 랜덤한 값을 색인해보자.

 

In[10]:

from datetime import datetime

list_of_dates = [datetime(2022, 5, 20), datetime(2022, 6, 20), datetime(2022, 7, 20)]

list_of_dates

Out[10]:

[datetime.datetime(2022, 5, 20, 0, 0),
 datetime.datetime(2022, 6, 20, 0, 0),
 datetime.datetime(2022, 7, 20, 0, 0)]

 

In[11]:

import numpy as np
import pandas as pd

random_ts = pd.Series(np.random.randn(3), index=list_of_dates)

random_ts

Out[11]:

2022-05-20    0.774389
2022-06-20   -0.382303
2022-07-20   -0.022317
dtype: float64

 

앞으로 알고리즘을 만들게 된다면, 위와 같이 Series 객체로 시계열 데이터가 들어오는 경우가 많다. 우리는 앞서 Chapter 2에서 배운 concatenate로 다른 Series 객체를 이어볼 수 있다.

 

다른 Series 객체를 random_ts_2라 하자.

 

In[12]:

random_ts_2 = pd.Series(np.random.randn(3), index=list_of_dates)

random_ts_2

Out[12]:

2022-05-20    1.176073
2022-06-20   -1.065495
2022-07-20   -1.599496
dtype: float64

 

In[13]:

ts_concat = pd.concat([random_ts, random_ts_2], axis=0)

ts_concat

Out[13]:

2022-05-20    0.774389
2022-06-20   -0.382303
2022-07-20   -0.022317
2022-05-20    1.176073
2022-06-20   -1.065495
2022-07-20   -1.599496
dtype: float64

In[14]:

ts_concat_2 = pd.concat([random_ts, random_ts_2], axis=1)

ts_concat_2

Out[14]:

	            0	             1
2022-05-20	 0.774389	 1.176073
2022-06-20	-0.382303	-1.065495
2022-07-20	-0.022317	-1.599496

 

-      인덱싱, (부분)선택

 

TimeSeries에서의 인덱싱은 Series와 같은 방법으로 표현할 수 있다. 이는 SeriesTimeSeries의 상위 클래스이기 때문이다.

 

In[15]:

third_index = random_ts.index[2]

third_index

Out[15]:

Timestamp('2022-07-20 00:00:00')

 

In[16]:

random_ts[third_index]

Out[16]:

-0.022316737620105935

 

년 단위, 혹은 월 단위의 긴 시계열을 생성하고 싶을 때, Pandas.date_range를 이용하면 쉽게 표현할 수 있다. Pandas documentation에서 확인할 수 있듯이, date_range의 파라미터 중에는 start, end, periods 등이 있는데, 각각 시작날짜, 끝 날짜, 날짜를 생성할 기간을 지정한다.

 

아래의 예제에서는 시작 날짜를 기준으로 일 년의 데이터를 랜덤으로 색인해보겠다.

 

In[17]:

date = pd.Series(np.random.randn(365), index=pd.date_range('2050-01-01', periods=365))

date

Out[17]:

2050-01-01   -0.494604
2050-01-02    0.135350
2050-01-03   -1.283357
2050-01-04    0.347170
2050-01-05    0.525342
                ...   
2050-12-27    0.626219
2050-12-28    0.474690
2050-12-29    0.199873
2050-12-30   -1.765469
2050-12-31   -0.617391
Freq: D, Length: 365, dtype: float64

 

이는 끝 날짜를 지정하여 똑같이 표현할 수 있다.

 

In[18]:

date = pd.Series(np.random.randn(365), index=pd.date_range('1/1/2050','12/31/2050'))

date

Out[18]:

2050-01-01   -0.001203
2050-01-02   -0.418517
2050-01-03    0.078868
2050-01-04   -0.887272
2050-01-05    0.410002
                ...   
2050-12-27    1.818385
2050-12-28    0.494894
2050-12-29    0.807756
2050-12-30   -0.925153
2050-12-31    1.766654
Freq: D, Length: 365, dtype: float64

 

생성한 date 객체에서 12월의 데이터만 보고 싶으면 아래와 같은 방식으로 할 수 있다. 만약 생성한 데이터가 1년을 넘어가면 같은 방법으로 특정 년도만 구분할 수 있다.

 

In[19]:

date['2050-12']

Out[19]:

2050-12-01    2.159670
2050-12-02   -0.293466
2050-12-03   -0.544400
2050-12-04    0.233726
2050-12-05   -0.602523
2050-12-06    0.708593
2050-12-07   -0.310918
2050-12-08    0.825040
2050-12-09   -0.072993
2050-12-10    0.817232
2050-12-11   -0.057526
2050-12-12   -1.725296
2050-12-13   -0.210941
2050-12-14    0.043790
2050-12-15    1.441651
2050-12-16    1.129214
2050-12-17    1.309630
2050-12-18   -1.545096
2050-12-19    0.379403
2050-12-20    0.297209
2050-12-21   -0.642082
2050-12-22   -0.791176
2050-12-23    1.244942
2050-12-24    0.863635
2050-12-25   -1.715502
2050-12-26    0.099707
2050-12-27   -0.223460
2050-12-28   -1.267077
2050-12-29    0.325684
2050-12-30    0.745202
2050-12-31    0.353581
Freq: D, dtype: float64


앞서 배운 슬라이싱으로 원하는 날짜로 데이터를 잘라내 보자.

 

In[20]:

date[:'2050-1-15']

Out[20]:

2050-01-01   -0.001203
2050-01-02   -0.418517
2050-01-03    0.078868
2050-01-04   -0.887272
2050-01-05    0.410002
2050-01-06   -0.979856
2050-01-07   -0.751049
2050-01-08    0.289133
2050-01-09    1.623377
2050-01-10   -0.332500
2050-01-11   -0.651282
2050-01-12    0.238200
2050-01-13    0.091797
2050-01-14   -1.865492
2050-01-15   -2.270227
Freq: D, dtype: float64

 

Pandas.DataFrame.truncate을 이용하면 우리는 NumPy 배열을 나누는 것과 동일하게 표현할 수 있는데, 이는 before, after 등의 파라미터로 범위를 지정하면 된다.

 

예를 들어, truncate 메서드를 사용하여 위의 date 2050-01-15까지 슬라이싱한 것과 같은 결과를 도출해내 보자.

 

In[21]:

date.truncate(after='1/15/2050')

Out[21]:

2050-01-01   -0.001203
2050-01-02   -0.418517
2050-01-03    0.078868
2050-01-04   -0.887272
2050-01-05    0.410002
2050-01-06   -0.979856
2050-01-07   -0.751049
2050-01-08    0.289133
2050-01-09    1.623377
2050-01-10   -0.332500
2050-01-11   -0.651282
2050-01-12    0.238200
2050-01-13    0.091797
2050-01-14   -1.865492
2050-01-15   -2.270227
Freq: D, dtype: float64

 

Chapter 2에서 배운 .loc.iloc을 복습해보자면, string형태로 레이블링 할 때에는 .loc, int형태로 레이블링 할 때에는 .iloc을 이용했다. 더 나아가, .loc은 처음과 끝이 포함되고 .iloc은 처음만 포함되며 끝은 포함이 안된다. DataFramepandas.ix라는 메서드를 사용하여 인덱싱 하는 방법 또한 있다. .ix 메서드는 .loc, .iloc과 마찬가지로 대괄호를 사용한다. 그러나 .ix pandas version 1.0.0부터는 삭제된 기능이므로 .loc으로 실습해보자.

.loc을 하기 전에 date_range2022-01-01에 시작하는 DataFrame을 먼저 생성하자.

 

In[22]:

date = pd.date_range('1/1/2022', periods = 50, freq='M')

df = pd.DataFrame(np.random.randn(50, 3), index=date, columns=['Seattle', 'New York', 'Seoul'])

pd.set_option('display.max_row', 10) #중간 값 숨김처리

df

Out[22]:

                 Seattle	 New York	  Seoul
2022-01-31	 0.048613	-1.939469	-0.010339
2022-02-28	-0.222063	-1.466078	 0.239327
2022-03-31	 2.025250	 0.222893	-0.877616
2022-04-30	-1.799358	-0.298335	 0.421221
2022-05-31	-0.853737	-0.398597	 1.333171
...	...	...	...
2025-10-31	 1.042705	 0.183116	-1.449736
2025-11-30	 1.494143	-3.169005	-0.480136
2025-12-31	-0.391582	-0.710749	-0.333553
2026-01-31	 0.830946	 1.469039	-1.501097
2026-02-28	 1.133783	 2.391733	 0.491374
50 rows × 3 columns

 

이제 .loc를 사용하여 2022년의 값들만 도출해내 보자.

 

 In[23]:

df.loc['2022']

Out[23]:

            	 Seattle	 New York	  Seoul
2022-01-31	-0.527381	 2.223938	 0.283217
2022-02-28	 0.688495	 1.000875	 0.225447
2022-03-31	 0.468626	-0.082935	-0.011451
2022-04-30	 0.621932	-0.197362	 0.562728
2022-05-31	-0.361838	-0.586796	 0.424908
...	...	...	...
2022-08-31	 1.751512	 1.338711	-0.965767
2022-09-30	 0.791905	-0.282237	-0.007144
2022-10-31	-0.560612	 0.725165	-0.590959
2022-11-30	-0.916942	-1.064896	-0.357532
2022-12-31	 2.135097	-0.936907	-1.101876
12 rows × 3 columns

 

-      중복 또는 NaN 색인을 갖는 시계열

 

매초//일 등으로 들어오는 시계열 데이터 구조에서는 보기 힘들 수 있지만, 특정 타임스템프에 중복으로 들어오는 시계열 데이터가 있을 수 있다. 이때 우리는 is_unique 속성을 확인하여 어떤 색인이 유일한지, 유일하지 않은지 확인할 수 있다. Is_unique를 실행하기 전 Series를 먼저 생성하자. 앞서 우리는 리스트 형태의 데이터를 생성한 후 Series를 만들었는데, 이번에는 pandas.DatetimeIndex를 이용하여 데이터를 생성하는 방법을 알아보자. Pandas.DatetimeIndex의 성질 중 하나는 튜플과 같이 value 수정이 불가능하다는 점이다.

 

In[24]:

data = pd.DatetimeIndex(['12/25/2022' ,'12/25/2022', '12/25/2023', '12/25/2023'])

dup_data = pd.Series(np.arange(4), index=data)

dup_data

Out[24]:

2022-12-25    0
2022-12-25    1
2023-12-25    2
2023-12-25    3
dtype: int32

 

이제 is_unique 속성을 확인하여 색인이 유일한지 알아보자.

 

In[25]:

dup_data.index.is_unique

Out[25]:

False

 

Is_unique로 색인이 유일하지 않음을 알았으니, 각 날짜별로 색인이 몇 개가 중복되는지 확인해보자.

 

In[26]:

dup_data['12/25/2022']

Out[26]:

2022-12-25    0
2022-12-25    1
dtype: int32

 

In[27]:

dup_data['12/25/2023']

Out[27]:

2023-12-25    2
2023-12-25    3
dtype: int32

 

만약 어떤 색인이 있는지 확인하여 중복 여부를 판단하는 작업이 귀찮다면, groupbylevel=0(단일 단계 인덱싱)을 사용할 수 있다.

 

In[28]:

grouped_data = dup_data.groupby(level=0)

grouped_data.count()

Out[28]:

2022-12-25    2
2023-12-25    2
dtype: int64

 

In[29]:

grouped_data.mean()

Out[29]:

2022-12-25    0.5
2023-12-25    2.5
dtype: float64

 

시계열은 규칙적으로 데이터(날짜, 수치 등)가 들어올 수 있지만, 일반적으로 pandas에서는 불규칙적으로 들어오는 것으로 간주한다. 따라서, pandas에는 리샘플링, 빈도 등에 관한 도구들이 존재한다. 그 중 한 예로 resample이라는 메서드로 우리는 타임스템프에 대한 빈도 변환을 할 수 있다.

ts1/1/2022부터 일 빈도로 6개의 datetime이라 정의하자. 그 후 series를 만들어 2일씩 값을 묶어 더해보자.

 

In[30]:

ts = pd.date_range('1/1/2022', periods=6, freq='D')

ts

Out[30]:

DatetimeIndex(['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04',
               '2022-01-05', '2022-01-06'],
              dtype='datetime64[ns]', freq='D')

 

In[31]:

ts_series = pd.Series(range(6), index=ts)

ts_series

Out[31]:

2022-01-01    0
2022-01-02    1
2022-01-03    2
2022-01-04    3
2022-01-05    4
2022-01-06    5
Freq: D, dtype: int64

 

In[32]:

ts_series.resample('2D').sum()

Out[32]:

2022-01-01    1
2022-01-03    5
2022-01-05    9
Freq: 2D, dtype: int64

 

앞에서 살펴보았던 padas.date_range를 사용하면 특정한 빈도(frequency)pandas.DatetimeIndex를 생성한다는 것을 눈치챘을 것이다. date_range는 기본적으로 일별 타임스템프를 생성하는데, 시작하는 날짜나 끝나는 날짜만 파라미터로 지정했을 때에는 생성할 기간(periods)을 정해줘야만 한다.

 

In[33]:

pd.date_range(start='1/1/2022', periods=5)

Out[33]:

DatetimeIndex(['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04',
               '2022-01-05'],
              dtype='datetime64[ns]', freq='D')

 

In[34]:

pd.date_range(end='1/5/2022', periods=5)

Out[34]:

DatetimeIndex(['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04',
               '2022-01-05'],
              dtype='datetime64[ns]', freq='D')

 

Pandas documentation에서 확인할 수 있는 time series offset aliases를 통해 자유롭게 작업할 수 있다.

 

Date Offset Frequency String Description
Day ‘D’ 달력상의 일
Hour ‘H’ 매 시간
Minute ‘T’ or ‘min’ 매 분
Second ‘S’ 매 초
Milli ‘L’ or ‘ms’ 밀리초
Micro ‘U’ or ‘us’ 마이크로초
Nano ‘N’ 나노초
BDay or BusinessDay ‘B’ 매 영업일
MonthEnd ‘M’ 월 마지막 일
MonthBegin ‘MS’ 월 시작일
BusinessMonthEnd ‘BM’ 월 영업 마감일
BusinessMonthBegin ‘BMS’ 월 영업 시작일
Week ‘W’ 요일, ex) W-MON, W-TUE, …
WeekOfMonth ‘WOM’ 월별 주차와 요일, ex) WOM-2WED : 매월 둘째 주 수요일
   

(출처: pandas documentation, https://pandas.pydata.org/docs/user_guide/timeseries.html)

 

 

WOM을 이용하여 2022/1/1부터 2022/12/31 사이에 매월 둘째 주 수요일만을 도출해내보자.

 

In[35]:

ts = pd.date_range('1/1/2022', '12/31/2022', freq='WOM-2WED')

ts

Out[35]:

DatetimeIndex(['2022-01-12', '2022-02-09', '2022-03-09', '2022-04-13',
               '2022-05-11', '2022-06-08', '2022-07-13', '2022-08-10',
               '2022-09-14', '2022-10-12', '2022-11-09', '2022-12-14'],
              dtype='datetime64[ns]', freq='WOM-2WED')

 

Pandas.period 클래스를 사용하면 정해진 빈도에 따라 기간을 자유롭게 이동시킬 수 있다. 아래 예제에서 나오는 ‘A-JAN’, ‘A-FEB’, … 의 형식은 주어진 월의 마지막 날을 의미한다.

 

In[36]:

a = pd.Period(2022, 'A-JAN')

a

Out[36]:

Period('2022', 'A-JAN')

 

In[37]:

a + 10

Out[37]:

Period('2032', 'A-JAN')

 

In[38]:

a - 20

Out[38]:

Period('2002', 'A-JAN')

 

이렇게 Period 객체를 만들어 정수를 더하거나 빼며 계산할 수 있다.

Pandas.PeriodIndex 클래스를 사용하여 문자열 배열 또한 객체화 할 수 있다. Period_range 함수를 이용하면 기간 범위를 생성할 수 있는데, period_range를 먼저 살펴보고 PeriodIndex 클래스로 인덱스를 만들어보자.

 

In[39]:

p_rng = pd.period_range('1/1/2022', '12/31/2022', freq='M')


p_rng

Out[39]:

PeriodIndex(['2022-01', '2022-02', '2022-03', '2022-04', '2022-05', '2022-06',
             '2022-07', '2022-08', '2022-09', '2022-10', '2022-11', '2022-12'],
            dtype='period[M]', freq='M')

 

In[40]:

pd.Series(np.random.randn(12), index=p_rng)

Out[40]:

2022-01   -0.136206
2022-02   -0.675644
2022-03   -1.167287
2022-04    1.078958
2022-05   -0.471014
             ...   
2022-08    0.943139
2022-09    2.255960
2022-10   -0.047296
2022-11   -0.127781
2022-12   -1.705026
Freq: M, Length: 12, dtype: float64

 

이제 문자열로 이루어진 value들로 PeriodIndex 클래스를 생성해보자. 아래 예제에서 나오는 ‘Q-JAN’, ‘Q-FEB’, … 의 형식은 지정된 월을 연도의 마감으로 한다. 많은 회사 또는 기관에서는 분기로 연도의 시작이나 끝을 정하는데, 2022년의 첫번째 분기가 2021에 시작하였을 때 이 frequency를 사용하면 된다.

 

In[41]:

values = ['2022Q1', '2032Q4', '2042Q1']

PI = pd.PeriodIndex(values, freq='Q-JAN')

PI

Out[41]:

PeriodIndex(['2022Q1', '2032Q4', '2042Q1'], dtype='period[Q-JAN]', freq='Q-JAN')

 

Period의 빈도는 asfreq 메서드를 통해 변환할 수 있다. 먼저 freq=’A-JAN’, ‘A-‘FEB’, … 로 설정한 후, 연간 빈도를 원간 빈도로 변환해보자.

 

In[42]:

PI = pd.Period('2022', freq='A-JUN')

PI.asfreq('M', how='start')

Out[42]:

Period('2021-07', 'M')

 

In[43]:

PI.asfreq('M', how='end')

Out[43]:

Period('2022-06', 'M')

 

이렇게 월간 빈도로 변환한 후 시작과 끝 월을 확인할 수 있다. 만약 A-JUN 빈도일 때 2022 7월은 2023년 기간에 속하게 된다.

 

-      타임스템프를 Period, Period를 타임스템프로 변환하기

 

먼저, date_range를 사용하여 타임스템프로 색인된 Series를 만들어보자. 다음 우리는 to_period 메서드를 사용하여 Period로 변환할 수 있다.

 

In[44]:

dates = pd.date_range('1/1/2022', periods=5, freq='M')

dates

Out[44]:

DatetimeIndex(['2022-01-31', '2022-02-28', '2022-03-31', '2022-04-30',
               '2022-05-31'],
              dtype='datetime64[ns]', freq='M')

 

In[45]:

ts = pd.Series(np.random.randn(5), index=dates)

ts

Out[45]:

2022-01-31    0.043213
2022-02-28   -1.114810
2022-03-31    0.639867
2022-04-30   -0.883854
2022-05-31    1.005160
Freq: M, dtype: float64

 

In[46]:

ts_period = ts.to_period()

ts_period

Out[46]:

2022-01    0.043213
2022-02   -1.114810
2022-03    0.639867
2022-04   -0.883854
2022-05    1.005160
Freq: M, dtype: float64

 

To_period()에는 새로운 빈도를 직접 지정하여 기간의 중복여부와 관계없이 값을 반환할 수 있다.

Date_range로는 일 단위의 빈도로 Series 객체를 생성한 후, Period로 변환할 때는 월 빈도로 변경해보자.

 

In[47]:

dates = pd.date_range('3/28/2022', periods=9, freq='D')

ts = pd.Series(np.random.randn(9), index=dates)

ts.to_period('M')

Out[47]:

2022-03    0.658025
2022-03    0.221905
2022-03   -0.178715
2022-03    1.294563
2022-04   -1.508884
2022-04    0.515005
2022-04    0.614365
2022-04   -0.145639
2022-04    0.435093
Freq: M, dtype: float64

 

지금까지는 타임스탬프를 Period로 변환하는 것에 대해 알아보았는데, 이제 반대로 Period를 타임스탬프로 변환하는 것에 대해 알아보자. to_timestamp 메서드를 사용하면 간단하게 변환할 수 있다. 위 예제 중 ts_period를 다시 타임스탬프로 변환해보자.

 

In[48]:

ts_period.to_timestamp(how='end')

Out[48]:

2022-01-31 23:59:59.999999999    0.043213
2022-02-28 23:59:59.999999999   -1.114810
2022-03-31 23:59:59.999999999    0.639867
2022-04-30 23:59:59.999999999   -0.883854
2022-05-31 23:59:59.999999999    1.005160
dtype: float64

 

시계열의 빈도를 변환하는 과정을 우리는 리샘플링(Resampling)이라 한다. 리샘플링에는 업샘플링과 다운샘플링이 있다. 업샘플링(Up-sampling)은 하위 빈도의 데이터를 상위 빈도로 집계하는 것을 일컫고, 다운샘플링(Down-sampling)은 그 반대를 말한다. 그러나 W-MON(월요일을 기준으로 한 주간)W-WED(수요일을 기준으로 한 주간)으로 변경하는 것은 두 범주에 들어가지 않는다. 따라서 모든 리샘플링이 업샘플링이나 다운샘플링으로만 구분되는 것은 아니다.

앞에서 잠깐 다뤘던 resample 메서드는 빈도 변환에 있어서 pandas에서 제공하는 도구 중 큰 시계열 데이터를 처리할 때 유용하게 쓰이는 메서드이다.

다운샘플링의 예를 먼저 들어보자. 앞서 말했듯이 다운샘플링은 상위 빈도의 데이터를 하위 빈도로 집계하는 것을 말한다. 만약 1분 단위로 들어오는 데이터가 있을 때, 10분 단위로 묶어서 각 그룹의 합을 집계해보자.

 

In[49]:

data = pd.date_range('1/1/2022', periods=20, freq='T')

ts = pd.Series(np.random.randn(20), index=data)

ts

Out[49]:

2022-01-01 00:00:00    0.122277
2022-01-01 00:01:00   -1.229143
2022-01-01 00:02:00   -0.408663
2022-01-01 00:03:00   -0.703140
2022-01-01 00:04:00    1.339576
2022-01-01 00:05:00   -0.433361
2022-01-01 00:06:00    0.540495
2022-01-01 00:07:00   -1.367583
2022-01-01 00:08:00   -1.342392
2022-01-01 00:09:00    0.174749
2022-01-01 00:10:00   -0.697325
2022-01-01 00:11:00   -0.967903
2022-01-01 00:12:00   -0.211787
2022-01-01 00:13:00    0.803688
2022-01-01 00:14:00    0.173893
2022-01-01 00:15:00    1.524987
2022-01-01 00:16:00   -0.646593
2022-01-01 00:17:00   -0.070960
2022-01-01 00:18:00    0.535796
2022-01-01 00:19:00    0.075011
Freq: T, dtype: float64

 

이제 resample 함수를 이용하여 다운샘플링을 진행해보자.

 

In[50]:

ts.resample('10T').sum()

Out[50]:

2022-01-01 00:00:00   -3.307185
2022-01-01 00:10:00    0.518807
Freq: 10T, dtype: float64

 

Resample의 파라미터 중 closed의 값에 ’right’을 넘기면 시작값을 오른쪽으로 포함시킨다. 이해하기 쉽게 20분의 데이터를 3분 단위로 묶어서 각 그룹의 합을 집계하되, closed=’right’을 지정하여 오른쪽에서부터 시작하여 결과를 도출해보자.

 

In[51]:

ts.resample('3min', closed='right').sum()

Out[51]:

2021-12-31 23:57:00    0.122277
2022-01-01 00:00:00   -2.340946
2022-01-01 00:03:00    1.446710
2022-01-01 00:06:00   -2.535227
2022-01-01 00:09:00   -1.877015
2022-01-01 00:12:00    2.502568
2022-01-01 00:15:00   -0.181757
2022-01-01 00:18:00    0.075011
Freq: 3T, dtype: float64

 

위 결과와 반대로 시작 값을 왼쪽에 포함시켰을 때에는 첫 번째 그룹은 00:00:00 부터 00:03:00 까지의 값을 집계했을 것이다.

이제 업샘플링과 보간에 대해서 알아보자. 업샘플링을 하면 누락되는 데이터 값이 발생한다. 따라서 이전 값을 채워서 보간을 하는 ffill을 사용해도 되고, pandas.DataFrame.interpolate 을 사용해도 된다.

 

보간에 앞서 업샘플링을 먼저 해보자.

 

In[52]:

df = pd.DataFrame(np.random.randn(2, 3),
                 index=pd.date_range('1/1/2022', periods=2, freq='W-FRI'),
                 columns=['New York', 'Seattle', 'Seoul'])

df

Out[52]:

                 New York	  Seattle	   Seoul
2022-01-07	 0.204289	-0.735862	 0.574188
2022-01-14	-0.094468	-1.805211	-1.940763

 

이렇게 금요일에만 데이터값을 생성하였을 때, 일 빈도로 리샘플링하면 누락되는 값이 발생한다.

이제 금요일이 아닌 요일에 ffill 메서드를 사용하여 보간을 진행해보자.

 

In[53]:

df.resample('D').ffill()

Out[53]:

	         New York	  Seattle	  Seoul
2022-01-07	 0.204289	-0.735862	 0.574188
2022-01-08	 0.204289	-0.735862	 0.574188
2022-01-09	 0.204289	-0.735862	 0.574188
2022-01-10	 0.204289	-0.735862	 0.574188
2022-01-11	 0.204289	-0.735862	 0.574188
2022-01-12	 0.204289	-0.735862	 0.574188
2022-01-13	 0.204289	-0.735862	 0.574188
2022-01-14	-0.094468	-1.805211	-1.940763

 

ffill 메서드를 사용하면 NaN 전의 값을 복사하여 채워주지만, interpolate메서드를 사용하면 NaN 값을 보간하여 자동으로 값을 생산해준다.

 

In[54]:

df.resample('D').interpolate()

Out[54]:

            	 New York	 Seattle	   Seoul
2022-01-07	 0.204289	-0.735862	 0.574188
2022-01-08	 0.161610	-0.888626	 0.214909
2022-01-09	 0.118930	-1.041390	-0.144370
2022-01-10	 0.076251	-1.194154	-0.503648
2022-01-11	 0.033571	-1.346918	-0.862927
2022-01-12	-0.009108	-1.499683	-1.222206
2022-01-13	-0.051788	-1.652447	-1.581484
2022-01-14	-0.094468	-1.805211	-1.940763

 

 

 

 

 

글 | AI 그룹 김경준

댓글