Python과 Numpy를 사용하여 r-squared를 어떻게 계산합니까?


92

저는 Python과 Numpy를 사용하여 임의 차수의 최적 다항식을 계산하고 있습니다. x 값, y 값 및 내가 맞추려는 다항식의 정도 (선형, 2 차 등)의 목록을 전달합니다.

이 정도는 효과가 있지만 r (상관 계수)과 r 제곱 (결정 계수)도 계산하고 싶습니다. 내 결과를 Excel의 최적 추세선 기능 및 계산하는 r 제곱 값과 비교하고 있습니다. 이것을 사용하여 선형 최적에 대해 r- 제곱을 올바르게 계산하고 있음을 알고 있습니다 (차수가 1과 같음). 그러나 내 함수는 차수가 1보다 큰 다항식에서는 작동하지 않습니다.

Excel은이를 수행 할 수 있습니다. Numpy를 사용하여 고차 다항식에 대한 r 제곱을 어떻게 계산합니까?

내 기능은 다음과 같습니다.

import numpy

# Polynomial Regression
def polyfit(x, y, degree):
    results = {}

    coeffs = numpy.polyfit(x, y, degree)
     # Polynomial Coefficients
    results['polynomial'] = coeffs.tolist()

    correlation = numpy.corrcoef(x, y)[0,1]

     # r
    results['correlation'] = correlation
     # r-squared
    results['determination'] = correlation**2

    return results

1
참고 : 차수는 계수 계산에만 사용합니다.
Nick Dandoulakis

타이 독이 맞습니다. x와 y의 상관 관계를 계산하고 y = p_0 + p_1 * x에 대한 r 제곱을 계산합니다. 작동해야 할 코드에 대해서는 아래 내 대답을 참조하십시오. 물어봐도 괜찮다면 궁극적 인 목표는 무엇입니까? 모델 선택을하고 있습니까 (사용할 정도 선택)? 또는 다른 것?
leif

@leif-요청은 "Excel처럼 실행"으로 요약됩니다. 이 답변에서 사용자가 비선형 최적 곡선을 사용할 때 r 제곱 값을 너무 많이 읽을 수 있다는 느낌을 받고 있습니다. 그럼에도 불구하고 저는 수학 마법사가 아니며 이것이 요청 된 기능입니다.
Travis Beale

답변:


62

로부터 numpy.polyfit의 문서, 그것은 선형 회귀를 피팅 있습니다. 특히 차수가 'd'인 numpy.polyfit은 평균 함수가있는 선형 회귀에 적합합니다.

E (y | x) = p_d * x ** d + p_ {d-1} * x ** (d-1) + ... + p_1 * x + p_0

따라서 해당 피팅에 대한 R- 제곱을 계산하면됩니다. 선형 회귀 에 대한 위키피디아 페이지 는 자세한 내용을 제공합니다. 몇 가지 방법으로 계산할 수있는 R ^ 2에 관심이 있습니다. 가장 쉬운 방법은

SST = Sum(i=1..n) (y_i - y_bar)^2
SSReg = Sum(i=1..n) (y_ihat - y_bar)^2
Rsquared = SSReg/SST

여기서는 'y_bar'를 y의 평균으로 사용하고 'y_ihat'을 각 점의 적합 값으로 사용합니다.

나는 numpy에별로 익숙하지 않습니다 (보통 R에서 일합니다), 아마도 R 제곱을 계산하는 더 깔끔한 방법이 있지만 다음은 정확해야합니다

import numpy

# Polynomial Regression
def polyfit(x, y, degree):
    results = {}

    coeffs = numpy.polyfit(x, y, degree)

     # Polynomial Coefficients
    results['polynomial'] = coeffs.tolist()

    # r-squared
    p = numpy.poly1d(coeffs)
    # fit values, and mean
    yhat = p(x)                         # or [p(z) for z in x]
    ybar = numpy.sum(y)/len(y)          # or sum(y)/len(y)
    ssreg = numpy.sum((yhat-ybar)**2)   # or sum([ (yihat - ybar)**2 for yihat in yhat])
    sstot = numpy.sum((y - ybar)**2)    # or sum([ (yi - ybar)**2 for yi in y])
    results['determination'] = ssreg / sstot

    return results

