본문 바로가기

😀 기초/판다스(Pandas)

4.8 시계열 자료 다루기

4.8 시계열 자료 다루기

 

1. #### DatetimeIndex 인덱스
- 시계열 자료는 인덱스가 날짜 혹은 시간인 데이터를 말한다.
- 판다스에서 시계열 자료를 생성하려면 인덱스를 DatetimeIndex 자료형으로 만들어야 한다.
- DatetimeIndex는 특정한 순간에 기록된 타임스탬프 형식의 시계열 자료를 다루기 위한 인덱스이다.
- 타임스탬프 인덱스의 라벨값이 반드시 일정한 간격일 필요는 없다

 

- DatetimeIndex 인덱스는 다음과 같이 보조 함수를 사용하여 생성
- pd.to_datetime 함수
- pd.date_range 함수
- pd.to_datetime 함수를 쓰면 날짜/시간을 나타내는 문자열을 자동으로 datetime 자료형으로 바꾼 후   
DatetimeIndex자료형 인덱스를 생성.

date_str = ["2018, 1, 1", "2018, 1, 4", "2018, 1, 5", "2018, 1, 6"]
idx = pd.to_datetime(date_str)
idx

#결과
DatetimeIndex(['2018-01-01', '2018-01-04', '2018-01-05', '2018-01-06'], dtype='datetime64[ns]', freq=None)
# 이렇게 만들어진 인덱스를 사용하여 시리즈나 데이터프레임을 생성하면 된다.

np.random.seed(0)
s = pd.Series(np.random.randn(4), index=idx)
s

2018-01-01    1.764052
2018-01-04    0.400157
2018-01-05    0.978738
2018-01-06    2.240893
dtype: float64

# pd.date_range 함수를 쓰면 모든 날짜/시간을 일일히 입력할 필요없이
# 시작일과 종료일 또는 시작일과 기간을 입력하면 범위 내의 인덱스를 생성해 준다.

pd.date_range('2018-4-1', "2018-4-30")

DatetimeIndex(['2018-04-01', '2018-04-02', '2018-04-03', '2018-04-04',
               '2018-04-05', '2018-04-06', '2018-04-07', '2018-04-08',
               '2018-04-09', '2018-04-10', '2018-04-11', '2018-04-12',
               '2018-04-13', '2018-04-14', '2018-04-15', '2018-04-16',
               '2018-04-17', '2018-04-18', '2018-04-19', '2018-04-20',
               '2018-04-21', '2018-04-22', '2018-04-23', '2018-04-24',
               '2018-04-25', '2018-04-26', '2018-04-27', '2018-04-28',
               '2018-04-29', '2018-04-30'],
              dtype='datetime64[ns]', freq='D')
              
pd.date_range(start="2018-4-1", periods=30)

DatetimeIndex(['2018-04-01', '2018-04-02', '2018-04-03', '2018-04-04',
               '2018-04-05', '2018-04-06', '2018-04-07', '2018-04-08',
               '2018-04-09', '2018-04-10', '2018-04-11', '2018-04-12',
               '2018-04-13', '2018-04-14', '2018-04-15', '2018-04-16',
               '2018-04-17', '2018-04-18', '2018-04-19', '2018-04-20',
               '2018-04-21', '2018-04-22', '2018-04-23', '2018-04-24',
               '2018-04-25', '2018-04-26', '2018-04-27', '2018-04-28',
               '2018-04-29', '2018-04-30'],
              dtype='datetime64[ns]', freq='D')

- freq 인수로 특정한 날짜만 생성되도록 할 수 있음
- 자주 사용하는 freq 인수값은 다음과 같다.
s: 초

T: 분

H: 시간

D: 일(day)

B: 주말이 아닌 평일

W: 주(일요일)

W-MON: 주(월요일)

M: 각 달(month)의 마지막 날

MS: 각 달의 첫날

BM: 주말이 아닌 평일 중에서 각 달의 마지막 날

BMS: 주말이 아닌 평일 중에서 각 달의 첫날

WOM-2THU: 각 달의 두번째 목요일

Q-JAN: 각 분기의 첫달의 마지막 날

Q-DEC: 각 분기의 마지막 달의 마지막 날

pd.date_range("2018.4.1", "2018-4-30", freq="B")

