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

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 [1]:
from patsy import dmatrix

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

In [2]:
np.random.seed(0)
x1 = np.random.rand(5) + 10
x2 = np.random.rand(5) * 10
y = x1 + 2 * x2 + np.random.randn(5)
df = pd.DataFrame(np.array([x1, x2, y]).T, columns=["x1", "x2", "y"])
In [3]:
df.tail()
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

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

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

In [4]:
dmatrix("x1", df)
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 [5]:
dmatrix("x1 - 1", df)
Out:
DesignMatrix with shape (5, 1)
        x1
  10.54881
  10.71519
  10.60276
  10.54488
  10.42365
  Terms:
    'x1' (column 0)
In [6]:
dmatrix("x1 + 0", df)
Out:
DesignMatrix with shape (5, 1)
        x1
  10.54881
  10.71519
  10.60276
  10.54488
  10.42365
  Terms:
    'x1' (column 0)

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

In [7]:
dmatrix("x1 + x2", df)
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 [8]:
dmatrix("x1 + x2 - 1", df)
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 [9]:
dmatrix("x1 + x2 + x1:x2 - 1", df)
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 [10]:
dmatrix("x1 * x2 - 1", df)
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 [11]:
dmatrix("x1 / x2 - 1", df)
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 [12]:
dmatrix("x1 + np.log(np.abs(x2))", df)
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 [13]:
def doubleit(x):
    return 2 * x

dmatrix("doubleit(x1)", df)
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 [14]:
dmatrix("center(x1) + standardize(x1) + scale(x2)", df)
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 [15]:
dmatrix("I(x1 + x2)", df)
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 [16]:
dmatrix("x1 + x2", df)
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 [17]:
dmatrix("x1 + I(x1**2) + I(x1**3) + I(x1**4)", df)
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)

카테고리 변수 인코딩

데이터로 문자열이 오는 경우에는 카테고리 값으로 취급한다.

회귀 분석에서 카테고리값을 사용하려면 실수값으로 바꾸어야 한다. 이 때 두가지 방식을 사용할 수 있다.

  1. Full-Rank 인코딩
  2. Reduced-Rank 인코딩

Full-Rank 인코딩은 One-Hot-Encoding 방식의 인코딩이다. 즉 특정 카테고리에 속해있다는 것을 나타내는 실수 변수를 카테고리마다 만들어 특정 카테고리 값은 그 실수 변수만 1, 나머지 실수 변수는 0으로 인코딩한다. 즉, "A", "B", "C" 세가지의 카테고리를 가지는 1차원 변수가 존재한다면 이를 x1, x2, x3의 3차원 실수 변수로 확장한다. 이렇게 0 또는 1의 값만 가지는 확장된 실수 변수를 더미 변수(dummy variable)이라고 한다. patsy에서 Full-Rank 인코딩을 사용하려면 formula에 " - 1" 또는 " + 0"을 붙여야 한다.

$$ x="A" \;\; \rightarrow \;\; x=(1, 0, 0) $$$$ x="B" \;\; \rightarrow \;\; x=(0, 1, 0) $$$$ x="C" \;\; \rightarrow \;\; x=(0, 0, 1) $$

Reduced-Rank 인코딩은 특정한 카테고리를 reference 카테고리로 지정한다. 카테고리의 갯수 만큼의 차원으로 더미 변수 확장하는 것은 마찬가지이나 reference 카테고리에 해당하는 더미 변수의 값은 항상 1이다. 즉 bias(intercept)의 역할을 한다. 나머지 카테고리에 대해서는 Full-Rank 인코딩과 마찬가지로 그 카테고리에 속한다는 것을 나타내는 실수 변수를 추가한다. 따라서 만약 "A" 카테고리를 reference 카테고리로 지정한다면 인코딩 결과는 다음과 같다.

$$ x="A" \;\; \rightarrow \;\; x=(1, 0, 0) $$$$ x="B" \;\; \rightarrow \;\; x=(1, 1, 0) $$$$ x="C" \;\; \rightarrow \;\; x=(1, 0, 1) $$
In [18]:
df2 = pd.DataFrame(["A", "B", "C", "D"], columns=["x3"])
df2
Out:
x3
0 A
1 B
2 C
3 D