5
목록 이해 대신 numpy 배열 함수를 사용하는 것이 훨씬 빠르다는 점을 지적하고 싶습니다. 예를 들어 numpy.sum ((yi-ybar) ** 2) 읽기가 더 쉽습니다
Josef

17
위키 페이지 en.wikipedia.org/wiki/Coefficient_of_determination 에 따르면 R ^ 2의 가장 일반적인 정의는 R^2 = 1 - SS_err/SS_tot이며 R^2 = SS_reg/SS_tot특별한 경우입니다.
LWZ 2013

137

매우 늦게 답변했지만 누군가 이에 대한 준비 기능이 필요한 경우를 대비하여 :

scipy.stats.linregress

slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(x, y)

@Adam Marples의 답변에서와 같이.


상관 계수 로 분석 한 다음 더 큰 작업 인 회귀 를 수행하는 것이 합리적 입니다.
象嘉道

19
이 응답은 간단한 다항식 회귀 인 선형 회귀 작동
tashuhka

8
주의 : 여기서 r_value는 R- 제곱이 아니라 Pearson의 상관 계수입니다. r_squared = r_value ** 2
Vladimir Lukin

52

yanl (아직 다른 라이브러리)에서 sklearn.metrics갖는 r2_score기능;

from sklearn.metrics import r2_score

coefficient_of_dermination = r2_score(y, p(x))

1
(주의 : "기본값은 'variance_weighted'에 해당합니다.이 동작은 버전 0.17부터 사용되지 않으며 0.19부터 'uniform_average'로 변경 될 것입니다.")
Franck Dernoncourt 2017 년

4
sklearn의 r2_score는 일반적인 경우가 아닌 음수 값일 수 있습니다.
Qinqing Liu

1
r2_score([1,2,3],[4,5,7])= -16?
cz

22

나는 이것을 성공적으로 사용하고 있는데, 여기서 x와 y는 배열과 같습니다.

def rsquared(x, y):
    """ Return R^2 where x and y are array-like."""

    slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(x, y)
    return r_value**2

20

나는 원래 추천 목적으로 아래 벤치 마크를 게시했으며 numpy.corrcoef, 어리석게도 원래 질문이 이미 사용 corrcoef하고 실제로 고차 다항식 적합에 대해 묻는다는 사실을 깨닫지 못했습니다 . 통계 모델을 사용하여 다항식 r- 제곱 질문에 실제 솔루션을 추가했으며 주제를 벗어 났지만 잠재적으로 누군가에게 유용 할 수있는 원래 벤치 마크를 남겼습니다.


statsmodelsr^2다항식 피팅을 직접 계산할 수있는 기능이 있습니다. 여기에 두 가지 방법이 있습니다.

import statsmodels.api as sm
import statsmodels.formula.api as smf

# Construct the columns for the different powers of x
def get_r2_statsmodels(x, y, k=1):
    xpoly = np.column_stack([x**i for i in range(k+1)])    
    return sm.OLS(y, xpoly).fit().rsquared

# Use the formula API and construct a formula describing the polynomial
def get_r2_statsmodels_formula(x, y, k=1):
    formula = 'y ~ 1 + ' + ' + '.join('I(x**{})'.format(i) for i in range(1, k+1))
    data = {'x': x, 'y': y}
    return smf.ols(formula, data).fit().rsquared # or rsquared_adj

를 더 활용하려면 statsmodelsJupyter / IPython 노트북에서 풍부한 HTML 테이블로 인쇄하거나 표시 할 수있는 적합 모델 요약도 살펴 봐야합니다. 결과 개체는뿐만 아니라 많은 유용한 통계 메트릭에 대한 액세스를 제공합니다 rsquared.

model = sm.OLS(y, xpoly)
results = model.fit()
results.summary()

아래는 다양한 선형 회귀 r ^ 2 방법을 벤치마킹 한 원래 답변입니다.

corrcoef 질문에 사용 된 함수는 상관 계수를 계산하고 r, 단지 하나의 선형 회귀를 위해, 그래서의 문제 해결하지 않는 r^2고차 다항식을 맞을. 그러나 그만한 가치는 선형 회귀의 경우 실제로 가장 빠르고 직접적인 계산 방법이라는 것을 알게되었습니다 r.

