Як обчислити r-квадрат за допомогою Python та Numpy?


92

Я використовую Python та Numpy для обчислення найкращого полінома довільного ступеня. Я передаю список значень x, значень y та ступеня полінома, який я хочу помістити (лінійний, квадратний тощо).

Це працює, але я також хочу обчислити r (коефіцієнт кореляції) і r-квадрат (коефіцієнт детермінації). Я порівнюю свої результати з найкращими можливостями трендової лінії Excel та значенням r-квадрата, яке він обчислює. Використовуючи це, я знаю, що правильно обчислюю r-квадрат для лінійного найкращого припасування (ступінь дорівнює 1). Однак моя функція не працює для поліномів зі ступенем більше 1.

Excel може це зробити. Як обчислити r-квадрат для поліномів вищого порядку за допомогою Numpy?

Ось моя функція:

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
Примітка: ви використовуєте ступінь лише при обчисленні коефіцієнтів.
Нік Дандулакіс,

тидок правильний. Ви обчислюєте кореляцію x та y та r-у квадраті для y = p_0 + p_1 * x. Дивіться мою відповідь нижче для коду, який повинен працювати. Якщо ви не проти, щоб я запитав, яка ваша кінцева мета? Ви робите вибір моделі (вибираючи, який ступінь використовувати)? Або щось інше?
leif

@leif - Запит зводиться до "зробити це так, як робить Excel". З цих відповідей я відчуваю, що користувачі можуть занадто багато читати значення r-квадрата при використанні нелінійної кривої, що найкраще підходить. Тим не менше, я не майстер математики, і це потрібна функціональність.
Тревіс Біл

Відповіді:


62

З документації numpy.polyfit це відповідає лінійній регресії. Зокрема, numpy.polyfit зі ступенем 'd' відповідає лінійній регресії із середньою функцією

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

Отже, вам просто потрібно розрахувати R-квадрат для цієї посадки. Сторінка wikipedia про лінійну регресію містить усі подробиці. Вас цікавить 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) і легше для читання
Йозеф

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

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-квадрат. 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 і буде змінена на" uniform_average ", починаючи з 0.19")
Франк Дернонкурт,

4
r2_score у sklearn може мати від’ємне значення, що не є звичайним випадком.
Циньцин Лю

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-квадрата питання, використовуючи statsmodels, і залишив оригінальні еталони, які, хоча і не в темі, можуть бути корисними для когось.


statsmodelsмає можливість безпосередньо розрахувати r^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, слід також поглянути на підбірку зразків моделі, яку можна надрукувати або відобразити як розширену HTML-таблицю в блокноті Jupyter / IPython. Об'єкт результатів забезпечує доступ до багатьох корисних статистичних показників, крім rsquared.

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

Нижче моя оригінальна відповідь, де я порівняв різні методи лінійної регресії r ^ 2 ...

Функція Корркоефа, використана у Запитанні, обчислює коефіцієнт кореляції r, лише для однієї лінійної регресії, тому вона не розглядає питання r^2щодо поліномів вищого порядку. Однак, для чого це варто, я прийшов до переконання, що для лінійної регресії це дійсно найшвидший і найпряміший метод обчислення r.

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

Це були мої результати за час порівняння групи методів для 1000 випадкових (х, у) точок:

  • Чистий Python (прямий rрозрахунок)
    • 1000 циклів, найкраще 3: 1,59 мс на цикл
  • Поліфіт Numpy (застосовується до поліномів n-го ступеня)
    • 1000 петель, найкраще 3: 326 мкс на петлю
  • Інструкція Numpy (прямий rрозрахунок)
    • 10000 петель, найкраще 3: 62,1 мкс на петлю
  • Numpy Corrcoef (прямий rрозрахунок)
    • 10000 петель, найкраще 3: 56,6 мкс на петлю
  • Скайпі (лінійна регресія з rвиходом)
    • 1000 петель, найкраще 3: 676 мкс на петлю
  • Статистичні моделі (можуть робити поліном n-го ступеня та багато інших підходів)
    • 1000 петель, найкраще 3: 422 мкс на петлю