다음은 Full-Rank 인코딩 결과이다.

In [19]:
dmatrix("x3 - 1", df2)
Out:
DesignMatrix with shape (4, 4)
  x3[A]  x3[B]  x3[C]  x3[D]
      1      0      0      0
      0      1      0      0
      0      0      1      0
      0      0      0      1
  Terms:
    'x3' (columns 0:4)

만약 " - 1" 또는 " + 0"을 붙이지 않으면 Reduced-Rank 인코딩을 한다.

In [20]:
dmatrix("x3", df2)
Out:
DesignMatrix with shape (4, 4)
  Intercept  x3[T.B]  x3[T.C]  x3[T.D]
          1        0        0        0
          1        1        0        0
          1        0        1        0
          1        0        0        1
  Terms:
    'Intercept' (column 0)
    'x3' (columns 1:4)

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

In [21]:
df3 = pd.DataFrame([1, 2, 3, 4], columns=["x4"])
df3
Out:
x4
0 1
1 2
2 3
3 4
In [22]:
dmatrix("C(x4) + 0", df3)
Out:
DesignMatrix with shape (4, 4)
  C(x4)[1]  C(x4)[2]  C(x4)[3]  C(x4)[4]
         1         0         0         0
         0         1         0         0
         0         0         1         0
         0         0         0         1
  Terms:
    'C(x4)' (columns 0:4)

만약 특정한 값으로 인코딩을 하고 싶다면 ContrastMatrix를 사용한다. 이 행렬은 각 클래스가 인코딩 될 결과 벡터를 미리 정의한 행렬이다.

In [23]:
from patsy import ContrastMatrix
contrast = ContrastMatrix([[0, 0], [0, 1], [1, 0], [1, 1]], [":1", ":2"])
dmatrix("C(x4, contrast) + 0", df3)
Out:
DesignMatrix with shape (4, 2)
  C(x4, contrast):1  C(x4, contrast):2
                  0                  0
                  0                  1
                  1                  0
                  1                  1
  Terms:
    'C(x4, contrast)' (columns 0:2)

OLS.from_formula 메서드

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

In [24]:
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"])

다음 두가지 방법으로 만든 모형은 동일하다.

In [25]:
# 직접 데이터 행렬을 만드는 경우
dfy = df4.iloc[:, -1]
dfX = sm.add_constant(df4.iloc[:, :-1])
model1 = sm.OLS(dfy, dfX)
In [26]:
print(model1.fit().summary())
                            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:                Wed, 27 Jun 2018   Prob (F-statistic):           2.75e-13
Time:                        09:53: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>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          1.4226     10.140      0.140      0.890     -19.971      22.816
x1             0.8114      0.977      0.831      0.418      -1.249       2.872
x2             2.0367      0.100     20.305      0.000       1.825       2.248
==============================================================================
Omnibus:                        1.081   Durbin-Watson:                   1.896
Prob(Omnibus):                  0.583   Jarque-Bera (JB):                0.949
Skew:                           0.470   Prob(JB):                        0.622
Kurtosis:                       2.494   Cond. No.                         494.
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [27]:
# 모형 정의 문자열을 사용하는 경우
model2 = sm.OLS.from_formula("y ~ x1 + x2", data=df4)
In [28]:
print(model2.fit().summary())
                            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:                Wed, 27 Jun 2018   Prob (F-statistic):           2.75e-13
Time:                        09:53: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>|t|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept      1.4226     10.140      0.140      0.890     -19.971      22.816
x1             0.8114      0.977      0.831      0.418      -1.249       2.872
x2             2.0367      0.100     20.305      0.000       1.825       2.248
==============================================================================
Omnibus:                        1.081   Durbin-Watson:                   1.896
Prob(Omnibus):                  0.583   Jarque-Bera (JB):                0.949
Skew:                           0.470   Prob(JB):                        0.622
Kurtosis:                       2.494   Cond. No.                         494.
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

질문/덧글

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

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

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

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