Bagaimana cara menghitung r-squared menggunakan Python dan Numpy?

93

Saya menggunakan Python dan Numpy untuk menghitung polinomial yang paling sesuai dari derajat arbitrer. Saya memberikan daftar nilai x, nilai y, dan derajat polinomial yang ingin saya paskan (linier, kuadrat, dll.).

Ini banyak berfungsi, tetapi saya juga ingin menghitung r (koefisien korelasi) dan r-kuadrat (koefisien determinasi). Saya membandingkan hasil saya dengan kapabilitas garis tren paling sesuai Excel, dan nilai r-squared yang dihitungnya. Dengan menggunakan ini, saya tahu saya menghitung r-kuadrat dengan benar untuk linier terbaik (derajat sama dengan 1). Namun, fungsi saya tidak berfungsi untuk polinomial dengan derajat lebih dari 1.

Excel mampu melakukan ini. Bagaimana cara menghitung r-kuadrat untuk polinomial orde tinggi menggunakan Numpy?

Inilah fungsi saya:

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
Travis Beale
sumber
1
Catatan: Anda menggunakan derajat hanya dalam perhitungan kopi.
Nick Dandoulakis
tydok benar. Anda menghitung korelasi dari x dan y dan r-kuadrat untuk y = p_0 + p_1 * x. Lihat jawaban saya di bawah untuk beberapa kode yang seharusnya berfungsi. Jika Anda tidak keberatan saya bertanya, apa tujuan akhir Anda? Apakah Anda melakukan pemilihan model (memilih gelar apa yang akan digunakan)? Atau sesuatu yang lain?
leif
@leif - Permintaan bermuara pada "lakukan seperti yang dilakukan Excel". Saya mendapatkan perasaan dari jawaban ini bahwa pengguna mungkin terlalu banyak membaca nilai r-squared saat menggunakan kurva best-fit non-linear. Meskipun demikian, saya bukan ahli matematika, dan ini adalah fungsi yang diminta.
Travis Beale

Jawaban:

62

Dari dokumentasi numpy.polyfit , itu adalah regresi linier yang pas. Secara khusus, numpy.polyfit dengan derajat 'd' cocok dengan regresi linier dengan fungsi mean

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

Jadi, Anda hanya perlu menghitung R-kuadrat untuk kecocokan itu. Halaman wikipedia tentang regresi linier memberikan detail lengkap. Anda tertarik pada R ^ 2 yang dapat Anda hitung dengan beberapa cara, mungkin yang paling mudah

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

Di mana saya menggunakan 'y_bar' untuk mean dari y, dan 'y_ihat' untuk menjadi nilai yang cocok untuk setiap poin.

Saya tidak terlalu paham dengan numpy (saya biasanya bekerja di R), jadi mungkin ada cara yang lebih rapi untuk menghitung R-kuadrat Anda, tetapi yang berikut ini seharusnya benar

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
leif
sumber
5
Saya hanya ingin menunjukkan bahwa menggunakan fungsi array numpy daripada pemahaman daftar akan jauh lebih cepat, misalnya numpy.sum ((yi - ybar) ** 2) dan lebih mudah dibaca
Josef
17
Menurut halaman wiki en.wikipedia.org/wiki/Coefficient_of_determination , definisi paling umum dari R ^ 2 adalah R^2 = 1 - SS_err/SS_tot, dengan R^2 = SS_reg/SS_tothanya kasus khusus.
LWZ
139

Balasan yang sangat terlambat, tetapi kalau-kalau seseorang membutuhkan fungsi siap untuk ini:

scipy.stats.linregress

yaitu

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

seperti dalam jawaban @Adam Marples.

Gökhan Sever
sumber
Masuk akal untuk menganalisis dengan koefisien korelasi , dan kemudian untuk melakukan pekerjaan yang lebih besar, regresi .
象 嘉 道
19
Balasan ini hanya berfungsi untuk regresi linier, yang merupakan regresi polinomial paling sederhana
tashuhka
9
Perhatian: r_value di sini adalah koefisien korelasi Pearson, bukan R-squared. r_squared = r_value ** 2
Vladimir Lukin
52

Dari yanl (yet-another-library) sklearn.metricsmemiliki r2_scorefungsi;

from sklearn.metrics import r2_score

