다운로드
작성자: admin 작성일시: 2016-06-03 22:23:48 조회수: 2345 다운로드: 233
카테고리: 머신 러닝 태그목록:

R 스타일 모형 정의

StatsModels 패키지는 통계분석과 관련된 R의 기능을 파이썬으로 옮겨오기 위한 패키지이다. R에는 데이터프레임과 문자열 기호를 이용하여 회귀모형을 정의하는 방법이 존재한다. StatsModels 패키지도 이러한 R 스타일 모형 정의 방법을 지원한다. 이러한 지원을 가능하게 하는 것은 patsy라는 패키지 덕분이다. 여기에서는 patsy 패키지의 간단한 사용법과 이를 이용하여 StatsModels에서 회귀 모형을 정의하는 방법을 설명한다.

patsy 패키지 소개

patsy 패키지는 회귀분석 전처리를 위한 패키지로 데이터프레임을 가공하여 인코딩, 변환 등을 쉽게 해주는 기능을 제공한다.

patsy 패키지의 dmatrix라는 명령을 사용하면 실험설계행렬(experiment design matrix)을 간단히 만들수 있다. dmatrix에 다음과 같이 모형 정의 문자열 formula와 원데이터 data을 입력하면 formula에서 지정한 대로 변환된 데이터 data_transformed를 출력한다.

data_transformed = dmatrix(formula, data)
In:
from patsy import dmatrix

예제를 위해 다음과 같이 데이터 x1, x2를 만들자.

In:
np.random.seed(0)
x1 = np.random.rand(5) + 10
x2 = np.random.rand(5) * 10
y = x1 + 2 * x2 + np.random.randn(5)
df1 = pd.DataFrame(x1, columns=["x1"])
df2 = pd.DataFrame(np.array([x1, x2]).T, columns=["x1", "x2"])
df = pd.DataFrame(np.array([x1, x2, y]).T, columns=["x1", "x2", "y"])
In:
df1
Out:
x1
0 10.548814
1 10.715189
2 10.602763
3 10.544883
4 10.423655
In:
df2
Out:
x1 x2
0 10.548814 6.458941
1 10.715189 4.375872
2 10.602763 8.917730
3 10.544883 9.636628
4 10.423655 3.834415

dmatrix의 첫번째 기능은 자동 오그멘테이션이다. 대상이 되는 데이터에 자동으로 Intecept라는 이름의 컬럼을 추가한다.

다음 예제에서 스타일 문자열은 단순히 "x1"이다. 스타일 문자열은 데이터와 연산자로 이루어지는데 데이터는 변수명 혹은 데이터프레임 컬럼 라벨로 지정한다. 변수명으로 지정하는 경우에는 현재의 이름 공간(name space)에서 변수를 찾고 데이터프레임 컬럼 라벨을 지정하는 경우에는 data라는 인수에 데이터프레임을 넣어주어야 한다.

In:
dmatrix("x1")
Out:
DesignMatrix with shape (5, 2)
  Intercept        x1
          1  10.54881
          1  10.71519
          1  10.60276
          1  10.54488
          1  10.42365
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)
In:
dmatrix("x1", df1)
Out:
DesignMatrix with shape (5, 2)
  Intercept        x1
          1  10.54881
          1  10.71519
          1  10.60276
          1  10.54488
          1  10.42365
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)
In:
dmatrix("x1", df2)
Out:
DesignMatrix with shape (5, 2)
  Intercept        x1
          1  10.54881
          1  10.71519
          1  10.60276
          1  10.54488
          1  10.42365
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)

R-style formula 연산자

모형정의 연산자 formula에 복수의 데이터를 지정하는 경우에는 다음과 같은 연산자를 포함해야 한다.

기호 설명
+ 설명 변수 추가
- 설명 변수 제거
1, 0 intercept. (제거시 사용)
: interaction (곱)
* a*b = a + b + a:b
/ a/b = a + a:b
~ 종속 - 독립 관계

상수항을 제외하고자 하는 경우에는 - 1 또는 + 0을 써주어야 한다.

In:
dmatrix("x1 - 1")
Out:
DesignMatrix with shape (5, 1)
        x1
  10.54881
  10.71519
  10.60276
  10.54488
  10.42365
  Terms:
    'x1' (column 0)
In:
dmatrix("x1 + 0")
Out:
DesignMatrix with shape (5, 1)
        x1
  10.54881
  10.71519
  10.60276
  10.54488
  10.42365
  Terms:
    'x1' (column 0)

