포스트

Working_With_Time_Series

Working_With_Time_Series

시계열 다루기 Working_With_Time_Series

타임 스탬프 : 특정 시점을 말함
시간 간격 : 특정 시작점과 종료점 사이의 시간의 길이
기간 : 일반적으로 각 간격이 일정하고 서로 곂치지 않는 특별한 경우의 시간 간격
시간 델타, 지속 기간 : 정확한 시간 길이

Dates and Times in Python

Native Python dates and times: datetime and dateutil

1
2
3
4
from datetime import datetime

# 날짜를 직접 구성
datetime(year=2015, month=7, day=4)
1
datetime.datetime(2015, 7, 4, 0, 0)
1
2
3
4
5
from dateutil import parser

# 다양한 문자열 형태로부터 날짜를 해석
date = parser.parse("4th of July, 2015")
date
1
datetime.datetime(2015, 7, 4, 0, 0)
1
2
# 요일을 출력
date.strftime('%A')
1
'Saturday'

Typed arrays of times: NumPy’s datetime64

1
2
3
4
5
import numpy as np

# datetime64 dtype은 날짜를 64비트 정수로 인코딩
date = np.array('2015-07-04', dtype=np.datetime64)
date
1
array('2015-07-04', dtype='datetime64[D]')
1
2
# 벡터화 연산 가능
date + np.arange(12)
1
2
3
4
array(['2015-07-04', '2015-07-05', '2015-07-06', '2015-07-07',
       '2015-07-08', '2015-07-09', '2015-07-10', '2015-07-11',
       '2015-07-12', '2015-07-13', '2015-07-14', '2015-07-15'],
      dtype='datetime64[D]')
1
2
# 하루 단위
np.datetime64('2015-07-04')
1
numpy.datetime64('2015-07-04')
1
2
# 분 단위
np.datetime64('2015-07-04 12:00')
1
numpy.datetime64('2015-07-04T12:00')
1
2
# 시간 단위 --> 나노초
np.datetime64('2015-07-04 12:59:59.50', 'ns')
1
numpy.datetime64('2015-07-04T12:59:59.500000000')

날짜와 시간 코드

CodeMeaningTime span (relative)Time span (absolute)
YYear± 9.2e18 years[9.2e18 BC, 9.2e18 AD]
MMonth± 7.6e17 years[7.6e17 BC, 7.6e17 AD]
WWeek± 1.7e17 years[1.7e17 BC, 1.7e17 AD]
DDay± 2.5e16 years[2.5e16 BC, 2.5e16 AD]
hHour± 1.0e15 years[1.0e15 BC, 1.0e15 AD]
mMinute± 1.7e13 years[1.7e13 BC, 1.7e13 AD]
sSecond± 2.9e12 years[ 2.9e9 BC, 2.9e9 AD]
msMillisecond± 2.9e9 years[ 2.9e6 BC, 2.9e6 AD]
usMicrosecond± 2.9e6 years[290301 BC, 294241 AD]
nsNanosecond± 292 years[ 1678 AD, 2262 AD]
psPicosecond± 106 days[ 1969 AD, 1970 AD]
fsFemtosecond± 2.6 hours[ 1969 AD, 1970 AD]
asAttosecond± 9.2 seconds[ 1969 AD, 1970 AD]

Dates and times in pandas: best of both worlds

1
2
3
4
import pandas as pd

date = pd.to_datetime("4th of July, 2015")
date
1
Timestamp('2015-07-04 00:00:00')
1
date.strftime('%A')
1
'Saturday'
1
2
# NumPy 스타일 벡터화 연산
date + pd.to_timedelta(np.arange(12), 'D')
1
2
3
4
DatetimeIndex(['2015-07-04', '2015-07-05', '2015-07-06', '2015-07-07',
               '2015-07-08', '2015-07-09', '2015-07-10', '2015-07-11',
               '2015-07-12', '2015-07-13', '2015-07-14', '2015-07-15'],
              dtype='datetime64[ns]', freq=None)

Pandas Time Series: Indexing by Time

1
2
3
4
5
# 시간 인덱스를 가진 데이터의 Series 객체 구성
index = pd.DatetimeIndex(['2014-07-04', '2014-08-04',
                          '2015-07-04', '2015-08-04'])
data = pd.Series([0, 1, 2, 3], index=index)
data
1
2
3
4
5
2014-07-04    0
2014-08-04    1
2015-07-04    2
2015-08-04    3
dtype: int64
1
2
# Series 인덱싱 패턴
data['2014-07-04':'2015-07-04']
1
2
3
4
2014-07-04    0
2014-08-04    1
2015-07-04    2
dtype: int64
1
2
# 인덱싱 연산
data['2015']
1
2
3
2015-07-04    2
2015-08-04    3
dtype: int64

Pandas Time Series Data Structures

