본문 바로가기

Programming

[Python][Library] 2. Pandas - 5. 시계열

반응형
#!/usr/bin/env python
# coding: utf-8

# # Pandas V: 시계열 분석

# In[ ]:


import numpy as np
#np.set_printoptions(precision=4, suppress=True)

import pandas as pd
#pd.options.display.max_rows = 20

import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')

plt.rc('figure', figsize=(10, 6))

from matplotlib import rcParams
rcParams['font.family'] = 'NanumGothic'
rcParams['font.size'] = 10


# # 1. 날짜와 시간

# In[ ]:


from datetime import datetime


# In[ ]:


# 현재 시간: 마이크로초까지 지원
now = datetime.now()
now


# In[ ]:


now.year, now.month, now.day


# In[ ]:


# 시간적인 차이
delta = datetime(2011, 1, 7) - datetime(2008, 6, 24, 8, 15)
delta


# In[ ]:


# Day, Second
delta.days, delta.seconds


# In[ ]:


from datetime import timedelta


# In[ ]:


# 날짜 연산
start = datetime(2011, 1, 7)
start + timedelta(12)


# In[ ]:


start - (timedelta(12) * 2)


# #### datetime 모듈 자료형
#
# 자료형 | 설명
# :---|:---
# date | 그레고리언 달력을 사용해서 날짜(년, 월, 일)를 저장
# time | 하루 중 시간을 시간, 분, 초, 마이크로초 단위로 저장
# datetime | 날짜와 시간을 같이 저장
# timedelta | 두 datetime 값 간의 차이(일, 초, 마이크로초)를 표현
#

# ### 1.1 문자열과 datetime간 변환

# In[ ]:


stamp = datetime(2011, 1, 3)
stamp


# In[ ]:


# datetime --> 문자열
#str(stamp)
stamp.strftime('%Y-%m-%d')


# In[ ]:


# 문자열 --> datetime
value = '2011-01-03'
datetime.strptime(value, '%Y-%m-%d')


# In[ ]:


# 여러개의 문자열 처리(리스트 내포)
datestrs = ['7/6/2011', '8/6/2011']
[datetime.strptime(x, '%m/%d/%Y') for x in datestrs]


# ### 1.2 parser 유틸리티 이용

# In[ ]:


from dateutil.parser import parse
parse('2011-01-03')


# In[ ]:


parse('Jan 31, 1997 10:45 PM')


# In[ ]:


parse('6/12/2011', dayfirst=True)


# ### 1.3 pandas 이용: 색인객체로 생성

# In[ ]:


datestrs = ['2011-07-06 12:00:00', '2011-08-06 00:00:00']
pd.to_datetime(datestrs)


# In[ ]:


# 누락된 값 처리
idx = pd.to_datetime(datestrs + [None])
idx


# In[ ]:


idx[2]


# In[ ]:


pd.isnull(idx)


# ---

# # 2. 시계열 기초

# In[ ]:


from datetime import datetime


# In[ ]:


# 타임스탬프로 색인된 Series 객체 생성

dates = [datetime(2011, 1, 2), datetime(2011, 1, 5),
         datetime(2011, 1, 7), datetime(2011, 1, 8),
         datetime(2011, 1, 10), datetime(2011, 1, 12)]
ts = pd.Series(np.random.randn(6), index=dates)
ts


# In[ ]:


ts.index


# In[ ]:


# 색인된 시계열 객체 간의 산술연산
ts + ts[::2]


# In[ ]:


# 나노초까지 지원
ts.index.dtype


# In[ ]:


# DatetimeIndex의 스칼라 값은 pandas의 Timestamp 객체
stamp = ts.index[0]
stamp, type(stamp)


# ### 2.1 인덱싱

# In[ ]:


ts


# In[ ]:


ts[2]


# In[ ]:


stamp = ts.index[2]
ts[stamp]


# In[ ]:


# 인덱스에 문자열로 접근 가능
ts['1/10/2011']

ts['20110110']


# In[ ]:


longer_ts = pd.Series(np.random.randn(1000),
                      index=pd.date_range('1/1/2000', periods=1000))
