junio 06, 2017

Regresión curvilínea


Las regresiones curvilíneas (páginas 233 – 239) ajustan mejor los valores de fenómenos naturales: el crecimiento de un patógeno con el tiempo, por ejemplo.


ggplot(MELOCOTONERO, aes(HORAS.FRÍO, PESO.SECO))                             + 
  geom_point(shape = 16, colour = "red", size = 4)                           +
  labs(title = "Fig 17.2. Relación entre el peso seco de yemas y horas-frío")+
  ylab("Peso seco de 10 yemas (mg)") + xlab("Horas-frío (horas<7ºC)")        + 
  geom_smooth(method = "loess", size = 1.5, se = T) 


Para comprender estos análisis y sus trasformaciones, recomendamos dar un repaso a los logaritmos comunes y neperianos, facilitará entender los cambios de base a exponente.


Códigos in R


#***********************************************************
# EXPERIMENTACIÓN EN AGRICULTURA                              
# Fernández Escobar, R.; Trapero, A.; Domínguez, J. 2010       
# CAPÍTULO 17: REGRESIÓN CURVILÍNEA
# CURVAS DE TIPO LOGARÍTMICO Y EXPONENCIAL (pp. 233-239)
#************************************************************
  
#### Tabla 17.1 (página 235)
MELOCOTON <- data.frame(
  HORAS.FRÍO = c(50, 100, 200, 300, 350, 400, 450),
  PESO.SECO  = c( 3,   4,   6,   8,  14,  30,  71))
attach(MELOCOTON)

#### Transformaciones
LogHORAS.FRÍO = log10(HORAS.FRÍO)
LogPESO.SECO  = log10(PESO.SECO)
LnHORAS.FRÍO  = log(HORAS.FRÍO)
LnPESO.SECO   = log(PESO.SECO)
MELOCOTONERO <- round(data.frame(PESO.SECO, HORAS.FRÍO, LogPESO.SECO, LogHORAS.FRÍO, LnPESO.SECO, LnHORAS.FRÍO),digits = 2)
attach(MELOCOTONERO); knitr::kable(MELOCOTONERO)
PESO.SECOHORAS.FRÍOLogPESO.SECOLogHORAS.FRÍOLnPESO.SECOLnHORAS.FRÍO
3500.481.701.103.91
41000.602.001.394.61
62000.782.301.795.30
83000.902.482.085.70
143501.152.542.645.86
304001.482.603.405.99
714501.852.654.266.11
#### Representación gráfica de los datos originales
plot(PESO.SECO ~ HORAS.FRÍO, pch = 19, col = "red")
lines(lowess(PESO.SECO ~ HORAS.FRÍO),  col = "blue")


#### Regresión lineal con los datos originales
# Fig 17.2. Relación entre el peso seco de yemas de melocotonero y las horas-frío acumuladas durante el invierno
library(ggplot2)
library(ggpmisc)
ggplot(MELOCOTONERO, aes(HORAS.FRÍO, PESO.SECO))                             + 
  geom_point(shape = 16, colour = "red", size = 4)                           +
  labs(title = "Fig 17.2. Relación entre el peso seco de yemas y horas-frío")+
  ylab("Peso seco de 10 yemas (mg)") + xlab("Horas-frío (horas<7ºC)")        + 
  geom_smooth(method = "loess", size = 1.5, se = T) 
# Regresión sin transformar datos
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = HORAS.FRÍO, y = PESO.SECO))           +
  geom_smooth(method     = "lm", se=TRUE, color="blue", formula = my.formula)          +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# Coeficientes de regresión lineal (página 235)
