내용
연속형변수를 목록형변수로 변환
연속형 변수인 주가자료를 종가의 상승과 하락에 대한 추정을 위해 로지스틱 회계분석을 적용할 수 있습니다. 이 분석의 자료는 목록변수이어야 하므로 연속형변수를 목록변수로의 전환이 필요합니다. 이번 포스트에서는 이 과정을 소개합니다.
주가자료의 호출과 정리
파이썬 패키지 FinanceDataReader
를 사용하여 다양한 금융자료를 사용할 수 있습니다. 다음은 일정 기간의 코스피 자료를 호출한 것입니다.
import numpy as np import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt import FinanceDataReader as fdr
st=pd.Timestamp(2010,8, 26) et=pd.Timestamp(2022, 5, 27) data=fdr.DataReader('KS11', st, et) data.head(2)
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2010-08-26 | 1729.76 | 1744.08 | 1744.40 | 1729.76 | 303050000.0 | -0.0029 |
2010-08-27 | 1729.56 | 1724.00 | 1732.84 | 1719.20 | 245580000.0 | -0.0001 |
호출받은 자료에 0, Na, inf 등 다양한 종류의 결측치가 포함되어 있을 수 있습니다. 이들은 직전 또는 직후 값으로 대체 또는 삭제 할 수 있습니다. 위에서 사용한 모듈로 부터 호출한 자료는 결측치를 0으로 대체하고 있습니다. 다음은 자료의 값이 0인 행과 열을 반환한 것입니다.
np.where(data==0)
(array([ 450, 473, 541, 571, 643, 689, 831, 904, 1060, 1118, 1273, 2005, 2081, 2125, 2150, 2376]), array([5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]))
인덱스 적용
data에서 0이 아닌 부분만을 추출할 경우 자료의 크기는 변함이 없습니다. 또한 0인 부분도 발견되지 않습니다.
data1=data[data != 0] data1.shape
(2899, 6)
np.where(data1==0)
(array([], dtype=int64), array([], dtype=int64))
data에서 0인 인덱스의 data1과 비교해보면 0이 nan로 전환됨을 알 수 있습니다.
data.iloc[450, 5], data1.iloc[450, 5]
(0.0, nan)
nan을 제거하기 위해 DataFrame.dropna()
메서드를 사용합니다. 이 결과 자료의 크기는 정확히 data에서 0이 포함된 부분이 제거되었음을 알 수 있습니다.
data2=data1.dropna() data2.shape
(2883, 6)
.mask()와 .where() 적용
DataFrame.mask(condition, other=nan)메서드를 적용하여 0을 지정한 값으로 전환할 수 있습니다. 이 메서드는 condition=True일 경우 other로 전환됩니다. 이 메서드와 반대는 DataFrame.where(condition, other)로서 condition=True인 경우는 원래값을 유지하고 아닌 경우 other로 전환합니다.
x=pd.DataFrame(np.random.randint(0, 10, (3, 5))) x
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 6 | 5 | 5 | 8 | 2 |
1 | 8 | 0 | 8 | 5 | 6 |
2 | 2 | 1 | 6 | 6 | 8 |
x.where(x==0, np.nan)
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | NaN | NaN | NaN | NaN | NaN |
1 | NaN | 0.0 | NaN | NaN | NaN |
2 | NaN | NaN | NaN | NaN | NaN |
x.where(x!=0)
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 6 | 5.0 | 5 | 8 | 2 |
1 | 8 | NaN | 8 | 5 | 6 |
2 | 2 | 1.0 | 6 | 6 | 8 |
x.mask(x==0)
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 6 | 5.0 | 5 | 8 | 2 |
1 | 8 | NaN | 8 | 5 | 6 |
2 | 2 | 1.0 | 6 | 6 | 8 |
메서드 mask를 사용하여 객체 data이 값 0을 nan으로 변환합니다. 그 결과는 위에서 인덱스를 사용하여 변환한 결과와 같습니다.
data1=data.mask(data==0) data1.iloc[449:452, :]
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2012-06-18 | 1891.71 | 1892.91 | 1901.11 | 1887.05 | 376280000.0 | 0.0181 |
2012-06-19 | 1891.77 | 1885.56 | 1895.32 | 1883.56 | 380910000.0 | NaN |
2012-06-20 | 1904.12 | 1908.19 | 1908.19 | 1895.27 | 367740000.0 | 0.0065 |
결측값 치환
nan을 제거하기 위해 위에서와 같이 DataFrame.dropna() 메서드를 적용하면 Nan이 포함된 행을 제거한 결과를 반환합니다. 대신에 DataFrame.ffill()
또는 DataFrame.bfill()
메서드를 적용하여 직전 행의 값, 또는 진후 행의 값으로 대체할 수 있습니다.
data1.ffill().iloc[449:452, :]
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2012-06-18 | 1891.71 | 1892.91 | 1901.11 | 1887.05 | 376280000.0 | 0.0181 |
2012-06-19 | 1891.77 | 1885.56 | 1895.32 | 1883.56 | 380910000.0 | 0.0181 |
2012-06-20 | 1904.12 | 1908.19 | 1908.19 | 1895.27 | 367740000.0 | 0.0065 |
data1.bfill().iloc[449:452, :]
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2012-06-18 | 1891.71 | 1892.91 | 1901.11 | 1887.05 | 376280000.0 | 0.0181 |
2012-06-19 | 1891.77 | 1885.56 | 1895.32 | 1883.56 | 380910000.0 | 0.0065 |
2012-06-20 | 1904.12 | 1908.19 | 1908.19 | 1895.27 | 367740000.0 | 0.0065 |
위 두 메서드에 의한 nan의 치환은 DataFrame.fillna(value, method)
메서드로 대신할 수 있습니다. 이 메서드는 nan을 지정한 값(value)나 방법(mehod)으로 치환합니다. 여기서 method는 'bfill'과 'ffill'를 인수로 전달할 수 있으며 결과는 각각 DataFrame.bfill()와 DataFrame.ffill()와 같습니다.
data1.fillna(method="ffill").iloc[449:452, :]
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2012-06-18 | 1891.71 | 1892.91 | 1901.11 | 1887.05 | 376280000.0 | 0.0181 |
2012-06-19 | 1891.77 | 1885.56 | 1895.32 | 1883.56 | 380910000.0 | 0.0181 |
2012-06-20 | 1904.12 | 1908.19 | 1908.19 | 1895.27 | 367740000.0 | 0.0065 |
data1.fillna(method="bfill").iloc[449:452, :]
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2012-06-18 | 1891.71 | 1892.91 | 1901.11 | 1887.05 | 376280000.0 | 0.0181 |
2012-06-19 | 1891.77 | 1885.56 | 1895.32 | 1883.56 | 380910000.0 | 0.0065 |
2012-06-20 | 1904.12 | 1908.19 | 1908.19 | 1895.27 | 367740000.0 | 0.0065 |
변화량 계산과 표준화
목록변수로 전환하기 위해 Close, Open, High, Low, Volume, Change의 각 변수별로 2일간의 변화량을 계산하여 적용합니다. DataFrame.pct_change()
메서드를 적용합니다.
d=data2.pct_change().dropna() d.head(2)
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
Date | ||||||
2010-08-27 | -0.000116 | -0.011513 | -0.006627 | -0.006105 | -0.189639 | -0.965517 |
2010-08-30 | 0.017675 | 0.014513 | 0.015876 | 0.015711 | 0.007248 | -178.000000 |
전환결과의 데이터들은 변수별로 scale의 차이가 존재합니다. 이는 통계 분석에 큰 분산의 원인이며 모델의 정확도에 손상을 줍니다. 이 부분은 데이터의 표준화로 보상이 됩니다.
from sklearn.preprocessing import StandardScaler XScaler=StandardScaler().fit(d.values) Xn=XScaler.transform(d.values) Xn[:10]
array([[-3.03562817e-02, -1.10544148e+00, -8.04888824e-01, -6.28245191e-01, -4.19690250e-02, 2.24060138e-02], [ 1.68248655e+00, 1.35086115e+00, 1.85616377e+00, 1.54718341e+00, -3.70464378e-02, -1.51836969e+01], ⋮
목록화
위 결과를 목록화하기 위해 각 값을 일정한 구간별로 분류합니다. 이 과정은 pd.cut()또는 pd.qcut() 함수를 사용할 수 있습니다. cut() 함수는 분류 구간의 상한값과 하한값을 지정하는데 반해 qcut()는 분류구간의 갯수를 지정합니다. 그러므로 qcut()에 의한 분류는 각 소그룹의 크기가 같습니다. 또한 이 두 함수에 대상 자료는 Series로서 1차원 벡터이어야 합니다. 즉, 하나의 열 또는 행에 대해서만 적용할 수 있습니다. 다음과 같이 for 문을 적용합니다.
d2={} for i in range(Xn.shape[1]): d2[i]=pd.qcut(pd.Series(Xn[:,i]), q=10, labels=range(1, 11)) d2=pd.DataFrame(d2.values()).T d2.columns=d.columns d2.head(2)
Close | Open | High | Low | Volume | Change | |
---|---|---|---|---|---|---|
0 | 5 | 1 | 2 | 3 | 2 | 6 |
1 | 10 | 10 | 10 | 10 | 6 | 1 |
댓글
댓글 쓰기