longer_ts


# In[ ]:


# 문자열의 년으로 인덱싱
longer_ts['2001']


# In[ ]:


# 문자열의 년-월로 인덱싱
longer_ts['2001-05']


# In[ ]:


# datetime으로 인덱싱
ts[datetime(2011, 1, 7):]


# In[ ]:


# 문자열로 범위 지정
ts['1/6/2011':'1/11/2011']


# In[ ]:


# after 이후 날짜 잘라 버리기
ts.truncate(after='1/9/2011')


# In[ ]:


# before 이전 날짜 잘라 버리기
ts.truncate(before='1/9/2011')


# #### DataFrame

# In[ ]:


dates = pd.date_range('1/1/2000', periods=100, freq='W-WED')
long_df = pd.DataFrame(np.random.randn(100, 4),
                       index=dates,
                       columns=['Colorado', 'Texas',
                                'New York', 'Ohio'])
#long_df['5-2001']
long_df.loc['5-2001']


# ### 2.2 중복된 색인(시계열)

# In[ ]:


dates = pd.DatetimeIndex(['1/1/2000', '1/2/2000', '1/2/2000',
                          '1/2/2000', '1/3/2000'])
dup_ts = pd.Series(np.arange(5), index=dates)
dup_ts


# In[ ]:


dup_ts.index.is_unique


# In[ ]:


# 중복되지 않은 색인
dup_ts['1/3/2000']


# In[ ]:


# 중복된 색인
dup_ts['1/2/2000']


# In[ ]:


# 집계
grouped = dup_ts.groupby(level=0)


# In[ ]:


grouped.mean()


# In[ ]:


grouped.count()


# ---

# # 3. 날짜 다루기

# In[ ]:


ts


# In[ ]:


# 고정된 일 빈도로 변환
ts.resample('D').ffill()


# ### 3.1 날짜 범위 생성

# In[ ]:


# Default: 일별 타임스탬프 생성
index = pd.date_range('2012-04-01', '2012-06-01')
index


# In[ ]:


pd.date_range(start='2012-04-01', periods=20)


# In[ ]:


pd.date_range(end='2012-06-01', periods=20)


# In[ ]:


# BM: 월 영업 마감일
pd.date_range('2000-01-01', '2000-12-01', freq='BM')


# In[ ]:


# 시간 보존
pd.date_range('2012-05-02 12:56:31', periods=5)


# In[ ]:


# normalize: 자정에 맞추어 타임스탬프 정규화
pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True)


# ### 3.2 빈도와 날짜 오프셋

# In[ ]:


from pandas.tseries.offsets import Hour, Minute


# In[ ]:


# 시간 오프셋
hour = Hour()
hour


# In[ ]:


four_hours = Hour(4)
four_hours


# In[ ]:


# 오프셋 연산
Hour(2) + Minute(30)


# In[ ]:


# 4시간 주기로 생성
pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4h')


# In[ ]:


# 1시간 30분 간격으로 10개 생성
pd.date_range('2000-01-01', periods=10, freq='1h30min')


# In[ ]:


# 3번째 금요일(월별 옵션 만기일)
pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI')



# ### 3.3 데이터 쉬프트

# In[ ]:


ts = pd.Series(np.random.randn(4),
               index=pd.date_range('1/1/2000', periods=4, freq='M'))
ts


# In[ ]:


# data shift
ts.shift(2)


# In[ ]:


# data shift
ts.shift(-2)


# In[ ]:


# 각 인덱스를 빈도수(2개월) 만큼 이동
ts.shift(2, freq='M')


# In[ ]:


# 각 인덱스를 빈도수(3일) 만큼 이동
ts.shift(3, freq='D')


# In[ ]:


# 각 인덱스를 빈도수(90시간) 만큼 이동
ts.shift(1, freq='90T')


# In[ ]:


from pandas.tseries.offsets import Day, MonthEnd


# In[ ]:


# 오프셋만큼 날짜 쉬프트
now = datetime(2011, 11, 17)
now + 3 * Day()


