시계열 데이터 분석(2) : 시계열 데이터 핸들링 기초

전창우·2023년 9월 26일
0

python for data analysis

목록 보기
14/23

날짜 범위, 오프셋, 시프트

pandas에서 일반적인 시계열은 불규칙적인 것으로 간주된다. 즉, 고정된 빈도를 갖지 않는다.
하지만, 시계열 안에 누락된 값이 발생할지라도 고정 빈도에서 작업이 요구되는 경우도 있다. pandas는 이런 곤란한 상황에 사용할 수 있는 리샘플링, 표준 시계열 빈도 모음, 빈도 추론, 빈도의 날짜 범위를 위한 도구가 있다.

날짜 범위 생성하기

pandas.date_range 함수를 사용하면 특정 빈도에 따라 지정한 길이만큼의 DatetimeIndex를 생성한다.

index=pd.date_range('2012-04-01','2012-06-01',periods=10)
index
"""
DatetimeIndex(['2012-04-01 00:00:00', '2012-04-07 18:40:00',
               '2012-04-14 13:20:00', '2012-04-21 08:00:00',
               '2012-04-28 02:40:00', '2012-05-04 21:20:00',
               '2012-05-11 16:00:00', '2012-05-18 10:40:00',
               '2012-05-25 05:20:00', '2012-06-01 00:00:00'],
              dtype='datetime64[ns]', freq=None)
"""

만약 시작 날짜나 종료 날짜만 넘긴다면 period 옵션을 반드시 넘겨줘야 하고, 시작 날짜와 종료 날짜를 넘겼어도 period 옵션을 사용하면 그 기간의 나눗셈을 계산해준다.

date_range는 기본적으로 시작 시간이나 종료 시간의 타임스탬프를 존재한다면 보존한다.

pd.date_range('2012-05-02 12:56:31',periods=5)
"""
DatetimeIndex(['2012-05-02 12:56:31', '2012-05-03 12:56:31',
               '2012-05-04 12:56:31', '2012-05-05 12:56:31',
               '2012-05-06 12:56:31'],
              dtype='datetime64[ns]', freq='D')
"""

만약 시간 정보를 포함하여 시작, 종료 날짜를 갖고 있으나 시간을 자정에 맞추어 타임스탬프를 정규화하고 싶다면 normalize 옵션을 사용해주면 된다.

pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True)
"""
DatetimeIndex(['2012-05-02', '2012-05-03',
'2012-05-04', '2012-05-05','2012-05-06'],
              dtype='datetime64[ns]', freq='D')
"""

빈도와 날짜 Offset

pandas에서 빈도는 기본 빈도와 배수의 조합으로 이루어진다. 기본 빈도는 보통 'M', 'H' 처럼 짧은 문자열로 참조된다. 각 기본 빈도에는 일반적으로 날짜 오프셋 객체를 사용할 수 있다. 예를 들어 시간별 빈도는 Hour 클래스를 사용해서 표현할 수 있다. 이 오프셋의 곱은 정수를 넘겨서 구할 수 있다.

from pandas.tseries.offsets import Hour, Minute
hour=Hour()
hour
#Out:<Hour>
four_hours=Hour(4)
#<4 * Hours>

종류가 다른 여러 오프셋을 덧셈으로 합칠 수도 있다.

Hour(2)+Minute(30)
#Out:<150 * Minutes>

월별 주차

한 가지 유용한 빈도 클래스는 WOM 으로 시작하는 '월별 주차'다. 월별 주차를 사용하면 매월 3째주 금요일 같은 날짜를 얻을 수 있다.

rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI')
list(rng)
"""
[Timestamp('2012-01-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-02-17 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-03-16 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-04-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-05-18 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-06-15 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-07-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-08-17 00:00:00', freq='WOM-3FRI')]
"""

data-shift

shift는 데이터를 시간 축에서 앞이나 뒤로 이동하는 것을 의미한다. pandas의 자료구조는 색인은 변경하지 않고 데이터를 앞뒤로 느슨한 시프트를 수행하는 shift 메서드를 가지고 있다.

ts=pd.Series(np.random.randn(4),
            index=pd.date_range('1/1/2000',periods=4,freq='M'))
ts
"""
2000-01-31   -2.013380
2000-02-29    0.618480
2000-03-31    0.421269
2000-04-30    0.126010
Freq: M, dtype: float64
"""
ts.shift(2)
"""
2000-01-31        NaN
2000-02-29        NaN
2000-03-31   -2.01338
2000-04-30    0.61848
Freq: M, dtype: float64
"""
ts.shift(-2)
"""
2000-01-31    0.421269
2000-02-29    0.126010
2000-03-31         NaN
2000-04-30         NaN
Freq: M, dtype: float64
"""

이렇게 shift를 하게 되면 시계열의 시작이나 끝에 결측치가 발생하게 된다.
shift는 일반적으로 한 시계열 내에서, 혹은 DataFrame의 컬럼으로 표현할 수 있는 여러 시계열에서의 퍼센트 변화를 계산할 때 흔히 사용하며, 코드로는 다음과 같이 표현한다.

ts/ts.shift(1) - 1

느슨한 shift는 색인을 바꾸지 않기 때문에 어떤 데이터는 버려지기도 한다.그래서 빈도를 알고 있다면 shift에 빈도를 넘겨서 타임스탬프가 확장되도록 할 수 있다.

ts.shift(2,freq='M')
"""
2000-03-31   -2.013380
2000-04-30    0.618480
2000-05-31    0.421269
2000-06-30    0.126010
Freq: M, dtype: float64
"""
ts.shift(3,freq='D')
"""
ts.shift(3,freq='D')
ts.shift(3,freq='D')
2000-02-03   -2.013380
2000-03-03    0.618480
2000-04-03    0.421269
2000-05-03    0.126010
dtype: float64
"""

이를 통해 아주 유연하게 데이터를 밀거나 당기는 작업을 할 수 있다.

오프셋만큼 날짜 시프트하기

pandas의 날짜 오프셋은 datetime이나 Timestamp객체에서도 사용할 수 있다.

from pandas.tseries.offsets import Day, MonthEnd
past=datetime(2011,11,17)
past+3*Day()

만일 MonthEnd 같은 앵커드 오프셋을 추가한다면 빈도 규칙의 다음 날짜로 roll forward 된다.

past+MonthEnd()
#Out:Timestamp('2011-11-30 00:00:00')

이런 앵커드 오프셋은 rollforward와 rollback 메서드를 사용해서 명시적으로 각각 날짜를 앞으로 밀거나 뒤로 당길 수 있다.

offset=MonthEnd()
offset.rollforward(past)
#Out:Timestamp('2011-11-30 00:00:00')
offset.rollback(past)
#Out:Timestamp('2011-10-31 00:00:00')

마치며

이번 로그에선 오프셋과 범위에 따른 날짜 생성, 데이터 시프트에 대해 다루어보았다. 다음 로그에서는 시간대에 대하여 다뤄보자.

0개의 댓글