def get_r2_numpy_corrcoef(x, y):
    return np.corrcoef(x, y)[0, 1]**2

다음은 1000 개의 임의 (x, y) 포인트에 대해 여러 방법을 비교 한 결과입니다.

  • 순수 Python (직접 r계산)
    • 1000 루프, 최고 3 : 루프 당 1.59ms
  • Numpy polyfit (n 차 다항식 피팅에 적용 가능)
    • 1000 루프, 최고 3 : 326 µs 루프 당
  • Numpy 매뉴얼 (직접 r계산)
    • 10000 루프, 최고 3 : 루프 당 62.1 µs
  • Numpy corrcoef (직접 r계산)
    • 10000 루프, 최고 3 : 루프 당 56.6 µs
  • Scipy ( r출력으로 선형 회귀 )
    • 1000 루프, 최고 3 : 676 µs / 루프
  • Statsmodels (n 차 다항식 및 기타 여러 피팅을 수행 할 수 있음)
    • 1000 루프, 최고 3 : 422 µs 루프 당

corrcoef 방법은 numpy 방법을 사용하여 r ^ 2를 "수동"으로 계산하는 것보다 좁습니다. polyfit 방법보다 5 배 이상 빠르며 scipy.linregress보다 12 배 빠릅니다. numpy가 당신을 위해하는 일을 강화하기 위해 순수한 파이썬보다 28 배 빠릅니다. 나는 numba 및 pypy와 같은 것에 정통하지 않으므로 다른 누군가가 그 간격을 채워야 할 것입니다.하지만 이것은 단순한 선형 회귀 corrcoef를 계산하는 데 가장 좋은 도구 라고 생각합니다 r.

다음은 내 벤치마킹 코드입니다. Jupyter Notebook에서 복사하여 붙여 넣었습니다 (IPython Notebook이라고 부르기 어렵습니다 ...). 도중에 문제가 발생하면 사과드립니다. % timeit 매직 명령에는 IPython이 필요합니다.

import numpy as np
from scipy import stats
import statsmodels.api as sm
import math

n=1000
x = np.random.rand(1000)*10
x.sort()
y = 10 * x + (5+np.random.randn(1000)*10-5)

x_list = list(x)
y_list = list(y)

def get_r2_numpy(x, y):
    slope, intercept = np.polyfit(x, y, 1)
    r_squared = 1 - (sum((y - (slope * x + intercept))**2) / ((len(y) - 1) * np.var(y, ddof=1)))
    return r_squared
    
def get_r2_scipy(x, y):
    _, _, r_value, _, _ = stats.linregress(x, y)
    return r_value**2
    
def get_r2_statsmodels(x, y):
    return sm.OLS(y, sm.add_constant(x)).fit().rsquared
    
def get_r2_python(x_list, y_list):
    n = len(x_list)
    x_bar = sum(x_list)/n
    y_bar = sum(y_list)/n
    x_std = math.sqrt(sum([(xi-x_bar)**2 for xi in x_list])/(n-1))
    y_std = math.sqrt(sum([(yi-y_bar)**2 for yi in y_list])/(n-1))
    zx = [(xi-x_bar)/x_std for xi in x_list]
    zy = [(yi-y_bar)/y_std for yi in y_list]
    r = sum(zxi*zyi for zxi, zyi in zip(zx, zy))/(n-1)
    return r**2
    
def get_r2_numpy_manual(x, y):
    zx = (x-np.mean(x))/np.std(x, ddof=1)
    zy = (y-np.mean(y))/np.std(y, ddof=1)
    r = np.sum(zx*zy)/(len(x)-1)
    return r**2
    
def get_r2_numpy_corrcoef(x, y):
    return np.corrcoef(x, y)[0, 1]**2
    
print('Python')
%timeit get_r2_python(x_list, y_list)
print('Numpy polyfit')
%timeit get_r2_numpy(x, y)
print('Numpy Manual')
%timeit get_r2_numpy_manual(x, y)
print('Numpy corrcoef')
%timeit get_r2_numpy_corrcoef(x, y)
print('Scipy')
%timeit get_r2_scipy(x, y)
print('Statsmodels')
%timeit get_r2_statsmodels(x, y)