# In[ ]:


# 빈도 규칙의 다음 날짜로 이동
now + MonthEnd()


# In[ ]:


now + MonthEnd(2)


# In[ ]:


# 앞으로 이동(월 말일)
offset = MonthEnd()
offset.rollforward(now)


# In[ ]:


# 뒤로 이동(월 말일)
offset.rollback(now)


# In[ ]:


ts = pd.Series(np.random.randn(20),
               index=pd.date_range('1/15/2000', periods=20, freq='4d'))
ts


# In[ ]:


# groupby 와 함께 사용
ts.groupby(offset.rollforward).mean()


# In[ ]:


# <참고>
ts.resample('M').mean()


# ---

# # 4. 시간대 다루기

# In[ ]:


import pytz


# In[ ]:


pytz.common_timezones[-5:]


# In[ ]:


pytz.timezone('America/New_York')


# In[ ]:


pytz.timezone('Asia/Seoul')


# In[ ]:


pytz.timezone('UTC')


# ### 4.1 지역 시간대 변환

# In[ ]:


rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts


# In[ ]:


# 지역 시간대 없음
print(ts.index.tz)


# In[ ]:


# 인덱스 생성시 지역 시간대 지정
pd.date_range('3/9/2012 9:30', periods=10, freq='D', tz='UTC')


# In[ ]:


# tz_localize(): 지역 시간대 설정
ts_utc = ts.tz_localize('UTC')
ts_utc


# In[ ]:


ts_utc.index


# In[ ]:


# tz_convert(): 지역 시간대 변경
ts_utc.tz_convert('UTC')
ts_utc.tz_convert('America/New_York')
ts_utc.tz_convert('Europe/Berlin')
ts_utc.tz_convert('Europe/Berlin')
ts_utc.tz_convert('Asia/Seoul')


# In[ ]:


# DatetimeIndex의 메소드
ts.index.tz_localize('Asia/Seoul')


# ### 4.2 지역 시간대(Timestamp 객체)

# In[ ]:


stamp = pd.Timestamp('2011-03-12 04:00')

stamp_utc = stamp.tz_localize('utc')
stamp_utc.tz_convert('Asia/Seoul')


# In[ ]:


stamp_moscow = pd.Timestamp('2011-03-12 04:00', tz='Europe/Moscow')
stamp_moscow


# In[ ]:


# Total second(나노 초)는 일정(UTC 기준)
stamp_utc.value
stamp_utc.tz_convert('America/New_York').value


# In[ ]:


stamp_utc.tz_convert('America/New_York').value


# In[ ]:


from pandas.tseries.offsets import Hour

stamp = pd.Timestamp('2012-03-12 01:30', tz='US/Eastern')
stamp


# In[ ]:


stamp + Hour()


# In[ ]:


stamp = pd.Timestamp('2012-11-04 00:30', tz='US/Eastern')
stamp
stamp + 2 * Hour()


# ### 4.3 다른 시간대 연산

# In[ ]:


# freq='B': 매 영업일
rng = pd.date_range('3/7/2012 9:30', periods=10, freq='B')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts


# In[ ]:


# 서로 다른 시간대의 연산은 UTC로 변환되어 반환
ts1 = ts[:7].tz_localize('Europe/London')
ts2 = ts1[2:].tz_convert('Europe/Moscow')
result = ts1 + ts2
result.index


# ---

# # 5. 기간과 기간 연산

# In[ ]:


# 'A-DEC': 12월을 마지막 달로 하는 년 주기
p = pd.Period(2007, freq='A-DEC')
p


# In[ ]:


p + 5


# In[ ]:


p - 2


# In[ ]:


# 두 기간간의 간격
pd.Period('2014', freq='A-DEC') - p


# In[ ]:


# Period Index
# 월별 기간 범위
rng = pd.period_range('2000-01-01', '2000-06-30', freq='M')
rng


# In[ ]:


pd.Series(np.random.randn(6), index=rng)


# In[ ]:


# 'Q-DEC': 12월을 마지막 달로 하는 분기 주기

