2009-05-21 31 views
61

Estoy usando Python y Numpy para calcular un polinomio de mejor ajuste de grado arbitrario. Paso una lista de valores x, valores y, y el grado del polinomio que quiero ajustar (lineal, cuadrático, etc.).¿Cómo puedo calcular r-squared usando Python y Numpy?

Esto funciona, pero también quiero calcular r (coeficiente de correlación) y r-cuadrado (coeficiente de determinación). Estoy comparando mis resultados con la capacidad de línea de tendencia que mejor se ajusta a Excel, y el valor r-cuadrado que calcula. Usando esto, sé que estoy calculando r-cuadrado correctamente para el mejor ajuste lineal (grado igual a 1). Sin embargo, mi función no funciona para polinomios con un grado mayor que 1.

Excel es capaz de hacer esto. ¿Cómo calculo r-cuadrado para polinomios de orden superior usando Numpy?

Aquí es mi función:

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

Nota: se utiliza el grado sólo en el cálculo de coeffs. –

+0

tydok es correcto. Está calculando la correlación de x, y, y r-cuadrado para y = p_0 + p_1 * x. Consulte mi respuesta a continuación para obtener un código que debería funcionar. Si no te importa que pregunte, ¿cuál es tu objetivo final? ¿Estás haciendo la selección del modelo (elegir qué grado usar)? ¿O algo mas? – leif

+0

@leif - La solicitud se reduce a "hacerlo como lo hace Excel". Me da la sensación de estas respuestas de que los usuarios pueden estar leyendo demasiado en el valor r-cuadrado cuando se utiliza una curva no lineal de mejor ajuste. Sin embargo, no soy un asistente de matemáticas, y esta es la funcionalidad solicitada. –

Respuesta

44

De la documentación numpy.polyfit, está ajustando la regresión lineal. Específicamente, numpy.polyfit con grado 'd' se ajusta a una regresión lineal con la función media

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

Así que solo tiene que calcular el R-cuadrado para ese ajuste. La página de wikipedia en linear regression da detalles completos. Usted está interesado en R^2, que se puede calcular en un par de maneras, siendo probablemente la easisest

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

Cuando utilizo 'y_bar' para la media de las y, y 'y_ihat' a ser el valor de ajuste para cada punto

No estoy terriblemente familiar con numpy (por lo general trabajan en I), por lo que es probable que haya una manera más ordenada para calcular su R-cuadrado, pero el siguiente debería ser correcta

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

Solo quiero señalar que usar las funciones numpy array en lugar de la lista de comprensión será mucho más rápido, p. numpy.sum ((yi - ybar) ** 2) y más fácil de leer – user333700

+12

Según la página wiki http://en.wikipedia.org/wiki/Coefficient_of_determination, la definición más general de R^2 es 'R^2 = 1 - SS_err/SS_tot', con 'R^2 = SS_reg/SS_tot' siendo solo un caso especial. – LWZ

4

R cuadrado es una estadística que sólo se aplica a la regresión lineal.

Básicamente, mide la cantidad de variación en sus datos que se puede explicar mediante la regresión lineal.

Por lo tanto, se calcula la "Suma total de cuadrados", que es la desviación cuadrática total de cada una de las variables de resultado de su media. . .

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

donde y_bar es la media de las y.

A continuación, se calcula la "suma de cuadrados de regresión", que es la cantidad de sus valores ajustados difieren de la media

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

y encuentra la proporción de esos dos.

Ahora, todo lo que tendría que hacer para un ajuste polinomial es conectar el y_que es de ese modelo, pero no es exacto llamar a ese r-cuadrado.

Here es un enlace que encontré que le habla un poco.

+0

Esto parece ser la raíz de mi problema. Entonces, ¿cómo obtiene Excel un valor de r cuadrado diferente para un ajuste polinómico frente a una regresión lineal? –

+1

¿Acaso le está dando a Excel los ajustes de una regresión lineal y los ajustes de un modelo polinomial? Calculará el rsq a partir de dos matrices de datos, y simplemente supondrá que está obteniendo los ajustes de un modelo lineal. ¿Qué estás dando sobresalir? ¿Cuál es el comando de "mejor ajuste de línea de tendencia" en Excel? – Baltimark

+0

Es parte de las funciones de gráficos de Excel. Puede trazar algunos datos, hacer clic con el botón derecho sobre ellos y elegir entre varios tipos diferentes de líneas de tendencia. Existe la opción de ver la ecuación de la línea, así como un valor r-cuadrado para cada tipo. El valor de r-cuadrado también es diferente para cada tipo. –

4

artículo de Wikipedia en r-squareds sugiere que se puede usar para el ajuste del modelo general en lugar de solo la regresión lineal.

+1

Aquí hay una buena descripción del problema con R2 para la regresión no lineal: http://blog.minitab.com/blog/adventures-in-statistics/why-is-there-no-r-squared-for-nonlinear- regresión – Tickon

83

Una respuesta muy tarde, pero por si acaso alguien necesita una función listo para esto:

scipy.stats.stats.linregress

decir

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

como en la respuesta de @ Adam Marples.

+0

