<jupyter_start><jupyter_text>3.2 statsmodels의 전처리 기능 StatsModels 패키지는 통계분석과 관련된 R의 기능을 파이썬으로 옮겨오기 위한 패키지이다. R에는 데이터프레임과 문자열 기호를 이용하여 회귀모형을 정의하는 방법이 존재한다. StatsModels 패키지도 이러한 R 스타일 모형 정의 방법을 지원한다. 이러한 지원을 가능하게 하는 것은 patsy라는 패키지 덕분이다. 여기에서는 patsy 패키지의 간단한 사용법과 이를 이용하여 StatsModels에서 회귀모형을 정의하는 방법을 설명한다.  patsy 패키지 소개 patsy 패키지는 회귀분석 전처리를 위한 패키지로 데이터프레임을 가공하여 인코딩, 변환 등을 쉽게 해주는 기능을 제공한다.<jupyter_code>from patsy import *
import statsmodels.api as sm
from patsy import *<jupyter_output><empty_output><jupyter_text>patsy 패키지의 `dmatrix`라는 명령을 사용하면 실험 설계 행렬(experiment design matrix)을 간단히 만들수 있다. `dmatrix`에 다음과 같이 모형 정의 문자열 `formula`와 원 데이터를 담은 데이터프레임 `data`을 입력하면 `formula`에서 지정한 대로 변환된 데이터 `data_transformed`를 출력한다. ```data_transformed = dmatrix(formula, data)``` patsy 패키지가 제공하는 `demo_data` 명령으로 다음과 같이 예제 데이터 `x1`, `x2`, `y`를 만들자. `dmatrix`의 첫번째 기능은 자동 상수항 결합 기능이다. 대상이 되는 데이터에 자동으로 Intecept라는 이름의 데이터 열을 추가한다. 다음 예제에서 스타일 문자열은 단순히 `"x1"`이다. 스타일 문자열은 데이터와 연산자로 이루어지는데 데이터는 변수명 혹은 데이터프레임 열 이름으로 지정한다. 변수명으로 지정하는 경우에는 현재의 이름 공간(name space)에서 변수를 찾고 데이터프레임 열 이름을 지정하는 경우에는 `data`라는 인수에 데이터프레임을 넣어주어야 한다.<jupyter_code>df = pd.DataFrame(demo_data("x1", "x2", "y"))
df
df = pd.DataFrame(demo_data("x1", "x2", "y"))
df
dmatrix("x1", df)
dmatrix("x1",df)<jupyter_output><empty_output><jupyter_text>R-style formula 연산자모형정의 연산자 `formula`에 복수의 데이터를 지정하는 경우에는 다음과 같은 연산자를 포함해야 한다.| 기호 | 설명 ||-|-||1, 0| 바이어스(bias, intercept) 추가 및 제거 ||`+`| 설명 변수 추가 ||`-`| 설명 변수 제거 ||`:`| 상호작용(interaction) ||`*`| `a*b = a + b + a:b` ||`/`| `a/b = a + a:b` | 상수항을 제외하고자 하는 경우에는 `- 1` 또는 `+ 0`을 써주어야 한다.<jupyter_code>dmatrix("x1 - 1", df)
dmatrix("x1 - 1", df)
dmatrix("x1 + 0", df)<jupyter_output><empty_output><jupyter_text>데이터를 추가하는 경우에는 `+` 연산자를 사용한다.<jupyter_code>dmatrix("x1+x2",df)
dmatrix("x1 + x2", df)<jupyter_output><empty_output><jupyter_text>마찬가지로 `-1` 또는 `+0`이 있으면 상수항이 없어진다.<jupyter_code>dmatrix("x1 + x2 - 1", df)<jupyter_output><empty_output><jupyter_text>두 변수의 곱을 새로운 변수로 추가하려면 상호작용(interaction) 연산자 `:`를 사용한다.<jupyter_code>dmatrix("x1 + x2 + x1:x2", df)
dmatrix("x1+x2+x1:x2",df)<jupyter_output><empty_output><jupyter_text>위 식은 다음과 같이 `*` 연산자로 간단하게 나타낼 수도 있다.<jupyter_code>dmatrix("x1 * x2", df)
dmatrix("x1 * x2", df)<jupyter_output><empty_output><jupyter_text>`/` 연산자는 두번째 데이터를 빼고 출력한다.<jupyter_code>dmatrix("x1/x2",df)
dmatrix("x1 / x2", df)<jupyter_output><empty_output><jupyter_text>수학 변환 `dmatrix`에서는 일반적인 수학 변환(transform)도 가능하다. numpy 함수 뿐 아니라 사용자 정의 함수도 사용할 수 있다.<jupyter_code>dmatrix("x1+np.log(np.abs(x2))",df)
dmatrix("x1+np.log(np.abs(x2))",df)
def doubleit(x):
    return 2*x