1
기울기를 맞추는 3 가지 방법과 기울기를 맞추지 않은 3 가지 방법과 회귀 분석을 비교하고 있습니다.
Josef

그래, 나는 그 정도를 알고 있었지만 이제는 원래 질문을 읽지 않고 이미 corrcoef를 사용하고 고차 다항식에 대해 r ^ 2를 구체적으로 다루고 있다는 사실에 어리석은 느낌이 들었습니다 ... 이제 벤치 마크를 게시하는 것이 어리석은 느낌입니다. 다른 목적을위한 것이 었습니다. 죄송합니다 ...
flutefreak7

1
statsmodels나는를 사용하여 원래 질문에 대한 해결책으로 내 대답을 업데이트 했으며, 흥미롭지 만 주제를 벗어난 정보로 유지 한 선형 회귀 r ^ 2 방법의 불필요한 벤치마킹에 대해 사과했습니다.
flutefreak7

scipy의 linregress가보다 일반적인 작업을 수행하는 statsmodels보다 느릴 것으로 예상하지 않았기 때문에 벤치 마크가 여전히 흥미 롭습니다.
Josef

1
참고 np.column_stack([x**i for i in range(k+1)])로 NumPy와의 벡터화 할 수 있습니다 x[:,None]**np.arange(k+1)또는 열에서 반대 순서가 NumPy와의 밴더 기능을 사용하여.
Josef

5

R- 제곱은 선형 회귀에만 적용되는 통계입니다.

기본적으로 선형 회귀로 설명 할 수있는 데이터의 변동 정도를 측정합니다.

따라서 평균에서 각 결과 변수의 총 제곱 편차 인 "총 제곱합"을 계산합니다. . .

\ sum_ {i} (y_ {i}-y_bar) ^ 2

여기서 y_bar는 y의 평균입니다.

그런 다음 FITTED 값이 평균과 얼마나 다른지 "회귀 제곱합"을 계산합니다.

\ sum_ {i} (yHat_ {i}-y_bar) ^ 2

그리고 그 둘의 비율을 찾으십시오.

이제 다항식 피팅을 위해해야 ​​할 일은 해당 모델의 y_hat을 연결하는 것뿐입니다.하지만 r- 제곱이라고 부르는 것은 정확하지 않습니다.

여기 에 내가 찾은 링크가 있습니다.


이것이 내 문제의 근원 인 것 같습니다. Excel은 다항식 적합과 선형 회귀에 대해 다른 r- 제곱 값을 어떻게 얻습니까?
Travis Beale

1
선형 회귀에서 적합하고 다항식 모델에서 적합을 제공합니까? 두 개의 데이터 배열에서 rsq를 계산하고 선형 모델에서 적합하다고 가정합니다. 당신은 무엇을 제공하고 있습니까? Excel에서 '최적의 추세선'명령은 무엇입니까?
Baltimark

Excel의 그래프 기능의 일부입니다. 일부 데이터를 플로팅하고 마우스 오른쪽 버튼으로 클릭 한 다음 여러 다른 유형의 추세선 중에서 선택할 수 있습니다. 선의 방정식과 각 유형에 대한 r- 제곱 값을 볼 수있는 옵션이 있습니다. r- 제곱 값도 각 유형마다 다릅니다.
Travis Beale

@Travis Beale-시도하는 각각의 다른 평균 함수에 대해 다른 r- 제곱을 얻을 것입니다 (두 모델이 중첩되고 더 큰 모델의 추가 계수가 모두 0으로 작동하지 않는 한). 물론 Excel은 다른 r- 제곱 값을 제공합니다. @Baltimark-이것은 선형 회귀이므로 r 제곱입니다.
leif


5

다음은 Python 및 Numpy로 가중치가 적용된 r- 제곱 을 계산하는 함수입니다 (대부분의 코드는 sklearn에서 가져옴).

from __future__ import division 
import numpy as np

def compute_r2_weighted(y_true, y_pred, weight):
    sse = (weight * (y_true - y_pred) ** 2).sum(axis=0, dtype=np.float64)
    tse = (weight * (y_true - np.average(
        y_true, axis=0, weights=weight)) ** 2).sum(axis=0, dtype=np.float64)
    r2_score = 1 - (sse / tse)
    return r2_score, sse, tse