두 개 이상의 데이터를 포함하는 경우에는 + 연산자를 사용한다.

In:
dmatrix("x1 + x2")
Out:
DesignMatrix with shape (5, 3)
  Intercept        x1       x2
          1  10.54881  6.45894
          1  10.71519  4.37587
          1  10.60276  8.91773
          1  10.54488  9.63663
          1  10.42365  3.83442
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)
    'x2' (column 2)
In:
dmatrix("x1 + x2 - 1")
Out:
DesignMatrix with shape (5, 2)
        x1       x2
  10.54881  6.45894
  10.71519  4.37587
  10.60276  8.91773
  10.54488  9.63663
  10.42365  3.83442
  Terms:
    'x1' (column 0)
    'x2' (column 1)

두 변수의 곱을 새로운 변수로 추가하려면 : 연산자를 사용한다.

In:
dmatrix("x1 + x2 + x1:x2 - 1")
Out:
DesignMatrix with shape (5, 3)
        x1       x2      x1:x2
  10.54881  6.45894   68.13417
  10.71519  4.37587   46.88830
  10.60276  8.91773   94.55258
  10.54488  9.63663  101.61711
  10.42365  3.83442   39.96862
  Terms:
    'x1' (column 0)
    'x2' (column 1)
    'x1:x2' (column 2)

위 식은 다음과 같이 * 연산자로 간단하게 나타낼 수도 있다.

In:
dmatrix("x1 * x2 - 1")
Out:
DesignMatrix with shape (5, 3)
        x1       x2      x1:x2
  10.54881  6.45894   68.13417
  10.71519  4.37587   46.88830
  10.60276  8.91773   94.55258
  10.54488  9.63663  101.61711
  10.42365  3.83442   39.96862
  Terms:
    'x1' (column 0)
    'x2' (column 1)
    'x1:x2' (column 2)

/ 연산자는 다음과 같은 출력을 낸다.

In:
dmatrix("x1 / x2 - 1")
Out:
DesignMatrix with shape (5, 2)
        x1      x1:x2
  10.54881   68.13417
  10.71519   46.88830
  10.60276   94.55258
  10.54488  101.61711
  10.42365   39.96862
  Terms:
    'x1' (column 0)
    'x1:x2' (column 1)

변환

dmatrix에서는 일반적인 수학 변환(transform)도 가능하다. numpy 함수 뿐 아니라 사용자 정의 함수와 다음과 같은 patsy 전용 함수도 사용할 수 있다.

  • center(x): 평균 제거
  • standardize(x): 평균 제거 및 표준편차로 스케일링
  • scale(x): standardize(x) 과 같음
In:
dmatrix("x1 + np.log(np.abs(x2))")
Out:
DesignMatrix with shape (5, 3)
  Intercept        x1  np.log(np.abs(x2))
          1  10.54881             1.86547
          1  10.71519             1.47611
          1  10.60276             2.18804
          1  10.54488             2.26557
          1  10.42365             1.34402
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)
    'np.log(np.abs(x2))' (column 2)
In:
def doubleit(x):
    return 2 * x

dmatrix("doubleit(x1)")
Out:
DesignMatrix with shape (5, 2)
  Intercept  doubleit(x1)
          1      21.09763
          1      21.43038
          1      21.20553
          1      21.08977
          1      20.84731
  Terms:
    'Intercept' (column 0)
    'doubleit(x1)' (column 1)
In:
dmatrix("center(x1) + standardize(x1) + scale(x2)")
Out:
DesignMatrix with shape (5, 4)
  Intercept  center(x1)  standardize(x1)  scale(x2)
          1    -0.01825         -0.19319   -0.07965
          1     0.14813          1.56828   -0.97279
          1     0.03570          0.37799    0.97458
          1    -0.02218         -0.23480    1.28282
          1    -0.14341         -1.51828   -1.20495
  Terms:
    'Intercept' (column 0)
    'center(x1)' (column 1)
    'standardize(x1)' (column 2)
    'scale(x2)' (column 3)

변수 보호

함수를 사용한 변수 변환 이외에도 모형 정의 문자열 자체내에 연산기호를 넣어 연산한 값을 만드는 것도 가능하다. 이 때에는 모형정의 연산자와 혼동되지 않도록 I() 연산자를 추가해야 한다.

