기본 콘텐츠로 건너뛰기

벡터와 행렬에 관련된 그림들

연속형변수를 목록형변수로 변환

내용

연속형변수를 목록형변수로 변환

연속형 변수인 주가자료를 종가의 상승과 하락에 대한 추정을 위해 로지스틱 회계분석을 적용할 수 있습니다. 이 분석의 자료는 목록변수이어야 하므로 연속형변수를 목록변수로의 전환이 필요합니다. 이번 포스트에서는 이 과정을 소개합니다.

주가자료의 호출과 정리

파이썬 패키지 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-261729.76 1744.08 1744.40 1729.76 303050000.0 -0.0029
2010-08-271729.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
06 5 5 8 2
18 0 8 5 6
22 1 6 6 8
x.where(x==0, np.nan)
0 1 2 3 4
0NaN NaN NaN NaN NaN
1NaN 0.0 NaN NaN NaN
2NaN NaN NaN NaN NaN
x.where(x!=0)
0 1 2 3 4
06 5.0 5 8 2
18 NaN 8 5 6
22 1.0 6 6 8
x.mask(x==0)
0 1 2 3 4
06 5.0 5 8 2
18 NaN 8 5 6
22 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-300.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
05 1 2 3 2 6
110 10 10 10 6 1

댓글

이 블로그의 인기 게시물

[Linear Algebra] 유사변환(Similarity transformation)

유사변환(Similarity transformation) n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사행렬(similarity matrix)이 되며 행렬 A를 가역행렬 P와 B로 분해하는 것을 유사 변환(similarity transformation) 이라고 합니다. $$\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B $$ 식 2는 식 1의 양변에 B의 고유값을 고려한 것입니다. \begin{align}\tag{식 2} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align} 식 2의 행렬식은 식 3과 같이 정리됩니다. \begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \textsf{det}(I)\end{aligned}\end{align} 유사행렬의 특성 유사행렬인 두 정방행렬 A와 B는 'A ~ B' 와 같...

[sympy] Sympy객체의 표현을 위한 함수들

Sympy객체의 표현을 위한 함수들 General simplify(x): 식 x(sympy 객체)를 간단히 정리 합니다. import numpy as np from sympy import * x=symbols("x") a=sin(x)**2+cos(x)**2 a $\sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$ simplify(a) 1 simplify(b) $\frac{x^{3} + x^{2} - x - 1}{x^{2} + 2 x + 1}$ simplify(b) x - 1 c=gamma(x)/gamma(x-2) c $\frac{\Gamma\left(x\right)}{\Gamma\left(x - 2\right)}$ simplify(c) $\displaystyle \left(x - 2\right) \left(x - 1\right)$ 위의 예들 중 객체 c의 감마함수(gamma(x))는 확률분포 등 여러 부분에서 사용되는 표현식으로 다음과 같이 정의 됩니다. 감마함수는 음이 아닌 정수를 제외한 모든 수에서 정의됩니다. 식 1과 같이 자연수에서 감마함수는 factorial(!), 부동소수(양의 실수)인 경우 적분을 적용하여 계산합니다. $$\tag{식 1}\Gamma(n) =\begin{cases}(n-1)!& n:\text{자연수}\\\int^\infty_0x^{n-1}e^{-x}\,dx& n:\text{부동소수}\end{cases}$$ x=symbols('x') gamma(x).subs(x,4) $\displaystyle 6$ factorial 계산은 math.factorial() 함수를 사용할 수 있습니다. import math math.factorial(3) 6 a=gamma(x).subs(x,4.5) a.evalf(3) 11.6 simpilfy() 함수의 알고리즘은 식에서 공통사항을 찾아 정리하...

sympy.solvers로 방정식해 구하기

sympy.solvers로 방정식해 구하기 대수 방정식을 해를 계산하기 위해 다음 함수를 사용합니다. sympy.solvers.solve(f, *symbols, **flags) f=0, 즉 동차방정식에 대해 지정한 변수의 해를 계산 f : 식 또는 함수 symbols: 식의 해를 계산하기 위한 변수, 변수가 하나인 경우는 생략가능(자동으로 인식) flags: 계산 또는 결과의 방식을 지정하기 위한 인수들 dict=True: {x:3, y:1}같이 사전형식, 기본값 = False set=True :{(x,3),(y,1)}같이 집합형식, 기본값 = False ratioal=True : 실수를 유리수로 반환, 기본값 = False positive=True: 해들 중에 양수만을 반환, 기본값 = False 예 $x^2=1$의 해를 결정합니다. solve() 함수에 적용하기 위해서는 다음과 같이 식의 한쪽이 0이 되는 형태인 동차식으로 구성되어야 합니다. $$x^2-1=0$$ import numpy as np from sympy import * x = symbols('x') solve(x**2-1, x) [-1, 1] 위 식은 계산 과정은 다음과 같습니다. $$\begin{aligned}x^2-1=0 \rightarrow (x+1)(x-1)=0 \\ x=1 \; \text{or}\; -1\end{aligned}$$ 예 $x^4=1$의 해를 결정합니다. solve() 함수의 인수 set=True를 지정하였으므로 결과는 집합(set)형으로 반환됩니다. eq=x**4-1 solve(eq, set=True) ([x], {(-1,), (-I,), (1,), (I,)}) 위의 경우 I는 복소수입니다.즉 위 결과의 과정은 다음과 같습니다. $$x^4-1=(x^2+1)(x+1)(x-1)=0 \rightarrow x=\pm \sqrt{-1}, \; \pm 1=\pm i,\; \pm1$$ 실수...