금융 데이터 전처리와 분석을 위한 판다스 사용법¶
데이터 가져오기¶
내가 제일 좋아하는 주식, 애플(AAPL) 주식을 이용해 판다스의 간단한 사용법에 대해 알아보자.
Yahoo Finance를 통해 애플 주가 CSV 파일 데이터를 다운로드한다.
import pandas as pd
df = pd.read_csv('AAPL.csv')
df.head()
Date | Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|---|
0 | 2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 |
1 | 2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 |
2 | 2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 |
3 | 2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 |
4 | 2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 |
aapl_df = pd.read_csv('AAPL.csv', index_col='Date', parse_dates=['Date'])
print(aapl_df.head())
print(type(aapl_df.index))
print(type(aapl_df.index[0]))
Open High Low Close Adj Close \ Date 2020-09-01 132.759995 134.800003 130.529999 134.179993 133.322495 2020-09-02 137.589996 137.979996 127.000000 131.399994 130.560257 2020-09-03 126.910004 128.839996 120.500000 120.879997 120.107483 2020-09-04 120.070000 123.699997 110.889999 120.959999 120.186981 2020-09-08 113.949997 118.989998 112.680000 112.820000 112.098999 Volume Date 2020-09-01 151948100 2020-09-02 200119000 2020-09-03 257599600 2020-09-04 332607200 2020-09-08 231366600 <class 'pandas.core.indexes.datetimes.DatetimeIndex'> <class 'pandas._libs.tslibs.timestamps.Timestamp'>
index_col 옵셥으로 인덱스로 설정할 칼럼명 설정하고 parse_dates 옵션을 활용해 str 타입의 'Date' 칼럼값을 timestamp 타입으로 변환한다.
결측치와 이상치 제거¶
Nan 데이터 확인 함수
- isna(): 해당 데이터 프레임에서 Nan 값 포함 여부를 True/False로 Boolean 값으로 반환한다.
- isin(): 해당 데이터 프레임에서 매개변수로 전달받은 인자 값 포함 여부를 True/False로 Boolean 값으로 반환한다.
- isnull(): isna()와 같은 역할을 수행한다.
import numpy as np
aapl_df[aapl_df.isin([np.nan, np.inf, -np.inf]).any(1)]
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
Date |
결측치 또는 이상치가 존재하지 않는다!
슬라이싱, 인덱싱, 서브셋 데이터 추출¶
슬라이싱 & 인덱싱¶
# 칼럼 단위 추출
aapl_df['Open'].head()
Date 2020-09-01 132.759995 2020-09-02 137.589996 2020-09-03 126.910004 2020-09-04 120.070000 2020-09-08 113.949997 Name: Open, dtype: float64
# 두 개 이상의 칼럼 추출
aapl_df[['Open', 'High', 'Low', 'Close']].head()
Open | High | Low | Close | |
---|---|---|---|---|
Date | ||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 |
# 행 단위로 추출
aapl_df[0:3]
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
Date | ||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 |
# 인덱스(Date) 값을 이용한 추출
aapl_df['2020-10-10':'2020-10-20']
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
Date | ||||||
2020-10-12 | 120.059998 | 125.180000 | 119.279999 | 124.400002 | 123.604996 | 240226800 |
2020-10-13 | 125.269997 | 125.389999 | 119.650002 | 121.099998 | 120.326073 | 262330500 |
2020-10-14 | 121.000000 | 123.029999 | 119.620003 | 121.190002 | 120.415512 | 150712000 |
2020-10-15 | 118.720001 | 121.199997 | 118.150002 | 120.709999 | 119.938568 | 112559200 |
2020-10-16 | 121.279999 | 121.550003 | 118.809998 | 119.019997 | 118.259369 | 115393800 |
2020-10-19 | 119.959999 | 120.419998 | 115.660004 | 115.980003 | 115.238800 | 120639300 |
2020-10-20 | 116.199997 | 118.980003 | 115.629997 | 117.510002 | 116.759033 | 124423700 |
loc & iloc¶
실무에서 판다스의 loc와 iloc 인덱서를 이용하여 데이터를 추출하는 작업을 많이 하게 된다.
- loc: 인덱스 라벨 값 기반으로 행 데이터 추출
- iloc: 정수형 값(순서에 기반한 숫자)으로 데이터 추출
# 데이터 프레임의 초기 데이터
aapl_df.loc['2020-09-01']
Open 1.327600e+02 High 1.348000e+02 Low 1.305300e+02 Close 1.341800e+02 Adj Close 1.333225e+02 Volume 1.519481e+08 Name: 2020-09-01 00:00:00, dtype: float64
aapl_df.iloc[0]
Open 1.327600e+02 High 1.348000e+02 Low 1.305300e+02 Close 1.341800e+02 Adj Close 1.333225e+02 Volume 1.519481e+08 Name: 2020-09-01 00:00:00, dtype: float64
0 번째 순서는 인덱스 기준으로 첫번째를 의미하므로 위의 결과와 같은 것을 알 수 있다.
aapl_df.loc['2020-09-01':'2020-10-01',['Open', 'High', 'Low', 'Close']].head()
Open | High | Low | Close | |
---|---|---|---|---|
Date | ||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 |
aapl_df.iloc[100:110,[0, 1, 2, 3]]
Open | High | Low | Close | |
---|---|---|---|---|
Date | ||||
2021-01-26 | 143.600006 | 144.300003 | 141.369995 | 143.160004 |
2021-01-27 | 143.429993 | 144.300003 | 140.410004 | 142.059998 |
2021-01-28 | 139.520004 | 141.990005 | 136.699997 | 137.089996 |
2021-01-29 | 135.830002 | 136.740005 | 130.210007 | 131.960007 |
2021-02-01 | 133.750000 | 135.380005 | 130.929993 | 134.139999 |
2021-02-02 | 135.729996 | 136.309998 | 134.610001 | 134.990005 |
2021-02-03 | 135.759995 | 135.770004 | 133.610001 | 133.940002 |
2021-02-04 | 136.300003 | 137.399994 | 134.589996 | 137.389999 |
2021-02-05 | 137.350006 | 137.419998 | 135.860001 | 136.759995 |
2021-02-08 | 136.029999 | 136.960007 | 134.919998 | 136.910004 |
행 인덱스 100번부터 110번까지의 행을 추출한다. iloc의 장점은 데이터를 정수형으로 전달할 수 있어 문자로 전달하는 것보다 간단하게 사용할 수 있다는 점이다. 반면에 데이터 수가 많을 때는 원하는 데이터를 쉽게 추출하기 어렵다는 단점이 있다. 따라서 Date를 인덱스로 사용하는 주가 데이터 분석에는 iloc보다는 loc을 사용해 명확한 기간을 추출할 것을 권장한다.
aapl_df.loc['2020-Sep-1':'2020-Oct-1'].head()
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
Date | ||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 |
aapl_df.loc['September 1, 2020':'October 1, 2020'].head()
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
Date | ||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 |
str 타입의 날짜 데이터를 넘겼음에도 불구하고 원하는 기간의 데이터를 슬라이싱해 출력한다. 위와 같이 ISO8601 규칙에만 어긋나지 않는다면 자동으로 날짜를 인식해 원하는 데이터를 추출할 수 있다.
금융 시계열 데이터 분석에 유용한 판다스 함수¶
shift() 함수¶
시계열 데이터를 분석하는 경우, 서로 다른 시간대의 데이터 간 변화율을 게산하거나 살펴볼 필요가 있다. 이 경우 흔히 데이터 시점을 지연시켜 그 효과를 확인하게 된다. 주가를 분석하는 경우 전일 주가 대비 당일 주가의 변화율을 계산해 모멘텀을 계산하거나 수익률을 계산하는 데 응용할 수 있다.
shift()함수는 인덱스에 연결된 데이터를 일정 간격으로 이동시키는 함수이다. 인덱스의 변화 없이 데이터만 전, 후로 이동한다. 시계열 데이터는 주로 날짜 시간으로 되어 있으므로 shift()함수를 사용하면 이전 일자 데이터 혹은 N일 전 데이터를 손쉽게 가져올 수 있다.
aapl_df['Close_lag1'] = aapl_df['Close'].shift()
aapl_df.head()
Open | High | Low | Close | Adj Close | Volume | Close_lag1 | |
---|---|---|---|---|---|---|---|
Date | |||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 | NaN |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 | 134.179993 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 | 131.399994 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 | 120.879997 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 | 120.959999 |
shift() 함수의 매개변수 'periods' 값의 default는 1이므로 'Close' 값이 한 칸씩 아래로 이동한것을 알 수 있다. 음의 정수 데이터가 전달되면 위 방향으로 이동하고 양의 정수 데이터가 전달되면 아래로 이동한다.
또한 axis='1'(column) 값을 전달하면 데이터가 오른쪽으로 이동하게 된다. (default axis='0')
pct_change() 함수¶
percentage_change의 약자로 현재 값과 이전 요소 값의 백분율 변화량을 연산하는 함수이다.
aapl_df['pct_change'] = aapl_df['Close'].pct_change()
aapl_df.head(10)
Open | High | Low | Close | Adj Close | Volume | Close_lag1 | pct_change | |
---|---|---|---|---|---|---|---|---|
Date | ||||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 | NaN | NaN |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 | 134.179993 | -0.020718 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 | 131.399994 | -0.080061 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 | 120.879997 | 0.000662 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 | 120.959999 | -0.067295 |
2020-09-09 | 117.260002 | 119.139999 | 115.260002 | 117.320000 | 116.570236 | 176940500 | 112.820000 | 0.039887 |
2020-09-10 | 120.360001 | 120.500000 | 112.500000 | 113.489998 | 112.764717 | 182274400 | 117.320000 | -0.032646 |
2020-09-11 | 114.570000 | 115.230003 | 110.000000 | 112.000000 | 111.284241 | 180860300 | 113.489998 | -0.013129 |
2020-09-14 | 114.720001 | 115.930000 | 112.800003 | 115.360001 | 114.622765 | 140150100 | 112.000000 | 0.030000 |
2020-09-15 | 118.330002 | 118.830002 | 113.610001 | 115.540001 | 114.801620 | 184642000 | 115.360001 | 0.001560 |
shift() 함수와 마찬가지로 periods 값은 default 1로 가지며 음의 정수 데이터가 전달되면 위 방향으로 숫자 간격만큼 변화율을 계산하고, 양의 정수 데이터가 전달되면 아래 방향으로 숫자 간격만큼 변화율을 계산한다.
diff() 함수¶
pct_change() 함수로 변화율을 구했다면, diff() 함수로 변화량을 쉽게 구할 수 있다.
aapl_df['Close_diff'] = aapl_df['Close'].diff()
aapl_df.head()
Open | High | Low | Close | Adj Close | Volume | Close_lag1 | pct_change | Close_diff | |
---|---|---|---|---|---|---|---|---|---|
Date | |||||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 | NaN | NaN | NaN |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 | 134.179993 | -0.020718 | -2.779999 |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 | 131.399994 | -0.080061 | -10.519997 |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 | 120.879997 | 0.000662 | 0.080002 |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 | 120.959999 | -0.067295 | -8.139999 |
shift(), pct_change() 함수와 마찬가지로 periods는 default값으로 1을 가지며 음의 정수 데이터가 전달되면 위 방향으로 숫자 간격만큼 변화량을 계산하고, 양의 정수 데이터가 전달되면 아래 방향으로 숫자 간격만큼 변화량을 계산한다.
rolling() 함수¶
여러 주식 차트를 제공하는 플랫폼을 살펴보면, 이동 평균선을 제공하는 곳이 많다. 실제로 이동 평균선을 이용한 투자 전략에서는 매수 시점과 매도 시점의 시그널이 이동 평균선 기준으로 발생한다. 이동 평균선은 판다스에서 제공하는 rolling() 함수를 이용해 구할 수 있다.
aapl_df['MA'] = aapl_df['Close'].rolling(window = 5).mean()
aapl_df.head(10)
Open | High | Low | Close | Adj Close | Volume | Close_lag1 | pct_change | Close_diff | MA | |
---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||
2020-09-01 | 132.759995 | 134.800003 | 130.529999 | 134.179993 | 133.322495 | 151948100 | NaN | NaN | NaN | NaN |
2020-09-02 | 137.589996 | 137.979996 | 127.000000 | 131.399994 | 130.560257 | 200119000 | 134.179993 | -0.020718 | -2.779999 | NaN |
2020-09-03 | 126.910004 | 128.839996 | 120.500000 | 120.879997 | 120.107483 | 257599600 | 131.399994 | -0.080061 | -10.519997 | NaN |
2020-09-04 | 120.070000 | 123.699997 | 110.889999 | 120.959999 | 120.186981 | 332607200 | 120.879997 | 0.000662 | 0.080002 | NaN |
2020-09-08 | 113.949997 | 118.989998 | 112.680000 | 112.820000 | 112.098999 | 231366600 | 120.959999 | -0.067295 | -8.139999 | 124.047997 |
2020-09-09 | 117.260002 | 119.139999 | 115.260002 | 117.320000 | 116.570236 | 176940500 | 112.820000 | 0.039887 | 4.500000 | 120.675998 |
2020-09-10 | 120.360001 | 120.500000 | 112.500000 | 113.489998 | 112.764717 | 182274400 | 117.320000 | -0.032646 | -3.830002 | 117.093999 |
2020-09-11 | 114.570000 | 115.230003 | 110.000000 | 112.000000 | 111.284241 | 180860300 | 113.489998 | -0.013129 | -1.489998 | 115.317999 |
2020-09-14 | 114.720001 | 115.930000 | 112.800003 | 115.360001 | 114.622765 | 140150100 | 112.000000 | 0.030000 | 3.360001 | 114.198000 |
2020-09-15 | 118.330002 | 118.830002 | 113.610001 | 115.540001 | 114.801620 | 184642000 | 115.360001 | 0.001560 | 0.180000 | 114.742000 |
윈도우 크기를 결정하는 'window' 매개변수에 5를 전달하고 평균을 계산한다. 윈도우 사이즈를 5로 전달했으므로 데이터 5개가 확보되지 않으면 NaN을 출력한다. 종종 NaN 처리를 위해 행을 삭제하거나 보간법을 사용하지만, 이동 평균선을 계산할 때는 결측치가 생기는것이 자연스럽기 때문에 NaN 값을 그대로 둔다.
resample() 함수¶
주가 데이터를 분석하다 보면 데이터를 가능한 한 가장 단위로 수집하는 경향이 있다. 이는 데이터의 정확도를 위해 당연한 일이겠지만, 단위를 바꾸어서 예측 모델링을 진행해야 할 경우가 있다. 예를 들어 월 단위의 데이터를 연 단위의 시가-고가-저가-종가(OHLC) 가격의 평균을 확하고 싶을 때, 판다스의 resample() 함수를 이용한다.
Resampling의 두 가지 유형
- 업 샘플링: 분 단위, 초 단위로 샘플의 빈도수를 증가시킨다.
- 다운 샘플링: 몇 일, 몇 달 단위로 샘플의 빈도수를 감소시킨다.
업 샘플링은 보간법을 사용해 누락된 데이터를 채워나가고, 다운 샘플링은 기존 데이터를 집계하는 방법으로 데이터를 사용한다.시계열 데이터 분석에서는 다운 샘플링을 사용하는 경우가 많으므로 다운 샘플링에 대해 알아보자.
import pandas as pd
index = pd.date_range(start = '2019-01-01', end ='2019-10-01', freq='B')
series = pd.Series(range(len(index)), index = index)
series
2019-01-01 0 2019-01-02 1 2019-01-03 2 2019-01-04 3 2019-01-07 4 ... 2019-09-25 191 2019-09-26 192 2019-09-27 193 2019-09-30 194 2019-10-01 195 Freq: B, Length: 196, dtype: int64
date_range() 함수를 이용해 임의의 시계열 데이터를 생성한 후, 시작일자와 종료일자를 전달하고 freq 옵션에 값을 'B'(Business Day)로 전달하여 일정한 간격으로 날짜를 생성한다.
series.resample(rule='M').sum()
2019-01-31 253 2019-02-28 650 2019-03-31 1113 2019-04-30 1639 2019-05-31 2231 2019-06-30 2370 2019-07-31 3220 2019-08-31 3575 2019-09-30 3864 2019-10-31 195 Freq: M, dtype: int64
resample() 함수의 시간 간격 매개변수를 'M'으로 설정하여 월말 일자를 기준으로 데이터를 정렬한다. 추가로 sum() 함수를 이용하여 월별 합계를 계산한다.
월말 일자 하루 데이터 또는 월초 일자 하루 데이터를 확인하고 싶을 때는 last()와 first() 함수를 호출하여 구할 수 있다.
#월말 일자 확인
series.resample(rule='M').last()
2019-01-31 22 2019-02-28 42 2019-03-31 63 2019-04-30 85 2019-05-31 108 2019-06-30 128 2019-07-31 151 2019-08-31 173 2019-09-30 194 2019-10-31 195 Freq: M, dtype: int64
월초 일자를 확인할 때 주의할 점은, 월말 일자를 확인할 때 'M'을 전달했다면 월초 일자를 확인할 때는 start를 의미하는 S를 덧붙여 'MS'를 전달한다.
series.resample(rule='MS').first()
2019-01-01 0 2019-02-01 23 2019-03-01 43 2019-04-01 64 2019-05-01 86 2019-06-01 109 2019-07-01 129 2019-08-01 152 2019-09-01 174 2019-10-01 195 Freq: MS, dtype: int64
정리¶
shift(), pct_change(), diff() 함수는 특징이 서로 비슷해계산 방향이나 이동 방향을 혼동하기 쉽다. 세 함수 모두 실제 분석에서 자주 사용되므로 자유자재로 사용할 수 있도록 익히자. rolling()와 resample() 함수는 모두 일정 시간 간격으로 데이터를 조정할 수 있는 기능이 있다. 하지만 rolling()은 일정 크기(윈도우 크기)로 새로운 시점에서 새로운 결과값을 도출하지만, resample() 함수는 주기(freq) 기준으로 동작하므로 고정된 크기 내에서 최솟값과 최댓값 사이의 이절 값을 보여주게 된다. 이외에도 판다스는 데이터 프레임을 합치는 concat, append, merge 함수, 칼럼의 유일한 값을 뽑는 unique 함수, 빠른 연산을 지원하는 map, apply 함수, groupby, join, drop, del, reset_index, rename 등 다양한 기능을 지원한다. 모든 함수들의 사용법을 설명하기에 한계가 있으므로 직접 실습해보면서 익히도록 하자.
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
'ML & DL (Machine Learning&Deep Learning) > DE & DS' 카테고리의 다른 글
[DE & DS] 경제 뉴스를 분석해보자! (1) (0) | 2021.08.02 |
---|
댓글