reg1 <- lm(PESO.SECO ~ HORAS.FRÍO, data = MELOCOTONERO)
reg1$coefficients
## (Intercept)  HORAS.FRÍO 
## -13.1030928   0.1230928
summary(reg1)
## Call:
## lm(formula = PESO.SECO ~ HORAS.FRÍO, data = MELOCOTONERO)
## 
## Residuals:
##       1       2       3       4       5       6       7 
##   9.948   4.794  -5.515 -15.825 -15.979  -6.134  28.711 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)  
## (Intercept) -13.10309   14.02402  -0.934   0.3930  
## HORAS.FRÍO    0.12309    0.04684   2.628   0.0466 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 17.44 on 5 degrees of freedom
## Multiple R-squared:   0.58,  Adjusted R-squared:  0.4961 
## F-statistic: 6.906 on 1 and 5 DF,  p-value: 0.04665
# Gráficos de diagnósticos
par(mfrow=c(1,2))
plot(reg1)
#### Representación gráfica de datos transformados a log10 (página 236)
# Opción 1 
reg2 <- lm(LogPESO.SECO ~ LogHORAS.FRÍO, data = MELOCOTONERO)
summary(reg2)
## Call:
## lm(formula = LogPESO.SECO ~ LogHORAS.FRÍO, data = MELOCOTONERO)
## 
## Residuals:
##       1       2       3       4       5       6       7 
##  0.1845 -0.0505 -0.2255 -0.3186 -0.1396  0.1194  0.4302 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)  
## (Intercept)    -1.7164     0.7749  -2.215   0.0776 .
## LogHORAS.FRÍO   1.1835     0.3301   3.585   0.0158 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2856 on 5 degrees of freedom
## Multiple R-squared:  0.7199, Adjusted R-squared:  0.6639 
## F-statistic: 12.85 on 1 and 5 DF,  p-value: 0.01579
# Opción 2
reg2 <- lm(LogPESO.SECO ~ I(LogHORAS.FRÍO), data = MELOCOTONERO) # modelo simple para obtener valores iniciales
summary(reg2)
## Call:
## lm(formula = LogPESO.SECO ~ I(LogHORAS.FRÍO), data = MELOCOTONERO)
## 
## Residuals:
##       1       2       3       4       5       6       7 
##  0.1845 -0.0505 -0.2255 -0.3186 -0.1396  0.1194  0.4302 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)  
## (Intercept)       -1.7164     0.7749  -2.215   0.0776 .
## I(LogHORAS.FRÍO)   1.1835     0.3301   3.585   0.0158 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2856 on 5 degrees of freedom
## Multiple R-squared:  0.7199, Adjusted R-squared:  0.6639 
## F-statistic: 12.85 on 1 and 5 DF,  p-value: 0.01579
# méthod = "lm"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = LogHORAS.FRÍO, y = LogPESO.SECO))     +
  geom_smooth(method     = "lm", se=TRUE, color="blue", formula = my.formula)          +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# método = nls"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = LogHORAS.FRÍO, y = LogPESO.SECO))      +
  geom_smooth(method     = "nls", se=TRUE, color="blue", formula = my.formula)          +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# Gráficos de diagnósticos
plot(reg2)
# Opción 3: Regresión nolineal, empleando "nls"
nolineal <- nls(LogPESO.SECO ~  a + b * LogHORAS.FRÍO, data = MELOCOTONERO, start = list(a = 0, b = 0))
summary(nolineal)
## 
## Formula: LogPESO.SECO ~ a + b * LogHORAS.FRÍO
## 
## Parameters:
##   Estimate Std. Error t value Pr(>|t|)  
## a  -1.7164     0.7749  -2.215   0.0776 .
## b   1.1835     0.3301   3.585   0.0158 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2856 on 5 degrees of freedom
## 
## Number of iterations to convergence: 1 
## Achieved convergence tolerance: 1.895e-08
#### Antilogaritmo
# base = b; y = número; x = valor de log
# log_b(x) = y => b^y = x      b > 0 y b != 1
# antilog_b(y) = x => b^y = x

#Funcion de antilogaritmo 
antilog<-function(lx,base) 
{ 
  lbx<-lx/log(exp(1),base=base) 
  result<-exp(lbx) 
  result 
}