coefficient_of_dermination = r2_score(y, p(x))
danodonovan
sumber
1
(Hati-hati: "Nilai default sesuai dengan 'variance_weighted', perilaku ini tidak digunakan lagi sejak versi 0.17 dan akan diubah menjadi 'uniform_average' mulai dari 0.19")
Franck Dernoncourt
4
r2_score di sklearn bisa menjadi nilai negatif, yang bukan kasus normal.
Qinqing Liu
1
Mengapa r2_score([1,2,3],[4,5,7])= -16?
cz
22

Saya telah berhasil menggunakan ini, di mana x dan y mirip dengan array.

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
Adam Marples
sumber
20

Saya awalnya memposting tolok ukur di bawah ini dengan tujuan merekomendasikan numpy.corrcoef, dengan bodohnya tidak menyadari bahwa pertanyaan asli sudah digunakan corrcoefdan sebenarnya menanyakan tentang kecocokan polinomial tingkat tinggi. Saya telah menambahkan solusi aktual untuk pertanyaan polinomial r-squared menggunakan statsmodels, dan saya telah meninggalkan tolok ukur asli, yang sementara di luar topik, berpotensi berguna bagi seseorang.


statsmodelsmemiliki kemampuan untuk menghitung r^2kesesuaian polinom secara langsung, berikut adalah 2 metode ...

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

Untuk memanfaatkan lebih jauh statsmodels, kita juga harus melihat ringkasan model yang dipasang, yang dapat dicetak atau ditampilkan sebagai tabel HTML kaya di notebook Jupyter / IPython. Objek hasil menyediakan akses ke banyak metrik statistik yang berguna sebagai tambahan rsquared.

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

Di bawah ini adalah Jawaban asli saya di mana saya membandingkan berbagai metode regresi linier r ^ 2 ...

Fungsi koreksi yang digunakan dalam Pertanyaan menghitung koefisien korelasi r, hanya untuk satu regresi linier, sehingga tidak menjawab pertanyaan tentang r^2kesesuaian polinomial orde tinggi. Namun, untuk apa nilainya, saya telah menemukan bahwa untuk regresi linier, itu memang metode penghitungan tercepat dan paling langsung r.

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

Ini adalah hasil waktu saya dari membandingkan sekumpulan metode untuk 1000 poin acak (x, y):

  • Python murni ( rperhitungan langsung )
    • 1000 loop, terbaik 3: 1,59 ms per loop
  • Poliit numpy (berlaku untuk kesesuaian polinomial derajat ke-n)
    • 1000 loop, terbaik 3: 326 µs per loop
  • Numpy Manual ( rperhitungan langsung )
    • 10000 loop, terbaik 3: 62,1 µs per loop
  • Numpy corrcoef ( rpenghitungan langsung )
    • 10000 loop, terbaik 3: 56,6 µs per loop
  • Scipy (regresi linier dengan rsebagai keluaran)
    • 1000 loop, terbaik 3: 676 µs per loop
  • Statsmodels (dapat melakukan polinomial derajat ke-n dan banyak kecocokan lainnya)
    • 1000 loop, terbaik 3: 422 µs per loop

Metode corrcoef mengalahkan penghitungan r ^ 2 "secara manual" menggunakan metode numpy. Ini> 5X lebih cepat dari metode polyfit dan ~ 12X lebih cepat dari scipy.linregress. Hanya untuk memperkuat apa yang numpy lakukan untuk Anda, ini 28X lebih cepat dari python murni. Saya tidak berpengalaman dalam hal-hal seperti numba dan pypy, jadi orang lain harus mengisi celah itu, tetapi saya pikir ini cukup meyakinkan bagi saya bahwa corrcoefini adalah alat terbaik untuk menghitung rregresi linier sederhana.

Ini kode pembandingan saya. Saya menyalin-tempel dari Notebook Jupyter (sulit untuk tidak menyebutnya sebagai Notebook IPython ...), jadi saya minta maaf jika ada yang rusak di jalan. Perintah sihir% timeit membutuhkan 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)
flutefreak7
sumber
1
Anda membandingkan 3 metode dengan memasang kemiringan dan regresi dengan 3 metode tanpa memasang kemiringan.
Josef
Ya, saya tahu banyak ... tapi sekarang saya merasa konyol karena tidak membaca pertanyaan asli dan melihat bahwa pertanyaan itu sudah menggunakan corrcoef dan secara khusus menangani r ^ 2 untuk polinomial tingkat tinggi ... sekarang saya merasa konyol untuk memposting tolok ukur saya yang mana untuk tujuan yang berbeda. Ups ...
flutefreak7
1
Saya telah memperbarui jawaban saya dengan solusi untuk pertanyaan asli menggunakan statsmodels, dan meminta maaf atas pembandingan yang tidak perlu dari metode regresi linier r ^ 2, yang saya simpan sebagai info yang menarik, namun di luar topik.
flutefreak7
Saya masih menganggap benchmark menarik karena saya tidak berharap linregress scipy menjadi lebih lambat dari statsmodels yang melakukan pekerjaan yang lebih umum.
Josef
1
Catatan, np.column_stack([x**i for i in range(k+1)])dapat di-vektorisasi secara numpy dengan x[:,None]**np.arange(k+1)atau menggunakan fungsi numpy vander yang memiliki urutan terbalik dalam kolom.
Josef
5