In:
dmatrix("I(x1 + x2)")
Out:
DesignMatrix with shape (5, 2)
  Intercept  I(x1 + x2)
          1    17.00775
          1    15.09106
          1    19.52049
          1    20.18151
          1    14.25807
  Terms:
    'Intercept' (column 0)
    'I(x1 + x2)' (column 1)

이 값을 다음 식과 비교하면 I()의 기능을 확실히 알 수 있다.

In:
dmatrix("x1 + x2")
Out:
DesignMatrix with shape (5, 3)
  Intercept        x1       x2
          1  10.54881  6.45894
          1  10.71519  4.37587
          1  10.60276  8.91773
          1  10.54488  9.63663
          1  10.42365  3.83442
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)
    'x2' (column 2)

다항선형회귀

I() 연산자를 활용하면 다항선형회귀(polynomial regression)도 할 수 있다.

In:
dmatrix("x1 + I(x1**2) + I(x1**3) + I(x1**4)")
Out:
DesignMatrix with shape (5, 5)
  Intercept        x1  I(x1 ** 2)  I(x1 ** 3)   I(x1 ** 4)
          1  10.54881   111.27747  1173.84524  12382.67452
          1  10.71519   114.81528  1230.26750  13182.54925
          1  10.60276   112.41859  1191.94772  12637.93965
          1  10.54488   111.19456  1172.53366  12364.23047
          1  10.42365   108.65258  1132.55698  11805.38301
  Terms:
    'Intercept' (column 0)
    'x1' (column 1)
    'I(x1 ** 2)' (column 2)
    'I(x1 ** 3)' (column 3)
    'I(x1 ** 4)' (column 4)

카테고리 변수 인코딩

데이터로 문자열이 오는 경우에는 카테고리 값으로 인정하여 One-Hot-Encoding 방식의 인코딩을 하게 된다.

In:
df3 = pd.DataFrame(["A", "B", "A", "C"], columns=["x3"])
df3
Out:
x3
0 A
1 B
2 A
3 C
In:
dmatrix("x3 - 1", df3)
Out:
DesignMatrix with shape (4, 3)
  x3[A]  x3[B]  x3[C]
      1      0      0
      0      1      0
      1      0      0
      0      0      1
  Terms:
    'x3' (columns 0:3)

카테고리 값이지만 정수 등으로 표시된 경우에는 C() 연산자를 이용하여 카테고리 값임을 지정할 수 있다.

In:
df4 = pd.DataFrame([1, 1, 4, 2], columns=["x4"])
df4
Out:
x4
0 1
1 1
2 4
3 2
In:
dmatrix("C(x4) + 0", df4)
Out:
DesignMatrix with shape (4, 3)
  C(x4)[1]  C(x4)[2]  C(x4)[4]
         1         0         0
         1         0         0
         0         0         1
         0         1         0
  Terms:
    'C(x4)' (columns 0:3)
In:
contrast = [[1, 1, 0], [0, 2, 2], [0, 0, 3]]
np.array(dmatrix("C(x4, contrast) + 0", df4))
Out:
array([[ 1.,  1.,  0.],
       [ 1.,  1.,  0.],
       [ 0.,  0.,  3.],
       [ 0.,  2.,  2.]])
In:
contrast = [[1, 0], [0, 1], [0, 0]]
np.array(dmatrix("C(x4, contrast) + 0", df4))
Out:
array([[ 1.,  0.],
       [ 1.,  0.],
       [ 0.,  0.],
       [ 0.,  1.]])
In:
df5 = pd.DataFrame([1, 2, 3, 1], columns=["x5"])
df345 = pd.concat([df3, df4, df5], axis=1)
df345
Out:
x3 x4 x5
0 A 1 1
1 B 1 2
2 A 4 3
3 C 2 1
In:
from patsy import Treatment

contrast4 = Treatment(reference=0).code_with_intercept(levels=[1, 2, 4])
contrast4
Out:
ContrastMatrix(array([[ 1.,  0.,  0.],
                      [ 0.,  1.,  0.],
                      [ 0.,  0.,  1.]]), ['[1]', '[2]', '[4]'])
In:
contrast5 = Treatment(reference=0).code_with_intercept(levels=[1, 2, 3])
contrast5
Out:
ContrastMatrix(array([[ 1.,  0.,  0.],
                      [ 0.,  1.,  0.],
                      [ 0.,  0.,  1.]]), ['[1]', '[2]', '[3]'])
