web-dev-qa-db-fra.com

Comment calculer r-squared avec Python et Numpy?

J'utilise Python et Numpy pour calculer un polynôme de meilleur ajustement de degré arbitraire. Je passe une liste de valeurs x, y et du degré de polynôme que je veux ajuster (linéaire, quadratique, etc.).

Cela fonctionne beaucoup, mais je veux aussi calculer r (coefficient de corrélation) et r-carré (coefficient de détermination). Je compare mes résultats à la fonctionnalité de courbe de tendance la mieux ajustée d'Excel et à la valeur de r-carré calculée. En utilisant ceci, je sais que je calcule correctement le r-carré pour le meilleur ajustement linéaire (degré = 1). Cependant, ma fonction ne fonctionne pas pour les polynômes de degré supérieur à 1.

Excel est capable de faire cela. Comment calculer r-carré pour les polynômes d'ordre supérieur à l'aide de Numpy?

Voici ma fonction:

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
79
Travis Beale

D'après la documentation numpy.polyfit , il s'agit d'une régression linéaire appropriée. Plus précisément, numpy.polyfit de degré 'd' correspond à une régression linéaire avec la fonction moyenne

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

Il vous suffit donc de calculer le R au carré pour cet ajustement. La page wikipedia sur régression linéaire donne tous les détails. Vous êtes intéressé par R ^ 2 que vous pouvez calculer de plusieurs façons, la plus facile étant probablement

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

Où j'utilise 'y_bar' pour la moyenne des y et 'y_ihat' pour la valeur d'ajustement de chaque point.

Je ne connais pas très bien numpy (je travaille habituellement en R), il existe donc probablement un moyen plus ordonné de calculer votre R au carré, mais ce qui suit devrait être correct

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
54
leif

Une réponse très tardive, mais juste au cas où quelqu'un aurait besoin d'une fonction prête pour cela:

scipy.stats.linregress

c'est à dire.

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

comme dans la réponse de @Adam Marples.

107
Gökhan Sever

De yanl (encore-une autre bibliothèque) sklearn.metrics a un r2_square fonction;

from sklearn.metrics import r2_score

coefficient_of_dermination = r2_score(y, p(x))
41
danodonovan

Je l'utilise avec succès, où x et y sont comme des tableaux.

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

À l'origine, j'avais posté les repères ci-dessous dans le but de recommander numpy.corrcoef, bêtement ne réalisant pas que la question initiale utilisait déjà corrcoef et posait en fait des questions sur les crises polynomiales d’ordre supérieur. J'ai ajouté une solution concrète à la question polynomiale r-squared à l'aide de statsmodels et j'ai laissé les points de repère d'origine, qui, s'ils sont hors sujet, sont potentiellement utiles pour quelqu'un.


statsmodels a la capacité de calculer le r^2 d'un ajustement polynomial directement, voici 2 méthodes ...

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

Pour profiter davantage de statsmodels, il convient également de consulter le résumé du modèle ajusté, qui peut être imprimé ou affiché sous forme de tableau HTML enrichi dans le bloc-notes Jupyter/IPython. L'objet de résultats donne accès à de nombreuses statistiques statistiques utiles en plus de rsquared.

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

Ci-dessous, ma réponse originale, où j’ai comparé diverses méthodes de régression linéaire r ^ 2 ...

La fonction corrcoef utilisée dans la question calcule le coefficient de corrélation, r, uniquement pour une régression linéaire unique. Elle ne traite donc pas la question de r^2 pour les ajustements polynomiaux d’ordre supérieur. Cependant, pour ce qui en vaut la peine, je viens de constater que pour la régression linéaire, il s’agit bien de la méthode la plus rapide et la plus directe pour calculer r.

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

Voici mes résultats obtenus en comparant plusieurs méthodes pour 1000 points aléatoires (x, y):

  • Pure Python (direct r calcul)
    • 1000 boucles, le meilleur des 3: 1,59 ms par boucle
  • Numpy polyfit (applicable aux ajustements polynomiaux du nième degré)
    • 1000 boucles, au mieux de 3: 326 µs par boucle
  • Manuel Numpy (calcul direct r)
    • 10000 boucles, le meilleur des 3: 62,1 µs par boucle
  • Numpy corrcoef (calcul direct r)
    • 10000 boucles, meilleur de 3: 56,6 µs par boucle
  • Scipy (régression linéaire avec r en sortie)
    • 1000 boucles, meilleur de 3: 676 µs par boucle
  • Statsmodels (peut faire des polynômes du n-degré et de nombreux autres crises)
    • 1000 boucles, meilleur de 3: 422 µs par boucle

La méthode corrcoef bat de peu le calcul du r ^ 2 "manuellement" à l’aide des méthodes numpy. Il est> 5 fois plus rapide que la méthode polyfit et environ 12 fois plus rapide que scipy.linregress. Juste pour renforcer ce que numpy fait pour vous, il est 28 fois plus rapide que le python pur. Numba et pypy ne me connaissent pas bien, alors quelqu'un d'autre devrait combler ces lacunes, mais je pense que cela me convainc beaucoup que corrcoef est le meilleur outil de calcul de r pour une régression linéaire simple.

Voici mon code d'analyse comparative. J'ai copié-collé à partir d'un bloc-notes Jupyter (difficile de ne pas l'appeler un bloc-notes IPython ...), donc je m'excuse si quelque chose s'est cassé en cours de route. La commande magique% timeit nécessite 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)
15
flutefreak7

Voici une fonction pour calculer le pondéré r-carré avec Python et Numpy (la majeure partie du code provient de 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

Exemple:

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 __== "__main__":
    main()
    #cProfile.run('main()') # if you want to do some profiling

les sorties:

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

Ceci correspond à la formule ( miroir ):

enter image description here

avec f_i est la valeur prédite de l'ajustement, y_ {av} est la moyenne des données observées y_i est la valeur des données observées. w_i est la pondération appliquée à chaque point de données, généralement w_i = 1. SSE est la somme des carrés due à une erreur et SST est la somme totale des carrés.


Si cela vous intéresse, le code dans R: https://Gist.github.com/dhimmel/588d64a73fa4fef02c8f ( miroir )

5
Franck Dernoncourt

L'article de Wikipédia sur r-squareds suggère qu'il peut être utilisé pour l'ajustement de modèle général plutôt que pour une simple régression linéaire.

5
Glenton

R-carré est une statistique qui ne s'applique qu'à la régression linéaire.

Essentiellement, il mesure l'ampleur de la variation de vos données pouvant être expliquée par la régression linéaire.

Ainsi, vous calculez la "somme totale des carrés", qui correspond à la déviation totale au carré de chacune de vos variables de résultat par rapport à leur moyenne. . .

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

où y_bar est la moyenne des y.

Ensuite, vous calculez la "somme des carrés de régression", qui correspond à la différence entre vos valeurs FITTED et la moyenne

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

et trouver le rapport de ces deux.

Maintenant, tout ce que vous auriez à faire pour un ajustement polynomial est de brancher le y_hat de ce modèle, mais il n'est pas exact d'appeler cela r-squared.

Ici est un lien que j'ai trouvé qui en parle un peu.

4
Baltimark