En primer lugar cargamos la librería que contiene base de datos Clothing así como el resto de librerías que necesitaremos para resolver los ejercicios
library(Ecdat)## Loading required package: Ecfun
##
## Attaching package: 'Ecfun'
## The following object is masked from 'package:base':
##
## sign
##
## Attaching package: 'Ecdat'
## The following object is masked from 'package:datasets':
##
## Orange
library(splines)
library(gam)## Loading required package: foreach
## Loaded gam 1.22
library(akima)Para que sea más fácil trabajar con la base de datos, aplicamos la instrucción attach()
attach(Clothing)Ahora vamos a revisar los nombres de la base de datos y las características principales
names(Clothing)## [1] "tsales" "sales" "margin" "nown" "nfull" "npart" "naux"
## [8] "hoursw" "hourspw" "inv1" "inv2" "ssize" "start"
summary(Clothing)## tsales sales margin nown
## Min. : 50000 Min. : 300 Min. :16.00 Min. : 1.000
## 1st Qu.: 495340 1st Qu.: 3904 1st Qu.:37.00 1st Qu.: 1.000
## Median : 694227 Median : 5279 Median :39.00 Median : 1.000
## Mean : 833584 Mean : 6335 Mean :38.77 Mean : 1.284
## 3rd Qu.: 976817 3rd Qu.: 7740 3rd Qu.:41.00 3rd Qu.: 1.295
## Max. :5000000 Max. :27000 Max. :66.00 Max. :10.000
## nfull npart naux hoursw
## Min. :1.000 Min. :1.000 Min. :1.000 Min. : 32.0
## 1st Qu.:1.923 1st Qu.:1.283 1st Qu.:1.333 1st Qu.: 80.0
## Median :1.956 Median :1.283 Median :1.367 Median :104.0
## Mean :2.069 Mean :1.566 Mean :1.390 Mean :121.1
## 3rd Qu.:2.066 3rd Qu.:2.000 3rd Qu.:1.367 3rd Qu.:145.2
## Max. :8.000 Max. :9.000 Max. :4.000 Max. :582.0
## hourspw inv1 inv2 ssize
## Min. : 5.708 Min. : 1000 Min. : 350 Min. : 16
## 1st Qu.:13.541 1st Qu.: 20000 1st Qu.: 10000 1st Qu.: 80
## Median :17.745 Median : 22207 Median : 22860 Median : 120
## Mean :18.955 Mean : 58257 Mean : 27829 Mean : 151
## 3rd Qu.:24.303 3rd Qu.: 62269 3rd Qu.: 22860 3rd Qu.: 190
## Max. :43.326 Max. :1500000 Max. :400000 Max. :1214
## start
## Min. :16.00
## 1st Qu.:37.00
## Median :40.00
## Mean :42.81
## 3rd Qu.:42.00
## Max. :90.00
Ejercicio: Una regresión polinómica de tercer grado que permita predecir el volumen de ventas anuales (tsales) en función de la inversión en locales comerciales (inv1).
Antes de comenzar el ejercicio, y para concer cual sería realmente el grado de polinomio a utilizar, veremos el test de hipotesis. Para ello ajustaremos todos los modleos que van hasta el polinomio de grado 5 y determinaremos el modelo más simple que sea sufiencite para explicar la relación entre tsales y inv. Para ello realizaremos un análisis de la varianza (ANOVA, utilizando un test F)
fit.1 <- lm(tsales ~ inv1, data = Clothing)
fit.2 <- lm(tsales ~ poly(inv1, 2), data = Clothing)
fit.3 <- lm(tsales ~ poly(inv1, 3), data = Clothing)
fit.4 <- lm(tsales ~ poly(inv1, 4), data = Clothing)
fit.5 <- lm(tsales ~ poly(inv1, 5), data = Clothing)
anova(fit.1, fit.2, fit.3, fit.4, fit.5)Como podemos observar, lo más eficiente sería usar un polinomio de grado 2, ya que el modelo cúbico 3 presenta un p-valor de 0.91.
En cualqueir caso, seguiremos las instrucciones del enunciado
En primer lugar, ajustamos el modelo utilizando la función lm utilizando el polinomio de tercer grado en inv1 (ploy(inv1,3).
fit <- lm(tsales ~ poly(inv1, 3), data = Clothing)
coef(summary(fit))## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 833584.21 28379.71 29.3725407 1.738756e-101
## poly(inv1, 3)1 2220675.50 567594.22 3.9124350 1.075213e-04
## poly(inv1, 3)2 -1822025.35 567594.22 -3.2100844 1.435174e-03
## poly(inv1, 3)3 -61555.13 567594.22 -0.1084492 9.136943e-01
Otra forma de ajustar el modelo sería la siguiente:
fit2 <- lm(tsales ~ inv1 + I(inv1^2) + I(inv1^3), data = Clothing)
coef(fit2)## (Intercept) inv1 I(inv1^2) I(inv1^3)
## 7.289332e+05 2.032759e+00 -6.156011e-07 -4.196202e-13
De una manera más compacta usando la función cbind()
fit2b <- lm(tsales ~ cbind(inv1, inv1^2, inv1^3), data = Clothing)
coef(fit2b)## (Intercept) cbind(inv1, inv1^2, inv1^3)inv1
## 7.289332e+05 2.032759e+00
## cbind(inv1, inv1^2, inv1^3) cbind(inv1, inv1^2, inv1^3)
## -6.156011e-07 -4.196202e-13
A continuación creamos una cuadrícula de valores para poder hacer las predicciones. Usarmeos la función habitual de predict() especificando que queremos saber los errores estándar (se = TRUE)
agelims <- range(inv1)
inv1.grid <- seq(from = agelims[1], to = agelims[2])
preds <- predict(fit, newdata = list(inv1 = inv1.grid), se = TRUE)
se.bands <- cbind(preds$fit + 2 * preds$se.fit, preds$fit - 2 * preds$se.fit)Finalmente visualizamos los datos y añadimos el ajuste polinómico de grado 3
plot(inv1, tsales, xlim = agelims, cex = .5, col = "darkgrey")
title("Regresión Polinómica de tercer grado")
lines(inv1.grid, preds$fit, lwd = 3, col = "blue")
matlines(inv1.grid, se.bands, lwd = 1, col = "red", lty = 3)Tal como observaremos a continuación, que se produzca o no un conjunto ortogan de funciones no afectará al modelo de manera significativa ya que los valores obtenidos son casi idénticos (diferencia = 1.389068e-06)
preds2 <- predict(fit2, newdata = list(inv1 = inv1.grid), se = TRUE)
max(abs(preds$fit - preds2$fit))## [1] 1.389068e-06
Ejercicio: Un modelo smooth spline que permita predecir el volumen de ventas anuales (tsales) en función de la inversión en automatización de procesos (inv2). Para ello, utilice un valor para λ=10, obtenga también el valor de λ por validación cruzada y represente gráficamente los resultados obtenidos.
En primer lugar, buscaremos los nudos ( Kknots ) que corresponden a los percentiles 25, 50 y 75 de la variable _inv_2
attr(bs(inv2 , df = 6), "knots")## 25% 50% 75%
## 10000.00 22859.85 22859.85
A modo de ejemplo, primero realizaremos un modelo spline
Para ajustar el modelo usamos la función bs. Como se observa en la gráfica, resulta poco natural, por lo que ya nos da una orientación de la necesidad de usar un modelo smooth spline
fit <- lm(tsales ~ bs(inv2, knots = c(10000.00, 22859.85, 22859.85 )), data = Clothing)
agelims <- range(inv2)
inv2.grid <- seq(from = agelims[1], to = agelims[2])
pred <- predict(fit, newdata = list(inv2 = inv2.grid), se = T)
plot(inv2, tsales, col = "gray")
title("modelo Spline")
lines(inv2.grid, pred$fit, lwd = 2, col = "blue")
lines(inv2.grid, pred$fit + 2 * pred$se, lty = "dashed", col = "red")
lines(inv2.grid, pred$fit - 2 * pred$se, lty = "dashed", col = "red")Para ajustar el modelo smooth spline usaremos la función smooth.splnie(). En este caso detemrinaremos que el valor λ conduzca a 10 grados de libertad ( df=10)
plot(inv2, tsales, xlim = agelims, cex = .5, col = "darkgrey")
title("Modelo Smooth Spline con λ = 10")
fit <- smooth.spline(inv2, tsales, df = 10)
lines(fit, col = "red", lwd = 2)
legend("topright", legend = c("10 DF"), col = c("red"), lty = 1, lwd = 2, cex = .8)A continuación calculamos seleccionamos el nivel de suavidad por validación cruzada. Esto nos resulta en un valor de λ que produce 2.79 grados de libertad
fit2 <- smooth.spline(inv2, tsales, cv = TRUE)## Warning in smooth.spline(inv2, tsales, cv = TRUE): cross-validation with
## non-unique 'x' values seems doubtful
fit2$df## [1] 2.791762
En el siguiente gráfico mostramos ambas opciones (λ = 10 en rojo y λ = 2.79 en azul)
plot(inv2, tsales, xlim = agelims, cex = .5, col = "darkgrey")
title("Modelo Smooth Spline con λ = 10 y λ = 2.79")
lines(fit, col = "red", lwd = 2)
lines(fit2, col = "blue", lwd = 2)
legend("topright", legend = c("10 DF", "2.79 DF"),
col = c("red", "blue"), lty = 1, lwd = 2, cex = .8)Ejercicio: Un modelo GAM que permita predecir el volumen de ventas anuales (tsales) en función del número total de horas trabajadas (hoursw), el número de trabajadores a tiempo completo (nfull) y a tiempo parcial (npart), y la superficie destinada a ventas (ssize).
En primer lugar ajustaremos el modelo pensandolo como un gram modelo de regresión lineal usando splines naturales. En este caso aplicaremos cuatro grados de libertad a cada parametro.
gam1 <- lm(tsales ~ ns(hoursw, 5) + ns(nfull, 5) +
ns(npart, 5) + ns(ssize, 5), data = Clothing)A continuación observamos el gráfico de las cuatro variables independientes propuestas. Como podemos observar, a pesar de ser una regresión lineal, la función plot.Gam() reconoce gam1 como de clse GAM a pesar de no serlo.
par(mfrow = c(1, 4))
plot.Gam(gam1 , se = TRUE , col = "red")A continuación ajustamos el modelo usando splines suavizados
gam.m3 <- gam(tsales ~ s(hoursw, 4) + s(nfull, 4) +
s(npart, 4) + s(ssize, 4), data = Clothing)Para verlo gráficamente usamos la función polt()
par(mfrow = c(1, 4))
plot(gam.m3, se = TRUE, col = "blue")Observamos que la variable hoursw puede ser lineal. Podemos realizar una serie de test ANOVA para para determinar cuál de estos tres modelos es el mejor: un GAM que excluye dicah variable hoursw (M1), un GAM que utiliza una función lineal de hoursw (M2), o un GAM que utiliza una función spline de hoursw (M3).
gam.m1 <- gam(tsales ~ s(nfull, 4) +
s(npart, 4) + s(ssize, 4), data = Clothing)
gam.m2 <- gam(tsales ~ hoursw + s(nfull, 4) +
s(npart, 4) + s(ssize, 4), data = Clothing)
anova(gam.m1, gam.m2, gam.m3, test = "F")Observamos que, si bien el modelo que utiliza una función lineal de hoursw (M2) es convincente, sí hay pruebas de que utilizar una función spline de hoursw (M3) es lo más adeucado.
Con la finción summary() podemos observar el ajuste de este GAM
summary(gam.m3)##
## Call: gam(formula = tsales ~ s(hoursw, 4) + s(nfull, 4) + s(npart,
## 4) + s(ssize, 4), data = Clothing)
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -965434 -163583 -41494 141512 1929643
##
## (Dispersion Parameter for gaussian family taken to be 96028468879)
##
## Null Deviance: 1.358316e+14 on 399 degrees of freedom
## Residual Deviance: 3.677893e+13 on 383.0002 degrees of freedom
## AIC: 11268.94
##
## Number of Local Scoring Iterations: NA
##
## Anova for Parametric Effects
## Df Sum Sq Mean Sq F value Pr(>F)
## s(hoursw, 4) 1 6.5460e+13 6.5460e+13 681.6724 < 2.2e-16 ***
## s(nfull, 4) 1 7.7740e+12 7.7740e+12 80.9551 < 2.2e-16 ***
## s(npart, 4) 1 1.2339e+12 1.2339e+12 12.8496 0.0003812 ***
## s(ssize, 4) 1 7.4996e+11 7.4996e+11 7.8098 0.0054577 **
## Residuals 383 3.6779e+13 9.6028e+10
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Anova for Nonparametric Effects
## Npar Df Npar F Pr(F)
## (Intercept)
## s(hoursw, 4) 3 7.8140 4.482e-05 ***
## s(nfull, 4) 3 22.0602 3.380e-13 ***
## s(npart, 4) 3 4.2314 0.005841 **
## s(ssize, 4) 3 11.7090 2.348e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Los p-valores del “Anova para efectos paramétricos” (Anova for Parametric Effects) demuestran claramente que hoursw, nfull, npart y ssize son altamente significativos desde el punto de vista estadístico.
Otra forma d eobservarlo sería la siguiente:
gam.lo <- gam(tsales ~ s(hoursw, 4) + s(nfull, 4) +
s(npart, 4) + s(ssize, 4), data = Clothing)
par(mfrow = c(1,4))
plot(gam.lo, se = TRUE, col = "green")Tal como hemos observado, las variables nfull y npart se comporetan de forma similar, por tanto podemos proponer un modelo en el cual interactuen entre ellas.
gam.lo.i <- gam(tsales ~ s(hoursw, 4) + lo(nfull, npart, span = 0.5) + s(ssize, 4), data = Clothing)
plot(gam.lo.i)## Warning in sqrt(out$se.fit[, TT]^2 + TS * object$var[, TT]): NaNs produced
summary(gam.lo.i)##
## Call: gam(formula = tsales ~ s(hoursw, 4) + lo(nfull, npart, span = 0.5) +
## s(ssize, 4), data = Clothing)
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -975490 -170309 -47497 157311 1648554
##
## (Dispersion Parameter for gaussian family taken to be 98096439583)
##
## Null Deviance: 1.358316e+14 on 399 degrees of freedom
## Residual Deviance: 3.749936e+13 on 382.2703 degrees of freedom
## AIC: 11278.16
##
## Number of Local Scoring Iterations: 1
##
## Anova for Parametric Effects
## Df Sum Sq Mean Sq F value Pr(>F)
## s(hoursw, 4) 1.00 5.9705e+13 5.9705e+13 608.637 < 2e-16 ***
## lo(nfull, npart, span = 0.5) 2.00 1.4438e+13 7.2188e+12 73.588 < 2e-16 ***
## s(ssize, 4) 1.00 3.6384e+11 3.6384e+11 3.709 0.05486 .
## Residuals 382.27 3.7499e+13 9.8096e+10
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Anova for Nonparametric Effects
## Npar Df Npar F Pr(F)
## (Intercept)
## s(hoursw, 4) 3.0 1.4917 0.2163
## lo(nfull, npart, span = 0.5) 6.7 16.4717 < 2.2e-16 ***
## s(ssize, 4) 3.0 13.5464 2.044e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
En este capítulo hemos usado diversas formas de estimar una regresión cuando el modelo no responde a una forma lineal. A nivel personal he de decir que he aprovechado para usar GAM en unos datos de una investigación mía (https://www.data-in-brief.com/article/S2352-3409(23)00135-X/fulltext) y he podido probar su funcionalidad, ya que al realizar en un primer momento una regresión líneal no obtuve relaciones significativas y al usar Splines suavizados sí