In:
dm = dmatrix("C(x3, Treatment) + C(x4, contrast4) + C(x5, contrast5) + 0", df345)
pd.DataFrame(dm, columns=dm.design_info.column_names)
Out:
C(x3, Treatment)[A] C(x3, Treatment)[B] C(x3, Treatment)[C] C(x4, contrast4)[1] C(x4, contrast4)[2] C(x4, contrast4)[4] C(x5, contrast5)[1] C(x5, contrast5)[2] C(x5, contrast5)[3]
0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0
1 0.0 1.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0
2 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0
3 0.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0 0.0

OLS.from_formula 메서드

선형회귀분석을 위한 OLS 클래스에는 모형 정의 문자열을 사용할 수 있는 from_formula라는 메서드가 있다. 이 메서드를 쓰면 사용자가 데이터 행렬을 직접 정의하지 않고 모형 정의 문자열만으로 선형회귀모형을 만드는 것이 가능하다.

예를 들어 df가 다음과 같은 경우,

In:
df
Out:
x1 x2 y
0 10.548814 6.458941 23.610739
1 10.715189 4.375872 20.921207
2 10.602763 8.917730 29.199261
3 10.544883 9.636628 29.939813
4 10.423655 3.834415 18.536348

다음 두 모형은 동일하다.

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

# 모형 정의 문자열을 사용하는 경우
model2 = sm.OLS.from_formula("y ~ x1 + x2", data=df)
In:
print(model1.fit().summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.998
Model:                            OLS   Adj. R-squared:                  0.995
Method:                 Least Squares   F-statistic:                     406.1
Date:                Sat, 10 Mar 2018   Prob (F-statistic):            0.00246
Time:                        14:53:07   Log-Likelihood:                0.41801
No. Observations:                   5   AIC:                             5.164
Df Residuals:                       2   BIC:                             3.992
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        -41.9480     17.631     -2.379      0.140    -117.809      33.913
x1             5.0904      1.671      3.046      0.093      -2.100      12.281
x2             1.8960      0.068     28.015      0.001       1.605       2.187
==============================================================================
Omnibus:                          nan   Durbin-Watson:                   1.540
Prob(Omnibus):                    nan   Jarque-Bera (JB):                0.451
Skew:                          -0.636   Prob(JB):                        0.798
Kurtosis:                       2.259   Cond. No.                     1.42e+03
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.42e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
/home/dockeruser/anaconda3/lib/python3.6/site-packages/statsmodels/stats/stattools.py:72: ValueWarning: omni_normtest is not valid with less than 8 observations; 5 samples were given.
  "samples were given." % int(n), ValueWarning)
In:
print(model2.fit().summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.998
Model:                            OLS   Adj. R-squared:                  0.995
Method:                 Least Squares   F-statistic:                     406.1
Date:                Sat, 10 Mar 2018   Prob (F-statistic):            0.00246
Time:                        14:53:07   Log-Likelihood:                0.41801
No. Observations:                   5   AIC:                             5.164
Df Residuals:                       2   BIC:                             3.992
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept    -41.9480     17.631     -2.379      0.140    -117.809      33.913
x1             5.0904      1.671      3.046      0.093      -2.100      12.281
x2             1.8960      0.068     28.015      0.001       1.605       2.187
==============================================================================
Omnibus:                          nan   Durbin-Watson:                   1.540
Prob(Omnibus):                    nan   Jarque-Bera (JB):                0.451
Skew:                          -0.636   Prob(JB):                        0.798
Kurtosis:                       2.259   Cond. No.                     1.42e+03
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.42e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
/home/dockeruser/anaconda3/lib/python3.6/site-packages/statsmodels/stats/stattools.py:72: ValueWarning: omni_normtest is not valid with less than 8 observations; 5 samples were given.
  "samples were given." % int(n), ValueWarning)

질문/덧글

dmatrix의 사용 이유는 데이터를 탐색하기 위해서 인가요? poio*** 2017년 3월 4일 10:32 오후

dmatrix의 결과물을 모델을 만들거나 계산을 하는데 사용하지는 못하는 것 같습니다.
그냥 간편하게 다항 선형 회귀나 카테고리 변수를 탐색하려고 사용하는게 맞는건가요?

답변: dmatrix의 사용 이유는 데이터를 탐색하기 위해서 인가요? 관리자 2017년 3월 12일 11:38 오전

입력 변수를 인코딩하는데 사용합니다.