Es razonable analizar con * coeficiente de correlación *, y luego hacer el trabajo más grande, * regresión *. –

+10

Esta respuesta solo funciona para la regresión lineal, que es la regresión polinómica más simple – tashuhka

15

He estado utilizando esto con éxito, donde xey son como una matriz.

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 
25

De yanl (todavía-otra-biblioteca) sklearn.metrics tiene una función r2_square;

from sklearn.metrics import r2_score 

coefficient_of_dermination = r2_score(y, p(x)) 
+0

(Cuidado: "El valor predeterminado corresponde a 'varianza_prevista', este comportamiento está obsoleto desde la versión 0.17 y cambiará a 'uniform_average' a partir de 0.19") –

+0

r2_score en sklearn podría ser un valor negativo, que no es el caso normal. –

8

originalmente publicado por debajo de los puntos de referencia con el fin de recomendar numpy.corrcoef, tontamente sin darse cuenta de que la pregunta original ya utiliza corrcoef y de hecho fue preguntar por ataques de orden superior polinomio. He agregado una solución real a la pregunta polinomial r-squared usando modelos de estadísticas, y dejé los puntos de referencia originales, que aunque fuera del tema, son potencialmente útiles para alguien.


statsmodels tiene la capacidad para calcular la r^2 de un ajuste polinómico directamente, aquí hay 2 métodos ...

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 

Tomar ventaja adicional de statsmodels, también hay que mirar el modelo ajustado resumen, que se puede imprimir o mostrar como una tabla HTML enriquecida en el cuaderno Jupyter/IPython. El objeto de resultados proporciona acceso a muchas métricas estadísticas útiles además de rsquared.

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

A continuación es mi respuesta original, en el que como punto de referencia diferentes R^2 métodos de regresión lineal ...

La función corrcoef utilizado en la pregunta calcula el coeficiente de correlación, r, sólo para un único regresión lineal, por lo que no aborda la cuestión de r^2 para ajustes polinómicos de orden superior. Sin embargo, por lo que vale la pena, he llegado a encontrar que para la regresión lineal, de hecho es el método más rápido y directo de calcular r.

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

Estas fueron mis TimeIt resultados de la comparación de un montón de métodos para 1000 aleatorio (X, Y) puntos:

  • puro Python (r cálculo directo)
    • 1000 bucles, mejor de 3: 1.59 ms por ciclo
  • Numphy polyfit (aplicable a los ajustes polinomiales de grado n-0)
    • 1000 bucles, mejor de 3: 326 mu s por bucle
  • Numpy Manual (r cálculo directo)
    • 10.000 bucles, mejor de 3: 62,1 mu s por bucle
  • Numex corrcoef (cálculo directo)
    • 10000 bucles, mejor de 3: 56,6 mu s por bucle
  • Scipy (regresión lineal con r como una salida)
    • 1000 bucles, mejor de 3: 676 mu s por bucle
  • Statsmodels (pueden hacer n-ésimo grado del polinomio y muchos otros ataques)
    • 1000 bucles, mejor de 3: 422 mu s por bucle

El método de corrcoef supera por mucho el cálculo de la r^2 "manualmente" utilizando los métodos numpy. Es> 5 veces más rápido que el método polyfit y ~ 12 veces más rápido que el scipy.linregress. Solo para reforzar lo que numpy está haciendo por ti, es 28 veces más rápido que python puro. No soy muy versado en cosas como numba y pypy, por lo que alguien más tendría que llenar esos vacíos, pero creo que esto me convence bastante de que corrcoef es la mejor herramienta para calcular r para una regresión lineal simple.

Aquí está mi código de evaluación comparativa. Copié y pegué de un cuaderno de Jupyter (difícil de no llamarlo un cuaderno de IPython ...), así que me disculpo si algo se rompió en el camino. El comando% timeit magic requiere 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) 
+1

Está comparando 3 métodos para ajustar una pendiente y regresión con 3 métodos sin ajustar una pendiente. – user333700

+0

Sí, sabía eso mucho ... pero ahora me siento tonto por no leer la pregunta original y porque ya usa corrcoef y está abordando específicamente r^2 para polinomios de orden superior ... ahora me siento tonto por publicar mis puntos de referencia que fueron para un propósito diferente. Oops ... – flutefreak7

+1

He actualizado mi respuesta con una solución a la pregunta original usando 'modelsmodels', y me disculpo por la innecesaria evaluación comparativa de métodos de regresión lineal r^2, que guardo como información interesante, pero fuera del tema. – flutefreak7

2

Aquí es una función para calcular la ponderado R cuadrado con Python y Numpy (la mayoría del código proviene 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 

Ejemplo:

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 

salidas:

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

Esto corresponde a la formula (mirror):

enter image description here

con f_i es el valor predicho a partir del ajuste, y_ {av} es la media de la y_i datos observados es el valor de datos observado. w_i es la ponderación aplicada a cada punto de datos, generalmente w_i = 1. SSE es la suma de cuadrados debido a error y SST es la suma total de cuadrados.


Si está interesado, el código en R: https://gist.github.com/dhimmel/588d64a73fa4fef02c8f (mirror)

Cuestiones relacionadas