values = ['2001Q3', '2002Q2', '2003Q1']
index = pd.PeriodIndex(values, freq='Q-DEC')
index.to_timestamp()


# ### 5.1 Period의 빈도 변환

# In[ ]:


# 'A-DEC': 12월을 마지막 달로 하는 년 주기

# 2007-01 ~ 2007-12

p = pd.Period('2007', freq='A-DEC')
p


# In[ ]:


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


# In[ ]:


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


# In[ ]:


# 'A-JUN': 6월을 마지막 달로 하는 년 주기

# 2006-07 ~ 2007-06

p = pd.Period('2007', freq='A-JUN')
p


# In[ ]:


p.asfreq('M', 'start')


# In[ ]:


p.asfreq('M', 'end')


# In[ ]:


p = pd.Period('Aug-2007', 'M')
p


# In[ ]:


# 2007-07 ~ 2008-06
p.asfreq('A-JUN')


# In[ ]:


# Index 활용

rng = pd.period_range('2006', '2009', freq='A-DEC')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts


# In[ ]:


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


# In[ ]:


ts.asfreq('B', how='end')


# ### 5.2 분기 빈도

# In[ ]:


# 1월을 마지막으로 하는 분기 빈도
p = pd.Period('2012Q4', freq='Q-JAN')
p


# In[ ]:


p.asfreq('D', 'start')


# In[ ]:


p.asfreq('D', 'end')


# In[ ]:


# 'B': 매 영업일, 'T': 매 분