타임스탬프 : numpy.datetime64 데이터 타입 기반 –> 관련 인덱스 구조 : DatetimeIndex
기간(time period) : numpy.datetime64 기반, 고정 주파수 간격을 인코딩 –> 관련 인덱스 구조 : PeriodIndex
시간 델타, 지속 기간 : datetime.timedelta 타입의 효율적 대체 타입, numpy.timedelta64 기반 –> 관련 인덱스 구조 : TimedeltaIndex

1
2
3
4
# 단일 날짜를 pd.to_datetime()에 전달하면 TimeStamp 생성하고, 일련의 날짜를 전달하면 DatetimeIndex를 생성
dates = pd.to_datetime([datetime(2015, 7, 3), '4th of July, 2015',
                        '2015-Jul-6', '07-07-2015', '20150708'])
dates
1
2
3
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
               '2015-07-08'],
              dtype='datetime64[ns]', freq=None)
1
2
# 주기(frequency) 코드를 추가해 PeriodIndex로 전환
dates.to_period('D')
1
2
3
PeriodIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
             '2015-07-08'],
            dtype='period[D]')
1
2
# TimedeltaIndex 생성
dates - dates[0]
1
TimedeltaIndex(['0 days', '1 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq=None)

Regular sequences: pd.date_range()

1
2
# 시작일, 종료일, 선택적 주기 코드를 받아 정규 날짜 시퀀스 생성
pd.date_range('2015-07-03', '2015-07-10')
1
2
3
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
               '2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
              dtype='datetime64[ns]', freq='D')
1
2
# 시작점과 기간의 수로 지정
pd.date_range('2015-07-03', periods=8)
1
2
3
DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
               '2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
              dtype='datetime64[ns]', freq='D')
1
2
# freq 인수를 바꿔 간격을 조정
pd.date_range('2015-07-03', periods=8, freq='h')
1
2
3
4
5
DatetimeIndex(['2015-07-03 00:00:00', '2015-07-03 01:00:00',
               '2015-07-03 02:00:00', '2015-07-03 03:00:00',
               '2015-07-03 04:00:00', '2015-07-03 05:00:00',
               '2015-07-03 06:00:00', '2015-07-03 07:00:00'],
              dtype='datetime64[ns]', freq='h')
1
2
# 기간이나 타임 델타값의 정규 시퀀스 생성
pd.period_range('2015-07', periods=8, freq='M')
1
2
3
PeriodIndex(['2015-07', '2015-08', '2015-09', '2015-10', '2015-11', '2015-12',
             '2016-01', '2016-02'],
            dtype='period[M]')
1
2
# 시간단위로 증가하는 기간의 시퀀스 생성
pd.timedelta_range(0, periods=10, freq='h')
1
2
3
4
5
TimedeltaIndex(['0 days 00:00:00', '0 days 01:00:00', '0 days 02:00:00',
                '0 days 03:00:00', '0 days 04:00:00', '0 days 05:00:00',
                '0 days 06:00:00', '0 days 07:00:00', '0 days 08:00:00',
                '0 days 09:00:00'],
               dtype='timedelta64[ns]', freq='h')

Frequencies and Offsets

Pandas 주기 코드

CodeDescriptionCodeDescription
DCalendar dayBBusiness day
WWeekly  
MMonth endBMBusiness month end
QQuarter endBQBusiness quarter end
AYear endBABusiness year end
hHoursBHBusiness hours
minMinutes  
sSeconds  
msMilliseonds  
usMicroseconds  
nsnanoseconds  

주기 코드의 시작 시점을 표시하는 목록

CodeDescription CodeDescription
MSMonth start BMSBusiness month start
QSQuarter start BQSBusiness quarter start
ASYear start BASBusiness year start
1
2
# 코드를 숫자와 결합해 주기 지정
pd.timedelta_range(0, periods=9, freq="2h30min")
1
2
3
4
TimedeltaIndex(['0 days 00:00:00', '0 days 02:30:00', '0 days 05:00:00',
                '0 days 07:30:00', '0 days 10:00:00', '0 days 12:30:00',
                '0 days 15:00:00', '0 days 17:30:00', '0 days 20:00:00'],
               dtype='timedelta64[ns]', freq='150min')
1
2
3
4
from pandas.tseries.offsets import BDay

# 영업일 오프셋 만들기
pd.date_range('2015-07-01', periods=5, freq=BDay())
1
2
3
DatetimeIndex(['2015-07-01', '2015-07-02', '2015-07-03', '2015-07-06',
               '2015-07-07'],
              dtype='datetime64[ns]', freq='B')

Resampling, Shifting, and Windowing

1
2
3
4
5
6
7
from pandas_datareader import data
import yfinance as yf

yf.pdr_override()
sp500 = data.DataReader('^GSPC', start='2018-01-01', end='2021-12-31')

sp500.head()
1
[*********************100%%**********************]  1 of 1 completed
OpenHighLowCloseAdj CloseVolume
Date
2018-01-022683.7299802695.8898932682.3601072695.8100592695.8100593397430000
2018-01-032697.8500982714.3701172697.7700202713.0600592713.0600593544030000
2018-01-042719.3100592729.2900392719.0700682723.9899902723.9899903697340000
2018-01-052731.3300782743.4499512727.9199222743.1499022743.1499023239280000
2018-01-082742.6699222748.5100102737.6000982747.7099612747.7099613246160000
1
sp500 = sp500['Close']
1
2
3
4
5
6
7
%matplotlib
inline
import matplotlib.pyplot as plt
import seaborn;