DatetimeIndex(['2018-04-02', '2018-04-03', '2018-04-04', '2018-04-05',
               '2018-04-06', '2018-04-09', '2018-04-10', '2018-04-11',
               '2018-04-12', '2018-04-13', '2018-04-16', '2018-04-17',
               '2018-04-18', '2018-04-19', '2018-04-20', '2018-04-23',
               '2018-04-24', '2018-04-25', '2018-04-26', '2018-04-27',
               '2018-04-30'],
              dtype='datetime64[ns]', freq='B')
              
pd.date_range("2018-1-1", "2018-12-31", freq="W")

DatetimeIndex(['2018-01-07', '2018-01-14', '2018-01-21', '2018-01-28',
               '2018-02-04', '2018-02-11', '2018-02-18', '2018-02-25',
               '2018-03-04', '2018-03-11', '2018-03-18', '2018-03-25',
               '2018-04-01', '2018-04-08', '2018-04-15', '2018-04-22',
               '2018-04-29', '2018-05-06', '2018-05-13', '2018-05-20',
               '2018-05-27', '2018-06-03', '2018-06-10', '2018-06-17',
               '2018-06-24', '2018-07-01', '2018-07-08', '2018-07-15',
               '2018-07-22', '2018-07-29', '2018-08-05', '2018-08-12',
               '2018-08-19', '2018-08-26', '2018-09-02', '2018-09-09',
               '2018-09-16', '2018-09-23', '2018-09-30', '2018-10-07',
               '2018-10-14', '2018-10-21', '2018-10-28', '2018-11-04',
               '2018-11-11', '2018-11-18', '2018-11-25', '2018-12-02',
               '2018-12-09', '2018-12-16', '2018-12-23', '2018-12-30'],
              dtype='datetime64[ns]', freq='W-SUN')

2. #### shift 연산
---
- 시계열 데이터의 인덱스는 시간이나 날짜를 나타내기 떄문에 날짜 이동 등의 다양한 연산이 가능
- 예를 들어 shift 연산을 사용하면 인덱스는 그대로 두고 데이터만 이동할 수도 있다.

 

np.random.seed(0)
ts = pd.Series(np.random.randn(4), index=pd.date_range(
    "2018-1-1", periods=4, freq="M"))
ts

2018-01-31    1.764052
2018-02-28    0.400157
2018-03-31    0.978738
2018-04-30    2.240893
Freq: M, dtype: float64

ts.shift(1)

2018-01-31         NaN
2018-02-28    1.764052
2018-03-31    0.400157
2018-04-30    0.978738
Freq: M, dtype: float64

ts.shift(-1)

2018-01-31    0.400157
2018-02-28    0.978738
2018-03-31    2.240893
2018-04-30         NaN
Freq: M, dtype: float64

ts.shift(1, freq="M")

2018-02-28    1.764052
2018-03-31    0.400157
2018-04-30    0.978738
2018-05-31    2.240893
Freq: M, dtype: float64

ts.shift(1, freq="W")

2018-02-04    1.764052
2018-03-04    0.400157
2018-04-01    0.978738
2018-05-06    2.240893
dtype: float64

3. #### resample 연산
---
- resample 연산을 쓰면 시간 간격을 재종하는 리샘플링이 가능
- 이 때 시간 구간이 작아지면 데이터 양이 증가한다고해서 업-샘플링(up-sampling)이라고 하고   
시간 구간이 커지면서 데이터 양이 감소한다고 해서 다운-샘플링(down-sampling)이라 부른다.

ts = pd.Series(np.random.randn(100), index=pd.date_range(
    "2018-1-1", periods=100, freq="D"))
ts.tail(20)

2018-03-22    1.488252
2018-03-23    1.895889
2018-03-24    1.178780
2018-03-25   -0.179925
2018-03-26   -1.070753
2018-03-27    1.054452
2018-03-28   -0.403177
2018-03-29    1.222445
2018-03-30    0.208275
2018-03-31    0.976639
2018-04-01    0.356366
2018-04-02    0.706573
2018-04-03    0.010500
2018-04-04    1.785870
2018-04-05    0.126912
2018-04-06    0.401989
2018-04-07    1.883151
2018-04-08   -1.347759
2018-04-09   -1.270485
2018-04-10    0.969397
Freq: D, dtype: float64