R-squared adalah statistik yang hanya berlaku untuk regresi linier.

Pada dasarnya, ini mengukur seberapa banyak variasi dalam data Anda dapat dijelaskan dengan regresi linier.

Jadi, Anda menghitung "Jumlah Total Kuadrat", yang merupakan total deviasi kuadrat dari setiap variabel hasil Anda dari rata-ratanya. . .

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

dimana y_bar adalah mean dari y.

Kemudian, Anda menghitung "jumlah regresi kuadrat", yaitu seberapa besar nilai FITTED Anda berbeda dari rata-rata

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

dan temukan rasio keduanya.

Sekarang, yang harus Anda lakukan untuk pemasangan polinomial adalah menyambungkan y_hat dari model itu, tetapi tidak akurat untuk menyebutnya r-squared.

Ini adalah tautan yang saya temukan yang menjelaskannya sedikit.

Baltimark
sumber
Ini sepertinya menjadi akar masalah saya. Bagaimana Excel mendapatkan nilai r-kuadrat yang berbeda untuk kecocokan polinomial vs. regresi linier?
Travis Beale
1
apakah Anda hanya memberikan kecocokan excel dari regresi linier, dan kecocokan dari model polinomial? Ini akan menghitung rsq dari dua array data, dan anggap saja Anda memberinya kecocokan dari model linier. Apa yang Anda berikan pada excel? Apa perintah 'best fit trendline' di excel?
Baltimark
Ini adalah bagian dari fungsi grafik Excel. Anda dapat memplot beberapa data, klik kanan padanya, lalu pilih dari beberapa jenis garis tren. Ada opsi untuk melihat persamaan garis serta nilai r-kuadrat untuk setiap jenis. Nilai r-squared juga berbeda untuk tiap jenis.
Travis Beale
@Travis Beale - Anda akan mendapatkan r-kuadrat berbeda untuk setiap fungsi rata-rata berbeda yang Anda coba (kecuali dua model bertingkat dan koefisien ekstra dalam model yang lebih besar semuanya berfungsi menjadi 0). Jadi tentu saja Excel memberikan nilai r-squared yang berbeda. @ Baltimark - ini adalah regresi linier sehingga r-kuadrat.
leif
5

Berikut adalah fungsi untuk menghitung weighted r-squared dengan Python dan Numpy (sebagian besar kode berasal dari 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

Contoh:

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

keluaran:

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

Ini sesuai dengan rumus ( cermin ):

masukkan deskripsi gambar di sini

dengan f_i adalah nilai prediksi dari fit, y_ {av} adalah mean dari data yang diamati y_i adalah nilai data yang diamati. w_i adalah pembobotan yang diterapkan pada setiap titik data, biasanya w_i = 1. SSE adalah jumlah kuadrat karena kesalahan dan SST adalah jumlah total kuadrat.


Jika tertarik, kodenya di R: https://gist.github.com/dhimmel/588d64a73fa4fef02c8f ( mirror )

Franck Dernoncourt
sumber
2

Berikut adalah fungsi python yang sangat sederhana untuk menghitung R ^ 2 dari nilai aktual dan prediksi dengan asumsi y dan y_hat adalah seri pandas:

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)
Michel Floyd
sumber
0

Dari sumber scipy.stats.linregress. Mereka menggunakan metode jumlah rata-rata kuadrat.

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
Mott The Tuple
sumber
0

Anda dapat menjalankan kode ini secara langsung, ini akan menemukan Anda polinomialnya, dan akan menemukan nilai-R untuk Anda yang dapat Anda beri komentar di bawah jika Anda memerlukan penjelasan lebih lanjut.

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)
Karam Qusai
sumber