Как вычислить r-квадрат с помощью Python и Numpy?
Я использую 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
8 ответов:
С numpy.polyfit документация, это соответствует линейной регрессии. В частности, numpy.полифит со степенью ' d ' соответствует линейной регрессии со средней функцией
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
очень поздний ответ, но на всякий случай кому-то нужна готовая функция для этого:
scipy.статистика.статистика.linregress
т. е.
slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(x, y)
как в ответе @Adam Marples.
из yanl (yet-another-library)
sklearn.metrics
естьr2_square
Я успешно использую это, где 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
Я первоначально разместил тесты ниже с целью рекомендовать
numpy.corrcoef
, по глупости не понимая, что исходный вопрос уже используетcorrcoef
и на самом деле спрашивал о полиномиальных подходах более высокого порядка. Я добавил фактическое решение полиномиального вопроса R-squared с использованием statsmodels, и я оставил исходные тесты, которые, хотя и не по теме, потенциально полезны для кого-то.
statsmodels
имеет возможность расчетаr^2
в многочлен подходит непосредственно, вот 2 метода...import statsmodels.api as sm import stasmodels.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
чтобы в дальнейшем воспользоваться
statsmodels
, следует также посмотреть на встроенную сводку модели, которая может быть напечатана или отображена в виде богатой таблицы HTML в ноутбуке Jupyter/IPython. Объект результатов предоставляет доступ ко многим полезным статистическим метрикам в дополнение к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
это были мои результаты timeit от сравнения кучу методов для 1000 случайных (x, y) очки:
- чистом Python (прямые
r
расчет)
- 1000 циклов, лучше 3: 1.59 МС на цикл
- Numpy polyfit (применимо к n-й степени полинома подходит)
- 1000 петель, лучше всего 3: 326 МКС на петлю
- руководство Numpy (сразу
r
расчет)
- 10000 петель, лучше всего 3: 62,1 МКС на петлю
- Numpy corrcoef (прямой
r
расчет)
- 10000 петель, лучше всего 3: 56,6 МКС на петлю
- Scipy (линейная регрессия с
r
как выход)
- 1000 петель, лучше всего 3: 676 МКС на петлю
- Statsmodels (может сделать N-й степени полинома и многие другие подходит)
- 1000 петель, лучше всего 3: 422 МКС на петлю
метод corrcoef узко бьет вычисление r^2 "вручную" с использованием методов numpy. Это >5X быстрее, чем метод polyfit и ~12X быстрее, чем scipy.линрегресс. Просто чтобы укрепить то, что numpy делает для вас, это в 28 раз быстрее, чем чистый python. Я не очень хорошо разбираюсь в таких вещах, как numba и pypy, поэтому кто-то другой должен был бы заполнить эти пробелы, но я думаю, что это достаточно убедительно для меня, что
corrcoef
самый лучший инструмент для расчетаr
для простой линейной регрессии.вот мой код бенчмаркинга. Я копирую-вставляю из ноутбук Jupyter (трудно не назвать его ноутбуком IPython...), поэтому я извиняюсь, если что-то сломалось по пути. В %раз все волшебная команда требует оболочкой 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) 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)
r-квадрат-это статистика, которая применяется только к линейной регрессии.
по существу, он измеряет, насколько вариации в ваших данных могут быть объяснены линейной регрессией.
Итак, вы вычисляете "общую сумму квадратов", которая представляет собой общее квадратическое отклонение каждой из ваших переменных результата от их среднего значения. . .
\sum_{i}(y_{i} - y_bar)^2
где y_bar-среднее значение Y.
затем, вы вычисляете "регрессии сумма квадратов", то есть насколько ваши установленные значения отличаются от среднего
\sum_{i}(yHat_{i} - y_bar)^2
и найти соотношение этих двух.
теперь все, что вам нужно сделать для полиномиальной подгонки,-это подключить y_hat из этой модели, но это не точно назвать r-квадрат.
здесь это ссылка, которую я нашел, что говорит с ним немного.
статья в Википедии о R-квадраты предполагает, что он может быть использован для общей подгонки модели, а не только линейной регрессии.
вот функция для вычисления взвешенного 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-прогнозируемое значение из fit, y_{av} - среднее значение наблюдаемых данных y_i-наблюдаемое значение данных. w_i-это взвешивание, применяемое к каждой точке данных, обычно w_i=1. SSE-это сумма квадратов из-за ошибки, а SST-общая сумма квадратов.
Если интересно, то код в R:https://gist.github.com/dhimmel/588d64a73fa4fef02c8f (зеркала)