# Antilog: El primer valor representa el intercepto, el segundo la base del logaritmo
antilog(-1.71, 10)
## [1] 0.01949845
#### Curva de tipo exponencial (página 237)
reg3 <- lm(LogPESO.SECO ~ HORAS.FRÍO, data = MELOCOTONERO)
summary(reg3)
## 
## Call:
## lm(formula = LogPESO.SECO ~ HORAS.FRÍO, data = MELOCOTONERO)
## 
## Residuals:
##        1        2        3        4        5        6        7 
##  0.10072  0.06789 -0.05778 -0.24345 -0.14629  0.03088  0.24804 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)   
## (Intercept) 0.2264433  0.1444351   1.568  0.17772   
## HORAS.FRÍO  0.0030567  0.0004824   6.336  0.00144 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1796 on 5 degrees of freedom
## Multiple R-squared:  0.8893, Adjusted R-squared:  0.8671 
## F-statistic: 40.15 on 1 and 5 DF,  p-value: 0.001444
# Gráfico método = "lm"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = HORAS.FRÍO, y = LogPESO.SECO))        +
  geom_smooth(method     = "lm", se=TRUE, color="blue", formula = my.formula)          +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# método = "nls"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = HORAS.FRÍO, y = LogPESO.SECO))       +
  geom_smooth(method     = "nls", se=TRUE, color="blue", formula = my.formula)        +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
              parse     = TRUE) + geom_point() ; p
# Gráficos de diagnósticos
plot(reg3)
# Antilog
antilog(0.22, 10) # valor 0.22 y 0.003 obtenidos de los coeficientes de regresión
## [1] 1.659587
antilog(0.003, 10)
## [1] 1.006932
#### Curva con logaritmo neperiano en variable dependiente (página 238)
reg4 <- lm(LnPESO.SECO ~ HORAS.FRÍO, data = MELOCOTONERO) 
summary(reg4)
## Call:
## lm(formula = LnPESO.SECO ~ HORAS.FRÍO, data = MELOCOTONERO)
## 
## Residuals:
##        1        2        3        4        5        6        7 
##  0.22619  0.16474 -0.13814 -0.55103 -0.34247  0.06608  0.57464 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)   
## (Intercept) 0.522371   0.332186   1.573  0.17664   
## HORAS.FRÍO  0.007029   0.001109   6.335  0.00145 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.413 on 5 degrees of freedom
## Multiple R-squared:  0.8892, Adjusted R-squared:  0.8671 
## F-statistic: 40.14 on 1 and 5 DF,  p-value: 0.001446
# Gráfico método = "lm"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = HORAS.FRÍO, y = LnPESO.SECO))         +
  geom_smooth(method     = "lm", se=TRUE, color="blue", formula = my.formula)          +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# método = "nls"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = HORAS.FRÍO, y = LnPESO.SECO))        +
  geom_smooth(method     = "nls", se=TRUE, color="blue", formula = my.formula)        +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# Gráficos de diagnósticos
plot(reg4)
# Antilog
exp(0.52) # 0.52 es el valor del intercepto
## [1] 1.682028
#### Curva con logaritmo neperiano en variable independiente (página 239)
reg5 <- lm(PESO.SECO ~ LnHORAS.FRÍO, data = MELOCOTONERO)
summary(reg5)
## Call:
## lm(formula = PESO.SECO ~ LnHORAS.FRÍO, data = MELOCOTONERO)
## 
## Residuals:
##       1       2       3       4       5       6       7 
##  10.674  -1.462 -12.410 -17.916 -14.918  -1.358  37.390 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)
## (Intercept)    -81.05      56.93  -1.424    0.214
## LnHORAS.FRÍO    18.77      10.53   1.782    0.135
## 
## Residual standard error: 21.04 on 5 degrees of freedom
## Multiple R-squared:  0.3885, Adjusted R-squared:  0.2662 
## F-statistic: 3.176 on 1 and 5 DF,  p-value: 0.1348
# Gráfico método = "lm"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = LnHORAS.FRÍO, y = PESO.SECO))         +
  geom_smooth(method     = "lm", se=TRUE, color="blue", formula = my.formula)          +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# método = "nls"