dmatrix("doubleit(x1)",df)
def doubleit(x):
    return 2 * x

dmatrix("doubleit(x1)", df)<jupyter_output><empty_output><jupyter_text>상태 보존 변환 patsy의 가장 강력한 기능 중의 하나는 상태 보존 변환(stateful trasform)이 가능하다는 점이다. 예를 들어 다음 변환 함수는 평균을 계산하여 빼주거나 및 표준편차를 계산하여 나누어주는데 이 때 계산한 평균과 표준편차를 내부에 상태변수로 저장한다.* `center(x)`: 평균 제거* `standardize(x)`: 평균 제거 및 표준편차로 스케일링* `scale(x)`: `standardize(x)` 과 같음  예를 들어 x1 데이터의 평균을 제거하는 변환은 다음과 같다.<jupyter_code>dm = dmatrix("center(x1)", df)
dm
dm = dmatrix("center(x1)",df)
dm<jupyter_output><empty_output><jupyter_text>이 변환 연산은 다음과 같이 x1 데이터에서 x1의 평균을 빼는 것이다.<jupyter_code>df.x1 - np.mean(df.x1)
df.x1 - np.mean(df.x1)<jupyter_output><empty_output><jupyter_text>그런데 이 때 사용한 평균값은 `design_info`라는 속성에 상태변수(state variable)로서 저장된다.<jupyter_code>dm
type(dm.design_info)<jupyter_output><empty_output><jupyter_text>이 값을 상태변수로 저장하는 이유는 다음과 같다.어떤 데이터 $X_{train}$을 학습용 데이터로 사용하여 예측모형으로 만든다고 하자. 이 때 학습성능을 좋게 하기 위해 $X_{train}$에서 $X_{train}$의 평균 $\bar{X}_{train}$(예를 들어 100)을 뺀 평균 제거 데이터 $X_{train} - 100$를 원래의 데이터 대신 학습용 데이터로 사용하여 모형을 만드는 경우가 있다. 이를 전처리 단계라고 한다.학습이 끝난 후 이 모형을 사용하여 실제 예측을 하자. 새로운 검증용 데이터 $X_{test}$를 이 모형에 넣으려면 모형을 학습할 때 사용한 것과 같은 전처리를 해야 한다. 즉, $X_{test}$에서 $X_{train}$의 평균인 $100$을 뺀 $X_{test}-100$을 입력으로 넣어서 출력을 계산해야 한다. 이 때 $X_{test}$의 평균이 아니라 $X_{train}$의 평균을 사용한다는 점에 주의한다. 이렇게 하기 위해서는 전처리 과정에서 계산한 $X_{train}$의 평균값 $100$을 기억하고 있어야 한다. patsy 패키지에서는 `center` 변환을 했을 때 사용한 평균값을 내부에 저장하고 있기 때문에 이러한 일을 할 수 있다. 예를 들어 다음처럼 검증용의 새로운 데이터가 있을 때,<jupyter_code>df_new = df.copy()
df_new["x1"] = df_new["x1"] * 10
df_new
df_new=df.copy()
df_new["x1"]=df_new["x1"]*10
df_new<jupyter_output><empty_output><jupyter_text>`build_design_matrices` 명령을 사용하면 이미 저장된 x1의 평균을 이용하여 같은 변환을 한다.<jupyter_code>build_design_matrices([dm.design_info], df_new)<jupyter_output><empty_output><jupyter_text>이 값은 다음 계산 결과와 같다.<jupyter_code>df_new.x1 - np.mean(df.x1)<jupyter_output><empty_output><jupyter_text>평균값을 다시 새롭게 구해서 계산한 것과 다르다는 것을 알 수있다.<jupyter_code>dmatrix('center(x1)',df_new)
dmatrix("center(x1)", df_new)
df_new.x1 - np.mean(df_new.x1)<jupyter_output><empty_output><jupyter_text>변수 보호 함수를 사용한 변수 변환 이외에도 모형 정의 문자열 자체내에 연산기호를 넣어 연산한 값을 만드는 것도 가능하다. 이 때에는 모형정의 연산자와 혼동되지 않도록  `I()` 연산자를 추가해야 한다.<jupyter_code>dmatrix("I(x1 + x2)", df)
dmatrix("I(x1+x2)",df)<jupyter_output><empty_output><jupyter_text>이 값을 다음 식과 비교하면  `I()`의 기능을 확실히 알 수 있다.<jupyter_code>dmatrix("x1 + x2", df)
dmatrix("x1+x2",df)<jupyter_output><empty_output><jupyter_text>다항회귀  `I()` 연산자를 활용하면 다항회귀(polynomial regression)도 할 수 있다.<jupyter_code>dmatrix("x1+I(x1*x1)+I(x1**3)+I(x1**4)",df)
dmatrix("x1+I(x1*x1)+I(x1**3)+I(x1**4)",df)
dmatrix("x1 + I(x1*x1) + I(x1**3) + I(x1**4)", df)<jupyter_output><empty_output><jupyter_text>`OLS.from_formula` 메서드 선형회귀분석을 위한 `OLS` 클래스에는 모형 정의 문자열을 사용할 수 있는 `from_formula`라는 클래스 메서드가 있다. 이 메서드를 쓰면 사용자가 데이터 행렬을 직접 정의하지 않고 모형 정의 문자열만으로 선형회귀모형을 만드는 것이 가능하다. 선형 회귀모형을 formula로 정의할 때는 `~` 연산자를 사용한다. `~` 연산자의 왼쪽에는 종속 변수, 오른쪽에는 독립 변수를 넣어서 정의한다. 예를 들어 다음과 같은 데이터가 있을 때,<jupyter_code>np.random.seed(0)
x1 = np.random.rand(20) + 10
x2 = np.random.rand(20) * 10
y = x1 + 2 * x2 + np.random.randn(20)
df4 = pd.DataFrame(np.array([x1, x2, y]).T, columns=["x1", "x2", "y"])
np.random.seed(0)
x1 = np.random.rand(20) + 10
x2 = np.random.rand(20) * 10
y = x1 + 2 * x2 + np.random.randn(20)
df4 = pd.DataFrame(np.array([x1, x2, y]).T, columns=["x1", "x2", "y"])<jupyter_output><empty_output><jupyter_text>다음 두가지 방법으로 만든 모형은 동일하다.<jupyter_code># 직접 데이터 행렬을 만드는 경우
dfy = df4.iloc[:, -1]
dfX = sm.add_constant(df4.iloc[:, :-1])
model1 = sm.OLS(dfy, dfX)

print(model1.fit().summary())
# 직접 데이터 행렬을 만드는 경우
dfy = df4.iloc[:, -1]
dfX = sm.add_constant(df4.iloc[:, :-1])
model1 = sm.OLS(dfy, dfX)

print(model1.fit().summary())
# 모형 정의 문자열을 사용하는 경우
model2 = sm.OLS.from_formula("y ~ x1 + x2", data=df4)

print(model2.fit().summary())<jupyter_output>OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.967
Model:                            OLS   Adj. R-squared:                  0.963
Method:                 Least Squares   F-statistic:                     246.8
Date:                Mon, 17 Jun 2019   Prob (F-statistic):           2.75e-13
Time:                        15:59:58   Log-Likelihood:                -29.000
No. Observations:                  20   AIC:                             64.00
Df Residuals:                      17   BIC:                             66.99
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|[...]