seaborn.set()
sp500.plot();

png

1
2
3
4
5
6
7
8
# resample() --> 데이터를 집계 --> 전년도 평균 보여줌
# asfreq() --> 데이터를 선택 --> 연말 주가 보여줌
sp500.plot(alpha=0.5, style='-')
# resampling
sp500.resample('BYE').mean().plot(style=':')
sp500.asfreq('BYE').plot(style='--');
plt.legend(['input', 'resample', 'asfreq'],
           loc='upper left');

png

1
2
3
4
5
6
7
8
fig, ax = plt.subplots(2, sharex=True)
data = sp500.iloc[:10]

data.asfreq('D').plot(ax=ax[0], marker='o')

data.asfreq('D', method='bfill').plot(ax=ax[1], style='-o')
data.asfreq('D', method='ffill').plot(ax=ax[1], style='--o')
ax[1].legend(["back-fill", "forward-fill"]);

png

Time-shifts

1
2
3
4
5
sp500 = sp500.asfreq('D', method='pad')
# 주어진 항목 수만큼 데이터를 이동하는데 shift 메서드 사용
ROI = 100 * (sp500.shift(-365) - sp500) / sp500
ROI.plot()
plt.ylabel('% Return on Investment after 1 year');

png

Rolling windows

1
2
3
4
5
6
7
8
9
# 롤링 통계
# 인덱스의 1년 중심 롤링 평균과 표준 편차
rolling = sp500.rolling(365, center=True)

data = pd.DataFrame({'input': sp500,
                     'one-year rolling_mean': rolling.mean(),
                     'one-year rolling_std': rolling.std()})
ax = data.plot(style=['-', '--', ':'])
ax.lines[0].set_alpha(0.3)

png

Example: Visualizing Seattle Bicycle Counts

1
2
3
data = pd.read_csv('FremontBridge.csv', index_col='Date', parse_dates=True, date_format='%m/%d/%Y %I:%M:%S %p')
data.columns = ['Total', 'West', 'East']
data.head()
TotalWestEast
Date
2012-10-02 13:00:0055.07.048.0
2012-10-02 14:00:00130.055.075.0
2012-10-02 15:00:00152.081.071.0
2012-10-02 16:00:00278.0167.0111.0
2012-10-02 17:00:00563.0393.0170.0
1
data.dropna().describe()
TotalWestEast
count100735.000000100735.000000100735.000000
mean105.35282745.55337359.799454
std131.84074860.09029181.591238
min0.0000000.0000000.000000
25%13.0000005.0000007.000000
50%59.00000025.00000032.000000
75%143.00000062.00000078.000000
max1097.000000667.000000850.000000

Visualizing the data

1
2
data.plot()
plt.ylabel('Hourly Bicycle Count');

png

1
2
3
4
# 밀집도를 낮추기 위해 주 단위로 리샘플링
weekly = data.resample('W').sum()
weekly.plot(style=[':', '--', '-'])
plt.ylabel('Weekly bicycle count');

png

1
2
3
4
# 30일 롤링 평균을 구해 데이터가 중심에 오게 하기
daily = data.resample('D').sum()
daily.rolling(30, center=True).sum().plot(style=[':', '--', '-'])
plt.ylabel('mean hourly count');

png

1
2
3
# 가우스 윈도우 함수 사용
daily.rolling(50, center=True,
              win_type='gaussian').sum(std=10).plot(style=[':', '--', '-']);

png

Digging into the data

1
2
3
4
# 하루 시간대별 통행량
by_time = data.groupby(data.index.time).mean()
hourly_ticks = 4 * 60 * 60 * np.arange(6)
by_time.plot(xticks=hourly_ticks, style=[':', '--', '-']);

png

1
2
3
4
# 요일에 따른 통행량
by_weekday = data.groupby(data.index.dayofweek).mean()
by_weekday.index = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun']
by_weekday.plot(style=[':', '--', '-']);

png

1
2
3
# 주말 표시 플래그와 시간대별 분류
weekend = np.where(data.index.weekday < 5, 'Weekday', 'Weekend')
by_time = data.groupby([weekend, data.index.time]).mean()
1
2
3
4
5
6
7
8
# 주중과 주말의 시간대별 추이
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 2, figsize=(14, 5))
by_time.loc['Weekday'].plot(ax=ax[0], title='Weekdays',
                            xticks=hourly_ticks, style=[':', '--', '-'])
by_time.loc['Weekend'].plot(ax=ax[1], title='Weekends',
                            xticks=hourly_ticks, style=[':', '--', '-']);

png

이 기사는 저작권자의 CC BY-NC 4.0 라이센스를 따릅니다.