PythonとNumpyを使用してr-2乗を計算するにはどうすればよいですか?


90

PythonとNumpyを使用して、任意の次数の最適な多項式を計算しています。x値、y値、および適合させたい多項式の次数(線形、二次など)のリストを渡します。

これで十分に機能しますが、r(相関係数)とr-2乗(決定係数)も計算したいと思います。私の結果をExcelの最適な近似曲線機能と計算したr二乗値と比較しています。これを使用して、線形最良適合(次数= 1)に対してr-2乗を正しく計算していることがわかります。ただし、私の関数は次数が1より大きい多項式では機能しません。

Excelはこれを行うことができます。Numpyを使用して高次多項式のr-2乗を計算するにはどうすればよいですか?

これが私の機能です:

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
注:次数は係数の計算にのみ使用します。
ニックダンドゥラキス、2009年

tydokは正しいです。y = p_0 + p_1 * xについて、xとyの相関、およびr二乗を計算しています。動作するはずのコードについては、以下の私の回答を参照してください。よろしければ、最終的な目標は何ですか。モデルの選択を行っていますか(使用する程度を選択していますか)?または、他の何か?
leif 2009年

@leif-要求は、「Excelのように実行する」に要約されます。これらの回答から、非線形のベストフィットカーブを使用すると、ユーザーがr-2乗値を読みすぎる可能性があるという印象を受けます。それでも、私は数学のウィザードではありません。これは要求された機能です。
Travis Beale

回答:


60

numpy.polyfitのドキュメントから、線形回帰に適合しています。具体的には、次数が 'd'のnumpy.polyfitは、平均関数を使用して線形回帰に適合します。

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

したがって、そのフィットのR-2乗を計算する必要があるだけです。詳細については、線形回帰に関するウィキペディアのページをご覧ください。あなたはいくつかの方法で計算できる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の平均に「y_bar」を使用し、「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
wikiページのによるとen.wikipedia.org/wiki/Coefficient_of_determination、R ^ 2の最も一般的な定義があるR^2 = 1 - SS_err/SS_totと、R^2 = SS_reg/SS_tot単に特殊なケースであること。
LWZ 2013

134

非常に遅い返信ですが、誰かがこのための準備ができた機能を必要とする場合に備えて:

scipy.stats.linregress

すなわち

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

@Adam Marplesの答えのように。


相関係数で分析してから、より大きな仕事である回帰を行うのが妥当です。
象嘉道

18
この応答は、最も単純な多項式回帰である線形回帰に対してのみ機能します
tashuhka

5
注意:ここでのr_valueはピアソンの相関係数であり、R-2乗ではありません。r_squared = r_value ** 2
ウラジミールルキン

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'に変更されます」)
フランクダーノンコート

4
sklearnのr2_scoreが負の値になる可能性がありますが、これは通常のケースではありません。
Qinqing Liu 2017

なぜ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

19

私はもともとnumpy.corrcoef、元の質問がすでに使用してcorrcoefおり、実際に高次の多項式の適合について尋ねていたことを知らせずに、推奨する目的で以下のベンチマークを投稿しました。statsmodelsを使用して、多項式のr二乗問題に実際のソリューションを追加しました。トピックから外れているにもかかわらず、誰かに役立つ可能性がある元のベンチマークを残しました。


statsmodelsr^2多項式近似のを直接計算する機能があります。ここに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

をさらに活用するにはstatsmodels、Jupyter / 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.59 ms
  • Numpyポリフィット(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が何をしているかを補強するためだけに、純粋なpythonより28倍高速です。私はnumbaやpypyなどに精通していないので、誰か他の人がそれらのギャップを埋める必要がありますが、これは単純な線形回帰corrcoefを計算するrための最良のツールであると私には十分説得力があると思います。

これが私のベンチマークコードです。私はJupyter Notebookからコピーして貼り付けたので(IPython Notebookとは言い難い...)、途中で何かが壊れた場合はお詫びします。%timeit magicコマンドには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つの方法で回帰を比較しています。
ジョセフ

ええ、私はそれだけ知っていました...しかし、元の質問を読んでいないこと、そしてそれがすでにcorrcoefを使用していること、そして特に高次多項式のr ^ 2に対処していることを愚かに感じています...別の目的のためでした。おっと...
flutefreak7

1
を使用して元の質問の解決策で回答を更新し、statsmodels線形回帰r ^ 2メソッドの不要なベンチマークについて謝罪しました。
flutefreak7

scipyのlinregressがより一般的な作業を行うstatsmodelsよりも遅くなることを期待していなかったので、ベンチマークは依然として興味深いと思います。
ジョセフ

1
注:列で順序が逆になっているnumpyのvander関数を使用np.column_stack([x**i for i in range(k+1)])して、x[:,None]**np.arange(k+1)または使用してnumpyでベクトル化できます。
ジョセフ

5

R-2乗は、線形回帰にのみ適用される統計です。

基本的には、データの変動量が線形回帰によって説明できるかどうかを測定します。

したがって、「総和の合計」を計算します。これは、各結果変数の平均からの偏差の合計の2乗です。。。

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

ここで、y_barはyの平均です。

次に、「回帰二乗和」を計算します。これは、FITTED値が平均とどれだけ異なるかを示します

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

そして、これら2つの比率を見つけます。

これで、多項式近似を行うには、そのモデルのy_hatをプラグインするだけで済みますが、そのr-2乗を呼び出すのは正確ではありません。

ここに私が見つけたリンクがあります。


これが私の問題の根源のようです。Excelはどのようにして、多項式フィットと線形回帰の異なる2乗値を取得しますか?
トラビスビール

1
線形回帰からの適合と多項式モデルからの適合を与えるだけですか?2つのデータ配列からrsqを計算し、線形モデルからの近似を与えると仮定します。あなたは何を与えていますか?Excelの「ベストフィットトレンドライン」コマンドとは何ですか?
バルチマルク2009年

Excelのグラフ機能の一部です。一部のデータをプロットして右クリックし、いくつかの異なるタイプの傾向線から選択できます。ラインの方程式と各タイプのr二乗値を表示するオプションがあります。また、r二乗値はタイプごとに異なります。
トラビスビール

@Travis Beale-2つのモデルが入れ子になっていて、より大きなモデルの余分な係数がすべて0である場合を除いて、試行する平均関数ごとに異なるr二乗が得られます。したがって、もちろんExcelは異なるr二乗値を提供します。@Baltimark-これは線形回帰であるため、r二乗されます。
leif 2009年


5

PythonとNumpyで重み付き r-2乗を計算する関数を次に示します(ほとんどのコードは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

これはミラー)に対応します。

ここに画像の説明を入力してください

f_iは近似からの予測値、y_ {av}は観測データの平均y_iは観測データ値です。w_iは各データポイントに適用される重みで、通常はw_i = 1です。SSEは誤差による二乗の合計で、SSTは二乗の合計です。


興味があれば、Rのコード: https //gist.github.com/dhimmel/588d64a73fa4fef02c8fミラー


2

yとy_hatがパンダ系列であると仮定して、実際の値と予測値からR ^ 2を計算する非常に単純なpython関数を次に示します。

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.