Метод Корркоефа вузько перевершує обчислення r ^ 2 "вручну", використовуючи методи numpy. Це> в 5 разів швидше, ніж метод polyfit, і ~ 12 разів швидше, ніж scipy.linregress. Просто для того, щоб підсилити те, що numpy робить для вас, це в 28 разів швидше, ніж чистий python. Я недостатньо розбираюся в таких речах, як 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 методами без встановлення нахилу.
Йозеф

Так, я знав це стільки ... але зараз я почуваюся безглуздо, оскільки не читаю оригінальне питання і бачу, що він уже використовує corrcoef і спеціально звертається до r ^ 2 для поліномів вищого порядку ... тепер я почуваюся безглуздо для розміщення своїх тестів, які були з іншою метою. На жаль ...
flutefreak7

1
Я оновив свою відповідь вирішенням оригінального питання, використовуючи statsmodels, і вибачився за непотрібний порівняльний аналіз методів лінійної регресії r ^ 2, який я дотримувався як цікаву, але не пов'язану з темою інформацію.
flutefreak7

Я все ще вважаю орієнтир цікавим, оскільки я не очікував, що зв’язок scipy буде повільнішим, ніж statsmodels, який робить більше загальної роботи.
Йозеф

1
Зверніть увагу, np.column_stack([x**i for i in range(k+1)])можна векторизувати в numpy за x[:,None]**np.arange(k+1)допомогою або за допомогою функцій numpy's vander, які змінили порядок у стовпцях.
Йозеф

5

R-квадрат - це статистика, яка стосується лише лінійної регресії.

По суті, він вимірює, скільки змін у ваших даних можна пояснити лінійною регресією.

Отже, ви обчислюєте "Загальну суму квадратів", яка є загальним квадратичним відхиленням кожної з ваших змінних результату від їх середнього значення. . .

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

де y_bar - середнє значення y.

Потім ви обчислюєте "регресійну суму квадратів", яка наскільки відрізняються ваші ВСТАНОВЛЕНІ значення від середнього

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

і знайти співвідношення цих двох.

Тепер все, що вам потрібно було б зробити для поліноміального припасування, - це підключити y_hat від цієї моделі, але невірно називати це r-квадратом.

Ось посилання, яке я знайшов, трохи про це говорить.


Здається, це корінь моєї проблеми. Як тоді Excel отримує різне значення r-квадрата для поліноміального прилягання порівняно з лінійною регресією?
Тревіс Біл,

1
ви просто даєте excel підходи з лінійної регресії, а підходи з поліноміальної моделі? Він збирається обчислити rsq за двома масивами даних, і просто припустити, що ви даєте йому відповідність з лінійної моделі. Що ви даєте Excel? Яка команда `` найкраще підходить для лінії тренду '' в Excel?
Балтімарк,

Це частина графічних функцій Excel. Ви можете побудувати деякі дані, клацнути на них правою кнопкою миші, а потім вибрати один із декількох різних типів ліній тренду. Існує можливість побачити рівняння лінії, а також значення r-квадрата для кожного типу. Значення r-квадрата також різне для кожного типу.
Тревіс Біл,

@Travis Beale - ви отримаєте різний r-квадрат для кожної різної середньої функції, яку ви намагаєтесь (якщо дві моделі не вкладені, а додаткові коефіцієнти у більшій моделі працюють рівними 0). Тож, звичайно, Excel надає різні значення r-квадрата. @Baltimark - це лінійна регресія, тому вона r-квадрат.
leif


5

Ось функція для обчислення зваженого r-квадрата за допомогою Python і Numpy (більша частина коду походить від 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

Ось дуже проста функція python для обчислення R ^ 2 з фактичних та передбачуваних значень, припускаючи, що y та y_hat є рядами панд:

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.