예:

from __future__ import print_function, division 
import sklearn.metrics 

def compute_r2_weighted(y_true, y_pred, weight):
    sse = (weight * (y_true - y_pred) ** 2).sum(axis=0, dtype=np.float64)
    tse = (weight * (y_true - np.average(
        y_true, axis=0, weights=weight)) ** 2).sum(axis=0, dtype=np.float64)
    r2_score = 1 - (sse / tse)
    return r2_score, sse, tse    

def compute_r2(y_true, y_predicted):
    sse = sum((y_true - y_predicted)**2)
    tse = (len(y_true) - 1) * np.var(y_true, ddof=1)
    r2_score = 1 - (sse / tse)
    return r2_score, sse, tse

def main():
    '''
    Demonstrate the use of compute_r2_weighted() and checks the results against sklearn
    '''        
    y_true = [3, -0.5, 2, 7]
    y_pred = [2.5, 0.0, 2, 8]
    weight = [1, 5, 1, 2]
    r2_score = sklearn.metrics.r2_score(y_true, y_pred)
    print('r2_score: {0}'.format(r2_score))  
    r2_score,_,_ = compute_r2(np.array(y_true), np.array(y_pred))
    print('r2_score: {0}'.format(r2_score))
    r2_score = sklearn.metrics.r2_score(y_true, y_pred,weight)
    print('r2_score weighted: {0}'.format(r2_score))
    r2_score,_,_ = compute_r2_weighted(np.array(y_true), np.array(y_pred), np.array(weight))
    print('r2_score weighted: {0}'.format(r2_score))

if __name__ == "__main__":
    main()
    #cProfile.run('main()') # if you want to do some profiling

출력 :

r2_score: 0.9486081370449679
r2_score: 0.9486081370449679
r2_score weighted: 0.9573170731707317
r2_score weighted: 0.9573170731707317

이것은 공식 ( mirror )에 해당합니다 .

여기에 이미지 설명 입력

f_i는 적합치로부터 예측 된 값이고, y_ {av}는 관측 된 데이터의 평균입니다. y_i는 관측 된 데이터 값입니다. w_i는 각 데이터 포인트에 적용되는 가중치이며 일반적으로 w_i = 1입니다. SSE는 오류로 인한 제곱합이고 SST는 총 제곱합입니다.


관심이 있다면 R의 코드 : https://gist.github.com/dhimmel/588d64a73fa4fef02c8f ( mirror )


2

다음은 y와 y_hat이 pandas 시리즈라고 가정하고 실제 값과 예측 값에서 R ^ 2를 계산하는 매우 간단한 파이썬 함수입니다.

def r_squared(y, y_hat):
    y_bar = y.mean()
    ss_tot = ((y-y_bar)**2).sum()
    ss_res = ((y-y_hat)**2).sum()
    return 1 - (ss_res/ss_tot)

0

scipy.stats.linregress 소스에서. 평균 제곱합 방법을 사용합니다.

import numpy as np

x = np.array(x)
y = np.array(y)

# average sum of squares:
ssxm, ssxym, ssyxm, ssym = np.cov(x, y, bias=1).flat

r_num = ssxym
r_den = np.sqrt(ssxm * ssym)
r = r_num / r_den

if r_den == 0.0:
    r = 0.0
else:
    r = r_num / r_den

    if r > 1.0:
        r = 1.0
    elif r < -1.0:
        r = -1.0

0

이 코드를 직접 실행하면 다항식을 찾을 수 있으며 더 많은 설명이 필요한 경우 아래에 주석을 달 수 있는 R 값찾을 수 있습니다 .

from scipy.stats import linregress
import numpy as np

x = np.array([1,2,3,4,5,6])
y = np.array([2,3,5,6,7,8])

p3 = np.polyfit(x,y,3) # 3rd degree polynomial, you can change it to any degree you want
xp = np.linspace(1,6,6)  # 6 means the length of the line
poly_arr = np.polyval(p3,xp)

poly_list = [round(num, 3) for num in list(poly_arr)]
slope, intercept, r_value, p_value, std_err = linregress(x, poly_list)
print(r_value**2)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.