my.formula <- y ~ x
p <- ggplot(data         = MELOCOTONERO, aes(x = LnHORAS.FRÍO, y = PESO.SECO))        +
  geom_smooth(method     = "nls", se=TRUE, color="blue", formula = my.formula)        +
  stat_poly_eq(formula   = my.formula, eq.with.lhs = "italic(hat(y))~`=`~",
               aes(label = paste(..eq.label.., ..rr.label.., sep = "*plain(\",\")~")), 
               parse     = TRUE) + geom_point() ; p
# Gráficos de diagnósticos
plot(reg5)
# Antilog
exp(-81.0) # -81.0 es el valor intercepto de la ecuación de regresión
## [1] 6.639677e-36

----------------------------------------------------

Archivos adjuntos: capítulo 17a.r



8 comentarios:

  1. Hola, muy buen post, soy agrónomo, no estadístico y estoy aprendiendo a usar R de modo autodidacta, y me surgió el siguiente problema.
    Ingreso las isguientes lineas de código

    x<-c(18.26,25.37,16.13,18.96,18.91,19.91)
    y<-c(9.11,13.58,9.03,11.48,10.00,9.57)

    Reg<-lm(formula=y~x)
    anova(Reg)
    summary(Reg)

    Reg<-lm(formula=y~x-1)
    anova(Reg)
    summary(Reg)

    No obstante, el R^2 y el p-value de la salidas del modelo sin intercepto no convence, aún más cuando lo comparo con la salida de la regresión con intercepto.
    Si pudera ayudarme, yo le agradecería mucho.
    Saludos!

    ResponderEliminar
    Respuestas
    1. Hola Víctor,

      ¡Muy buena observación! El R^2 no se calculó correctamente debido a la ausencia de un intercepto. Los resultados que obtuviste con el software son correctos. La diferencia se produce principalmente en el Total de Suma de Cuadrados (SST) de la regresión sin intercepto. Con intercepto el SST es 15.64588; no obstante, sin intercepto el número sube a 672.3247. Te envío la muestra de los cálculos.

      #### datos
      x<-c(18.26,25.37,16.13,18.96,18.91,19.91)
      y<-c(9.11,13.58,9.03,11.48,10.00,9.57)


      #### regresiones con (int.reg) y sin intercepto (noint.reg)
      int.reg <- lm(y~x) ; anova(int.reg) ; summary(int.reg)
      noint.reg <- lm(y~x+0); anova(noint.reg); summary(noint.reg)


      #### para comparación
      # incluye intercepto
      y_ajustados <- int.reg$fitted.values
      SSE <- sum((y_ajustados - y)^2); SSE
      SST <- sum((y - mean(y))^2); SST
      1-SSE/SST # R^2 con intercepto

      # sin intercepto
      y_ajustados2 <- noint.reg$fitted.values
      SSE2 <- sum((y_ajustados2 - y)^2); SSE2 # SSE2 es solo ligeramente mas alto que SSE..
      SST2 <- sum((y - 0)^2) ; SST2 # !!! la diferencia esta aqui !!!
      1-SSE2/SST2 # R^2 sin intercepto


      Encontraras explicaciones teóricas en estos sitios:
      https://econintec.files.wordpress.com/2008/06/regresion-a-traves-del-origen.pdf
      https://fvela.files.wordpress.com/2010/06/apunte_extensiones-de-regresion.pdf

      Y, ejemplos de codigos en los dos siguientes:
      https://stats.stackexchange.com/questions/26176/removal-of-statistically-significant-intercept-term-increases-r2-in-linear-mo
      https://stackoverflow.com/questions/20333600/why-does-summary-overestimate-the-r-squared-with-a-no-intercept-model-formula

      Eliminar
    2. Por su tiempo y explicación ¡Muchas Gracias! Ahora seré un seguidor del blog!!! muy interesante y delicioso el tema de las pithayas!
      Solo otra duda, que no me queda claro con al leer las páginas que me recomendaste.
      Desde la perspectiva de la interpretación de resultados.
      ¿Tiene sentido interpretar el R^2 y p-value, a pesar de estar sobreestimados en el modelo sin intercepto?
      ¡Gracias!
      ¡Muy amable!

      Eliminar
    3. Tu pregunta tiene implícita solo una respuesta, lo cual no es cierto. Es como si me preguntaras ¿Tiene sentido medir la temperatura únicamente empleando el punto de congelación y ebullición del agua, en grados Celsius? Eso no es exacto, pues también existen otras maneras de cuantificar los cambios de temperatura, además, ya conoces que hay temperaturas más bajas y más altas que el punto de congelación y ebullición; igual, hay otras unidades de medida: Fahrenheit y Kelvin.

      Así que si desconoces a priori que E [Y] = 0 cuando x = 0 (o no tienes seguridad si la regresión atraviesa el cero en la gráfica x,y = 0,0), entonces probablemente no deberías estar ajustando una línea a través del origen.

      Cuando la línea de regresión pasa por el origen, es decir, β0 = 0, entonces R2 no se puede usar para juzgar el modelo ajustado. Para comparar modelos de regresión, te recomiendo analizarlos con otros criterios, como el Criterio de Información de Akaike (AIC) y el Mallows's Cp — ambos indican que variables debes emplear o descartar en tus modelos, y eso incluye el intercepto. Elije el modelo que ofrezca los resultados con menores valores.

      En tu ejemplo es la regresión sin intercepto. Aunque la diferencia es muy mínima, con intercepto el AIC=19.812103 y sin intercepto AIC=17.893030. Yo no me complicaría, elegiría cualquiera de los dos. Sin embargo, optaría por la regresión CON INTERCEPTO si estoy seguro que ES IMPOSIBLE que existencia algo que tengo valor cero en la vida real. En el siguiente paper, encontraras un ejemplo, en donde la altura (y) depende del ancho del huevo (x); en él, el autor dice que es imposible que exista un huevo con nada de ancho (ver paginas 78-79): https://online.stat.psu.edu/~ajw13/stat501/SpecialTopics/Reg_thru_origin.pdf

      Cuéntame, ¿Qué libro de texto lees? Pregunto porque posiblemente necesites uno que trate modelos lineales de manera más avanzada; fórmulas que emplean el alfabeto griego, con notaciones matemáticas, sumatorias, sigmas,... son ineludibles. Saludos, Edgardo

      #### datos
      x<-c(18.26,25.37,16.13,18.96,18.91,19.91)
      y<-c(9.11,13.58,9.03,11.48,10.00,9.57)

      #### regresión con intercepto (int.reg)
      # Computo de AIC y Cp de Mallows para regresion con intercepto
      int.reg <- lm(y~x) ; anova(int.reg) ; summary(int.reg);
      print(sprintf('con intercept AIC=%f',AIC(int.reg)))
      MSE=(summary(int.reg)$sigma)^2 # guardar MSE para el modelo completo
      intercepto <- lm(y~1) # regresión incluye solo la constante del intercepto
      Cp.int.reg <- step(intercepto, scope = list(upper = int.reg), scale = MSE); Cp.int.reg

      #### regresión sin intercepto (noint.reg)
      # Computo de AIC y Cp de Mallows para regresion sin intercepto
      noint.reg <- lm(y~x+0); anova(noint.reg); summary(noint.reg);
      print(sprintf('sin intercept AIC=%f',AIC(noint.reg)))
      MSE=(summary(noint.reg)$sigma)^2
      intercepto <- lm(y~-1) # regresión no incluye intercepto
      Cp.noint.reg <- step(intercepto, scope = list(upper = noint.reg), scale = MSE); Cp.noint.reg

      Eliminar
    4. Este comentario ha sido eliminado por el autor.

      Eliminar
    5. Este comentario ha sido eliminado por el autor.

      Eliminar
  2. Gracias Edgardo, muy amable por tu respuesta.
    En cuanto a libro: estoy leyendo The R Book de Crawley y llegué a ese libro al leer la introducción a la estadística con R que está en el CRAN castellano.
    Felices fiestas de fin de año.
    ¡Saludos!

    ResponderEliminar
    Respuestas
    1. Excelente texto. Tienes muy buena referencia. Igualmente, disfruta las festividades de esta época. E.

      Eliminar