122

我正在使用 Python 和 Numpy 来计算任意次数的最佳拟合多项式。我传递了 x 值、y 值和我想要拟合的多项式的次数(线性、二次等)的列表。

这很有效,但我也想计算 r(相关系数)和 r-squared(确定系数)。我将我的结果与 Excel 的最佳拟合趋势线功能以及它计算的 r 平方值进行比较。使用它,我知道我正在为线性最佳拟合(度数等于 1)正确计算 r 平方。但是,我的函数不适用于度数大于 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
4

12 回答 12

163

一个很晚的回复,但以防万一有人需要为此准备好的功能:

scipy.stats.linregress

IE

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

就像@Adam Marples 的回答一样。

于 2009-10-04T21:15:51.133 回答
75

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
于 2009-05-21T20:48:35.393 回答
69

从 yanl (yet-another-library)sklearn.metrics有一个r2_score功能;

from sklearn.metrics import r2_score

coefficient_of_dermination = r2_score(y, p(x))
于 2015-06-12T13:41:03.323 回答
29

我一直在成功使用它,其中 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
于 2012-12-17T16:37:46.943 回答
25

我最初发布下面的基准是为了推荐numpy.corrcoef,愚蠢地没有意识到原始问题已经使用corrcoef并且实际上是在询问高阶多项式拟合。我已经使用 statsmodels 为多项式 r-squared 问题添加了一个实际的解决方案,并且我留下了原始基准,这些基准虽然离题,但可能对某人有用。


statsmodels具有r^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 表格。除了 .results 对象之外,结果对象还提供对许多有用统计指标的访问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) 点的一堆方法的 timeit 结果:

  • 纯Python(直接r计算)
    • 1000 个循环,3 个循环中的最佳:每个循环 1.59 毫秒
  • 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 为您所做的事情,它比纯 python 快 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)

21 年 7 月 28 日基准测试结果。(Python 3.7、numpy 1.19、scipy 1.6、statsmodels 0.12)

Python
2.41 ms ± 180 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Numpy polyfit
318 µs ± 44.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Numpy Manual
79.3 µs ± 4.05 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Numpy corrcoef
83.8 µs ± 1.37 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Scipy
221 µs ± 7.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Statsmodels
375 µs ± 3.63 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
于 2016-01-05T17:21:45.813 回答
8

这是一个用 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

这对应于公式镜像):

在此处输入图像描述

其中 f_i 是拟合的预测值,y_{av} 是观测数据的平均值 y_i 是观测数据值。w_i 是应用于每个数据点的权重,通常 w_i=1。SSE 是误差平方和,SST 是总平方和。


如果有兴趣,R 中的代码:https ://gist.github.com/dhimmel/588d64a73fa4fef02c8f ( mirror )

于 2017-08-07T00:55:37.317 回答
5

R-squared 是一种仅适用于线性回归的统计量。

从本质上讲,它衡量了数据中有多少变化可以用线性回归来解释。

因此,您计算“总平方和”,即每个结果变量与其均值的总平方偏差。. .

公式1

其中 y_bar 是 y 的平均值。

然后,您计算“回归平方和”,即您的拟合值与平均值相差多少

公式2

并找到这两者的比例。

现在,对于多项式拟合,您所要做的就是插入该模型中的 y_hat,但称其为 r-squared 并不准确。

是我发现的一个链接,可以说明一点。

于 2009-05-21T16:54:49.733 回答
5

关于r-squareds的维基百科文章表明它可以用于一般模型拟合,而不仅仅是线性回归。

于 2009-08-25T06:06:24.767 回答
4

假设 y 和 y_hat 是熊猫系列,这是一个非常简单的 python 函数,用于从实际值和预测值计算 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)
于 2020-05-01T18:48:17.113 回答
2

您可以直接执行此代码,这将找到您的多项式,并会找到您的 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)
于 2020-07-01T13:38:34.167 回答
1

使用 numpy 模块(在 python3 中测试):

import numpy as np
def linear_regression(x, y): 
    coefs = np.polynomial.polynomial.polyfit(x, y, 1)
    ffit = np.poly1d(coefs)
    m = ffit[0]
    b = ffit[1] 
    eq = 'y = {}x + {}'.format(round(m, 3), round(b, 3))
    rsquared = np.corrcoef(x, y)[0, 1]**2
    return rsquared, eq, m, b

rsquared, eq, m, b = linear_regression(x,y)
print(rsquared, m, b)
print(eq)

输出:

0.013378252355751777 0.1316331351105754 0.7928782850418713 
y = 0.132x + 0.793

注:r² ≠ R²
r² 称为“确定系数”
R² 是皮尔逊系数的平方

R²,正式合并为 r²,可能是您想要的,因为它是最小二乘拟合,比 r² 的简单分数要好。Numpy 不怕称其为“corrcoef”,这假定 Pearson 是事实上的相关系数。

于 2021-08-21T23:50:32.107 回答
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
于 2019-11-16T00:43:40.177 回答