# 분기 영업 마감일의 오후 4시
p4pm = (p.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60
p4pm


# In[ ]:


p4pm.to_timestamp()


# In[ ]:


# 인덱스에 활용
rng = pd.period_range('2011Q3', '2012Q4', freq='Q-JAN')
ts = pd.Series(np.arange(len(rng)), index=rng)
ts


# In[ ]:


# 인덱스에 활용
new_rng = (rng.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60
ts.index = new_rng.to_timestamp()
ts


# ### 5.3 타임스탬프와 Period간 변환

# In[ ]:


rng = pd.date_range('2000-01-01', periods=3, freq='M')
ts = pd.Series(np.random.randn(3), index=rng)
ts


# In[ ]:


# timestamp --> period
pts = ts.to_period()
pts


# In[ ]:


rng = pd.date_range('1/29/2000', periods=6, freq='D')
ts2 = pd.Series(np.random.randn(6), index=rng)
ts2


# In[ ]:


ts2.to_period('M')


# In[ ]:


pts = ts2.to_period()
pts


# In[ ]:


# period --> timestamp
pts.to_timestamp(how='end')


# ### 5.4 배열로 PeriodIndex 생성

# In[ ]:


data = pd.read_csv('data/macrodata.csv')
data.head(5)


# In[ ]:


index = pd.PeriodIndex(year=data.year, quarter=data.quarter,
                       freq='Q-DEC')
index


# In[ ]:


data.index = index


# In[ ]:


data.head()


# ---

# # 6. 리샘플링과 빈도 변환

# - Up sampling: 기간이 늘어남(보간 필요)
#   - 예: 월 --> 일
# - Down sampling: 기간이 줄어듬(집계 필요)
#   - 예: 일 --> 월

# In[ ]:


rng = pd.date_range('2000-01-01', periods=100, freq='D')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts.head()


# In[ ]:


ts.resample('M').mean()


# In[ ]:


ts.resample('M', kind='period').mean()


# ### 6.1 Downsampling

# In[ ]:


rng = pd.date_range('2000-01-01', periods=12, freq='T')
ts = pd.Series(np.arange(12), index=rng)
ts


# In[ ]:


# 'left': 시작값이 그룹의 왼쪽에 위치하도록 그룹 생성
ts.resample('5min', closed='left').sum()


# In[ ]:


# 'right': 시작값이 그룹의 오른쪽에 위치하도록 그룹 생성
ts.resample('5min', closed='right').sum()


# In[ ]:


# 표시값을 그룹의 오른쪽 값으로 설정
ts.resample('5min', closed='right', label='right').sum()


# In[ ]:


# offset 이동: shift와 같은 효과
ts.resample('5min', closed='right',
            label='right', loffset='-1s').sum()


# #### Open-High-Low-Close (OHLC) resampling

# In[ ]:


ts.resample('5min').ohlc()


# ### 6.2 Upsampling

# In[ ]:


frame = pd.DataFrame(np.random.randn(2, 4),
                     index=pd.date_range('1/1/2000', periods=2,
                                         freq='W-WED'),
                     columns=['Colorado', 'Texas', 'New York', 'Ohio'])
frame


# In[ ]:


df_daily = frame.resample('D').asfreq()
df_daily


# In[ ]:


# 결측치 채우기
frame.resample('D').ffill()


# In[ ]:


frame.resample('D').ffill(limit=2)


# In[ ]:


frame.resample('W-THU').ffill()


# ### 6.3 기간 리샘플링

# In[ ]:


frame = pd.DataFrame(np.random.randn(24, 4),
                     index=pd.period_range('1-2000', '12-2001',
                                           freq='M'),
                     columns=['Colorado', 'Texas', 'New York', 'Ohio'])
frame.head()


# In[ ]:


annual_frame = frame.resample('A-DEC').mean()
annual_frame


# In[ ]:


# Q-DEC: Quarterly, year ending in December
annual_frame.resample('Q-DEC').ffill()


# In[ ]:


annual_frame.resample('Q-DEC', convention='end').ffill()


# In[ ]:


annual_frame.resample('Q-MAR').ffill()


# ---

# # 참고

# ## Moving Window Functions

# In[ ]:


import numpy as np
import pandas as pd

#pd.options.display.max_rows = 20
#np.set_printoptions(precision=4, suppress=True)

import matplotlib
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')

plt.rc('figure', figsize=(10, 6))

matplotlib.rcParams['font.family'] = 'NanumGothic'
#matplotlib.rcParams['font.family'] = '나눔고딕'
matplotlib.rcParams['font.size'] = 10


# In[ ]:


close_px_all = pd.read_csv('data/stock_px_2.csv',
                           parse_dates=True, index_col=0)
close_px = close_px_all[['AAPL', 'MSFT', 'XOM']]
close_px = close_px.resample('B').ffill()


# In[ ]:


close_px.AAPL.plot()
close_px.AAPL.rolling(250).mean().plot()


# In[ ]:





# In[ ]:


plt.figure()


# In[ ]:


appl_std250 = close_px.AAPL.rolling(250, min_periods=10).std()
appl_std250[5:12]
appl_std250.plot()


# In[ ]:


expanding_mean = appl_std250.expanding().mean()


# In[ ]:


plt.figure()


# In[ ]:


close_px.rolling(60).mean().plot(logy=True)


# In[ ]:


close_px.rolling('20D').mean()


# ### Exponentially Weighted Functions

# In[ ]:


plt.figure()


# In[ ]:


aapl_px = close_px.AAPL['2006':'2007']
ma60 = aapl_px.rolling(30, min_periods=20).mean()
ewma60 = aapl_px.ewm(span=30).mean()
ma60.plot(style='k--', label='Simple MA')
ewma60.plot(style='k-', label='EW MA')
plt.legend()


# ### Binary Moving Window Functions

# In[ ]:


plt.figure()


# In[ ]:


spx_px = close_px_all['SPX']
spx_rets = spx_px.pct_change()
returns = close_px.pct_change()


# In[ ]:


corr = returns.AAPL.rolling(125, min_periods=100).corr(spx_rets)
corr.plot()


# In[ ]:


plt.figure()


# In[ ]:


corr = returns.rolling(125, min_periods=100).corr(spx_rets)
corr.plot()


# ### User-Defined Moving Window Functions

# In[ ]:


plt.figure()


# In[ ]:


from scipy.stats import percentileofscore
score_at_2percent = lambda x: percentileofscore(x, 0.02)
result = returns.AAPL.rolling(250).apply(score_at_2percent)
result.plot()


# ---

# In[ ]:


# end of file


# In[ ]:





반응형
LIST