# 다운-샘플링의 경우에는 원래의 데이터가 그룹으로 묶이기 때문에
# 그룹바이(groupby)때와 같이 그룹 연산을 해서 대표값을 구해야 함

ts.resample("W").mean()

2018-01-07    0.305776
2018-01-14    0.629064
2018-01-21   -0.006910
2018-01-28    0.277065
2018-02-04   -0.144972
2018-02-11   -0.496299
2018-02-18   -0.474473
2018-02-25   -0.201222
2018-03-04   -0.775142
2018-03-11    0.052868
2018-03-18   -0.450379
2018-03-25    0.601892
2018-04-01    0.334893
2018-04-08    0.509605
2018-04-15   -0.150544
Freq: W-SUN, dtype: float64

ts.resample("M").first()

2018-01-31    1.867558
2018-02-28    0.156349
2018-03-31   -1.726283
2018-04-30    0.356366
Freq: M, dtype: float64

날짜가 아닌 시/분 단위에서는 구간위 왼쪽 한계값(가장 빠른 값)은 포함하고
오른쪽 한계값(가장 늦은 값)은 포함하지 않는다.
즉, 가장 늦은 값은 다음 구간에 포함된다.
예를 들어 10분 간격으로 구간을 만들면 10의 배수가 되는 시각은 구간의 시작점이 된다.

ts = pd.Series(np.random.randn(60), index=pd.date_range(
    "2018-1-1", periods=60, freq="T"))
ts.head(20)

2018-01-01 00:00:00   -1.540797
2018-01-01 00:01:00    0.063262
2018-01-01 00:02:00    0.156507
2018-01-01 00:03:00    0.232181
2018-01-01 00:04:00   -0.597316
2018-01-01 00:05:00   -0.237922
2018-01-01 00:06:00   -1.424061
2018-01-01 00:07:00   -0.493320
2018-01-01 00:08:00   -0.542861
2018-01-01 00:09:00    0.416050
2018-01-01 00:10:00   -1.156182
2018-01-01 00:11:00    0.781198
2018-01-01 00:12:00    1.494485
2018-01-01 00:13:00   -2.069985
2018-01-01 00:14:00    0.426259
2018-01-01 00:15:00    0.676908
2018-01-01 00:16:00   -0.637437
2018-01-01 00:17:00   -0.397272
2018-01-01 00:18:00   -0.132881
2018-01-01 00:19:00   -0.297791
Freq: T, dtype: float64

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

2018-01-01 00:00:00   -3.968277
2018-01-01 00:10:00   -1.312698
2018-01-01 00:20:00   -2.264954
2018-01-01 00:30:00   -4.039789
2018-01-01 00:40:00   -1.376215
2018-01-01 00:50:00    1.122044
Freq: 10T, dtype: float64

4. #### dt접근자
---
- datetime 자료형 시리즈에는 dt 접근자가 있어   
datetime 자료형이 가진 몇가지 유용한 속성과 메서드를 사용할 수 있다.

s = pd.Series(pd.date_range("2020-12-25", periods=100, freq="D"))
s

0    2020-12-25
1    2020-12-26
2    2020-12-27
3    2020-12-28
4    2020-12-29
        ...    
95   2021-03-30
96   2021-03-31
97   2021-04-01
98   2021-04-02
99   2021-04-03
Length: 100, dtype: datetime64[ns]

# year, month, day, weekday 등의 소성을 이용하면 년, 월, 일, 요일 정보를 빼낼 수 있다.

s.dt.year

0     2020
1     2020
2     2020
3     2020
4     2020
      ... 
95    2021
96    2021
97    2021
98    2021
99    2021
Length: 100, dtype: int64

s.dt.weekday

0     4
1     5
2     6
3     0
4     1
     ..
95    1
96    2
97    3
98    4
99    5
Length: 100, dtype: int64

s.dt.strftime("%Y년 %m월 %d일")

0     2020년 12월 25일
1     2020년 12월 26일
2     2020년 12월 27일
3     2020년 12월 28일
4     2020년 12월 29일
          ...      
95    2021년 03월 30일
96    2021년 03월 31일
97    2021년 04월 01일
98    2021년 04월 02일
99    2021년 04월 03일
Length: 100, dtype: object