tidyverse
Este mini-curso presenta cómo estimar algunos de los tipos de modelos más comunes en la investigación utilizando el programa R, el entorno RStudio y los paquetes del tidyverse. Se centra en estrategias comunes para analizar la validez y los resultados de los modelos empleando técnicas de modelización generales.
tidyverse
R es un programa libre de uso general centrado en la estadística. Su potencia procede de los miles de paquetes creados por sus usuarios que añaden funcionalidad.
El curso del año pasado, R: Técnicas y herramientas para investigadores hizo una primera presentación sobre el proceso de análisis empírico con R utilizando el tidyverse (Wickham and Grolemund 2017Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. 1st ed. O’Reilly Media, Inc. https://r4ds.had.co.nz/.; Ginolhac, Koncina, and Krause 2017Ginolhac, Aurélien, Eric Koncina, and Roland Krause. 2017. Data Processing with R Tidyverse. University of Luxembourg. https://lsru.github.io/tv_course/.) y utilizando el entorno RStudio (RStudio Team 2018RStudio Team. 2018. RStudio: Integrated Development Environment for R. Boston, MA: RStudio, Inc. http://www.rstudio.com/.)
Para la instalación de R y Rstudio con la misma configuración que en las Aulas de Informática, seguid este enlace. Pero no utilicéis el fichero zip, que está ya anticuado. Utilizad el código que se especifica.
Uno de los elementos básicos del tidyverse es el uso del operador después.1 %>%, que se puede obtener con CTR+MAY+M en RStudio. Simplifica mucho el código porque pasa a leerse de forma secuencial:
datos %>% lee %>% cambia(nuevo=viejo+1) %>% muestra %>% ...
install.packages("*package*"). Los cargamos en nuestra sesión con library(*package*). Con este código podemos instalar todos los paquetes que utilizaremosinstall.packages(c("tidyverse","tidymodels","mosaic","mosaicModel","Rcmdr","gamlss","ggstatsplot","plotly",
"distreg.vis","bisoreg","colorspace","mgcv","mgcViz","gamlss.demo","gamlss.utils","bcaboot"),
dependencies= c("Depends", "Imports", "LinkingTo", "Suggests"))
install.packages("devtools")
devtools::install_github("ProjectMOSAIC/mosaicModel") # La versión en cran tiene errores
Fuente: (Wickham and Grolemund 2017Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. 1st ed. O’Reilly Media, Inc. https://r4ds.had.co.nz/.)
El proceso del análisis empírico queda resumido en el gráfico del margen. Las primeras fases consisten en la obtención de los datos y su modificación hasta que estén en la forma necesaria para la fase de modelización. La modelización propiamente dicha es un proceso iterativo a través del diagnóstico del modelo y la visualización. La fase final de comunicación es también clave y requiere generar documentos que transmitan el proceso de modelización. Nosotros nos vamos a concentrar en la parte central del proceso: la modelización, utilizando las herramientas de estimación contenidas en diferentes paquetes de R asi como paquetes que permiten analizar resultados de modelos diversos. En nuestro enfoque emplearemos los paquetes del tidyverse.
tidyverseLos objetos fundamentales para la modelización son los objetos de datos. En R hay varias clases asociadas: data.frame, tibble. Ambos son similares. Vamos a ver algunos de los objetos que emplearemos en nuestros ejemplos:
Un objeto de datos tiene siempre la misma estructura: Las columnas son variables, las filas son observaciones y cada celda es un dato. Para cada estructura de datos diferente, tenemos un objeto diferente.
Cada columna puede ser un objeto de tipo diferente. En este caso tenemos columnas numéricas (con números en doble precisión y enteros) y columnas con factores (variables cualitativas)
La tiabla (tibble) tiene la ventaja de que al imprimirse sólo se muestran la información básica de la estructura y las primeras filas
library(tidyverse)
library(gamlss.data)
data(rent99)
rent99 = rent99 %>% as_tibble
rent99
## # A tibble: 3,082 x 9
## rent rentsqm area yearc location bath kitchen cheating district
## <dbl> <dbl> <int> <dbl> <fct> <fct> <fct> <fct> <int>
## 1 110. 4.23 26 1918 2 0 0 0 916
## 2 243. 8.69 28 1918 2 0 0 1 813
## 3 262. 8.72 30 1918 1 0 0 1 611
## 4 106. 3.55 30 1918 2 0 0 0 2025
## 5 133. 4.45 30 1918 2 0 0 1 561
## 6 339. 11.3 30 1918 2 0 0 1 541
## 7 215. 6.94 31 1918 1 0 0 0 822
## 8 323. 10.4 31 1918 1 0 1 1 1713
## 9 216. 6.76 32 1918 1 0 0 0 1812
## 10 245. 7.43 33 1918 2 0 0 0 152
## # ... with 3,072 more rows
Datos sobre taquillaje de películas en los años 90. Incluyen el número de pantallas en que se mostró la primera semana y los ingresos de la primera semana, los ingresos tras la primera semana (nuestra variable de interés) y el tipo de distribuidora.
data(film90)
film90= film90 %>% as_tibble
film90
## # A tibble: 4,031 x 4
## lnosc lboopen lborev1 dist
## <dbl> <dbl> <dbl> <fct>
## 1 6.93 14.1 13.9 Independent
## 2 1.79 9.72 9.16 Independent
## 3 7.29 14.2 14.3 Independent
## 4 1.39 8.22 8.44 Independent
## 5 0 9.24 12.1 Independent
## 6 0 7.92 10.7 Independent
## 7 1.39 7.56 9.38 Independent
## 8 1.95 9.03 10.5 Independent
## 9 0 6.64 7.12 Independent
## 10 0 8.87 11.8 Independent
## # ... with 4,021 more rows
La siguiente tabla resume los paquetes principales para cargar datos en R:
| Procedencia | ¿Cómo leerlos? | Ejemplo |
|---|---|---|
| Datos R | load;readRDS | load("a.RData"), a=readRDS("a.RDS") |
| Un paquete de R | library(lib);data(df) |
data(cars) |
| Un fichero: | Paquetes: | |
| csv,fwf,txt | readr |
read_table("data.txt") |
| SPSS,SAS,… | haven |
read_sav("data.sav") |
| xls, xlsx | readxl |
read_excel("data.xls",skip=10) |
| Cualquiera | rio |
import("data.csv") |
| pcAxis (INE) | pxR |
read.px("data.px") |
| Una página web: | Paquetes: | |
| Tablas html | htmltab |
htmltab(url) |
| html | rvest |
read_html(url) |
| JSON | jsonlite |
fromJSON(url) |
gather
spread
separate
Para manejar los datos una vez están en R, las herramientas claves son los paquetes tidyr y dplyr que forman parte del tidyverse.
tidyr lo empleamos cuando queremos cambiar la estructura de los datos para llevarlos a la forma ordenada: que las columnas se correspondan con las variables de nuestro análisis. Las tres funciones principales de tidyr son las siguientes:
| Función | Utilidad | Argumentos |
|---|---|---|
gather |
Pasar de forma ancha a larga | key,value,cols |
spread |
Pasar de forma larga a ancha | key,value |
separate |
Separar una columna en varias | col, into, sep |
Estas funciones son muy útiles también para el manejo de los resultados o la pre-preparación de gráficos.
El tidyverse incluye numerosas funciones para manipular objetos de datos. Recordamos las principales funciones del paquete dplyr. La filosofia del tidyverse para manipular datos es aplicar de forma sucesiva las funciones elementales que sean necesarias separadas con el operador después %>%.
| Función | Utilidad | Argumentos |
|---|---|---|
select |
Selecciona variables | a:x, -var1, ... |
filter |
Selecciona observaciones | V1>5, !is.na(V2),... |
mutate |
Muta variables | ratio=V1/V2 |
rename |
Renombra variables | año = fecha, … |
arrange |
Ordena observaciones | desc(V2), V1, ... |
summarize |
Calcula resúmenes | media=mean(V1),max=max(V1), … |
En la selección de observaciones con filter tenemos que emplear condiciones lógicas como las siguientes:
group_by y summarize según Ismay and Kim (2018Ismay, Chester, and Albert Y. Kim. 2018. ModernDive: An Introduction to Statistical and Data Sciences via R. moderndive.com. https://moderndive.com/.)
| Operador | Descripción |
|---|---|
< |
menor |
<= |
menor o igual |
> |
mayor |
>= |
mayor o igual |
== |
exactamente igual |
%in% |
está entre |
!= |
distinto |
!x |
Lo contrario de |
x|y |
x O y |
x & y |
x E y |
is.na() |
¿es NA? |
!is.na() |
Distinto de NA |
Los tipos de
join en Wickham and Grolemund (2017Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. 1st ed. O’Reilly Media, Inc. https://r4ds.had.co.nz/.)
La potencia de estas funciones tan básicas se deriva de la combinación con la declaración de grupos mediante group_by. group_by declara que la tabla está compuesta por distintas subtablas definidas a partir de la variable o variables de grupo. Después de declarados los grupos, tanto mutate como arrange o summarize operan dentro de cada subtabla. En particular, summarize cambia la estructura de los datos calculando una fila para cada combinación de las variables de grupo.
Por último, una última operación de gran utilidad la constituyen los joins. Mediante los joins, y en particular el left_join: estos permiten pegar a nuestro objeto de datos variables que procedan de otro objeto relacionado. Los requisitos para que funcione bien es que las variables relacionadas tengan el mismo nombre (se pueden renombrar antes del join) y que contengan información del mismo tipo (ejemplo: numérica, factores, …)
Podéis encontrar más información sobre manejo de datos en el tidyverse, con ejemplos, en este enlace de José Antonio Ortega o, mucho más detallado, en Ginolhac, Koncina, and Krause (2017Ginolhac, Aurélien, Eric Koncina, and Roland Krause. 2017. Data Processing with R Tidyverse. University of Luxembourg. https://lsru.github.io/tv_course/.). Para una introducción rápida podéis consultar esta simpática presentación de Omaima Said. El texto de Kaplan and Shaw (2016Kaplan, Daniel, and Frank Shaw. 2016. Statistical Modeling: Computational Technique. mosaic-web.org. http://mosaic-web.org/go/SM2-technique/.) proporciona una muy buena introducción a R orientada hacia la modelización utilizando el paquete mosaic
ggplot2La herramienta básica para visualización con el tidyverse es el paquete ggplot2. Los gráficos ggplot2 están compuestos por distintas capas separadas por +. La información necesaria mínima para un gráfico es:
data, primer argumento.aes, del gráfico: Qué representan los ejes, para qué utilizo el color, …geom, que añado al gráfico. Separados por +
Ingresos de las películas tras la primera semana en función de los ingresos de la primera semana (en logaritmos). Aparece superpuesta la estimación de un modelo de regresión no paramétrico
gam
film90 %>% ggplot(aes(y=lborev1,x=lboopen)) +
geom_point() +
geom_smooth()
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
Del gráfico básico anterior puedo pasar a uno más completo cambiando lo que desée del aspecto del gráfico. En particular, es posible establecer para cada capa geométrica parámetros como el tamaño, el color, el tipo de línea o la transparencia que pueden asociarse a variables dentro de aes() o ser establecidos de manera directa fuera de aes(). Las declaraciones de ggplot() son globales, las de cada geom_xxx() son locales.
film90 %>% ggplot(aes(y=lborev1,x=lboopen)) +
geom_point(aes(color=dist,size=lnosc),alpha=0.3) +
geom_smooth() +
labs(color="Productor",
y="log de ingresos resto",
x="log de ingresos en primera semana",
size="log de número de pantallas") +
theme_classic(20) +
theme(legend.position="bottom",legend.box="vertical")
Ingresos de las películas de los 90 utilizando las posibilidades de ggplot: Área proporcional al número de pantallas (log), y color asociado al tipo de productor.
plotly::ggplotly()
Gráfico interactivo plotly que se obtiene directamente a partir del ggplot anterior.
Como referencia podemos consultar la chuleta de RStudio sobre ggplot, o las secciones correspondientes de los libros online de Wickham and Grolemund (2017Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. 1st ed. O’Reilly Media, Inc. https://r4ds.had.co.nz/.), Ismay and Kim (2018Ismay, Chester, and Albert Y. Kim. 2018. ModernDive: An Introduction to Statistical and Data Sciences via R. moderndive.com. https://moderndive.com/.) o Anderson and Severson (2018Anderson, Brooke, and Rachel Severson. 2018. R Programming for Research. Colorado State University. https://geanders.github.io/RProgrammingForResearch/.). Para un análisis más detallado se puede consultar Healy (2018Healy, Kieran. 2018. Data Visualization: A Practical Introduction. Forthcoming, Princeton University Press. https://socviz.co/.) o esta presentación muy completa de Ginolhac, Koncina, and Krause (2017).
R nos ofrece la posibilidad de estimar cualquier tipo de modelo, bien a través de las funciones de R base, como lm para modelos lineales y glm para modelos lineales generalizados, o a través de la funcionalidad añadida por los miles de paquetes de los disponibles en CRAN. Además de los paquetes “oficiales” de cran existen otros muchos disponibles en GitHub y otros repositorios. Podemos buscar la documentación de todos ellos en rdrr.
En este mini-curso vamos a centrarnos en algunos ejemplos que cubren desde lo más sencillo, como modelos para una variable cuantitativa, a modelos muy generales que acomodan “casi” todos los modelos como caso particular: gamlss.
El modelo típico se puede escribir como \(y=f(X)+\varepsilon\). Nuestro problema es estimar a partir de los datos {y_i,X_i} la función \(f(X)=E(y/x)\). Al estimador le llamaremos \(\hat{f}(X)\). Dependiendo del tipo de modelo (paramétrico, semiparamétrico, no paramétrico), la función \(\hat{f}\) será seleccionada de entre la familia de funciones que componen el modelo correspondiente.
En todos los casos nos vamos a centrar en el mismo problema: Hay una variable explicada, que llamaremos \(y\), que puede ser una variable cuantitativa, binaria o categórica, y una serie de variables explicativas (o regresores o covariables) que llamaremos \(x_j\). Nuestras observaciones proceden de una muestra de tamaño \(n\), y cada una de ellas la podremos escribir como
\[\{y_i,x_{i1},x_{i2},...,x_{ik}\} = \{y_i,X_i\}\]
Desde el punto de vista de la estimación en R la estimación es muy sencilla: Tenemos que saber la función que calcula el estimador, la fórmula que especifica el modelo, el objeto de datos en el que estimamos, y, en su caso, los argumentos extra que puedan controlar la estimación. Este es un ejemplo típico: mod = estimador(y ~ x1 + x2, data=misdatos) Para cada tipo de modelo están definidas una serie de funciones genéricas para trabajar con el modelo. Las más frecuentes son summary, predict, plot o residuals.
En todos los casos nuestro objetivo es captar la relación funcional existente entre la variable \(y\) y las variables \(x\) de modo que deseamos recuperar una función \(y=f(X)\) que las relaciona. Nos centraremos sobre todo en modelos predictivos, que intentan captar la parte sistemática de la relación entre las variables. Esto llevará en una primera aproximación a identificar \(f(X)=E(y/X)\), es decir, a captar la esperanza condicionada de la variable. También veremos extensiones que se proponen modelizar otros momentos de la distribución. De hecho, los modelos más generales que veremos, los gamlsss, Generalised Additive Models for Location Scale and Shape, que permiten estimar funciones separadas para los distintos momentos (esperanza, \(\mu\); desviación típica, \(\sigma\); asimetría, \(\tau\), y kurtosis, \(\nu\)) (Stasinopoulos et al. 2017Stasinopoulos, Mikis D., R. A. Rigby, G. Z. Heller, V. Voudouris, and F. D. Bastiani. 2017. Flexible Regression and Smoothing : Using GAMLSS in R. R. Chapman; Hall/CRC. https://www.crcpress.com/Flexible-Regression-and-Smoothing-Using-GAMLSS-in-R/Stasinopoulos-Rigby-Heller-Voudouris-Bastiani/p/book/9781138197909.; Rigby et al. 2018Rigby, R. A., Mikis D. Stasinopoulos, G. Z. Heller, and F. D. Bastiani. 2018. Distributions for Modelling Location, Scale and Shape: Using GAMLSS in R. R. Manuscript draft. https://www.gamlss.com/wp-content/uploads/2018/01/DistributionsForModellingLocationScaleandShape.pdf.). La evolución ha ido en la dirección de permitir utilizar funciones más complejas y de hacer menos supuestos a priori sobre la distribución.
| Modelo | Aporta | Referencia | Función | Paquete |
|---|---|---|---|---|
| Lineales | Relaciones lineales | Fox and Weisberg (2019), Weisberg (2014) | lm |
base |
| Regresión no paramétrica | Formas funcionales flexibles | Shalizi (2018) | npreg,loess |
np,base |
| Árboles de regresión | Adaptables a cambios bruscos | James et al. (2014),QuickR | rpart,randomForest |
rpart,randomForest |
| Aditivo Generalizado (gam) | Funciones suaves+glm | Clark (2018a) Wood (2017) | gam |
mgcv |
| Lineales Mixtos | Fuentes múltiples de variación | Clark (2018b) | lme,lmer |
lme4,nlme |
| lasso | Selección de variables | James et al. (2014) | lasso,glmnet |
lars,parsnip |
| gamlss | Modeliza momentos, cualquier distribución | Stasinopoulos et al. (2017) | gamlss |
gamlss |
Vamos ahora a ver un cuadro similar para técnicas de modelización que no vamos a ver que son de uso más común en economía. Existen varios libros sobre econometría con R como Kleiber and Zeileis (2008Kleiber, Christian, and Achim Zeileis. 2008. Applied Econometrics with R. Springer. doi:10.1007/978-0-387-77318-6.), Hanck et al. (2018Hanck, Christoph, Martin Arnold, Alexander Gerber, and Martin Schmelzer. 2018. Introduction to Econometrics with R. University of Duisburg-Essen. http://www.econometrics-with-r.org/.), Colonescu (2018Colonescu, Constantin. 2018. Principles of Econometrics with R. 2nd ed. CreateSpace Independent Publishing Platform. https://bookdown.org/ccolonescu/RPoE4/.), o, sobre predicción y análisis de series temporales, Hyndman and Athanasopoulos (2018Hyndman, Rob J, and George Athanasopoulos. 2018. Forecasting: Principles and Practice. OTexts. https://otexts.org/fpp2/.). James et al. (2014James, Gareth, Daniela Witten, Trevor Hastie, and Robert Tibshirani. 2014. An Introduction to Statistical Learning: With Applications in R. Springer Publishing Company, Incorporated. http://www-bcf.usc.edu/~gareth/ISL/.), y su hermano mayor Hastie, Tibshirani, and Friedman (2009Hastie, Trevor, Robert Tibshirani, and Jerome Friedman. 2009. The Elements of Statistical Learning: Data Mining, Inference, and Prediction. Springer. https://web.stanford.edu/~hastie/ElemStatLearn/.), proporcionan una excelente introducción a las técnicas de aprendizaje automático que cubren muchos más modelos de los que veremos.
Diferencias entre R y otros programas estadísticos
Aquellos de vosotros que conocéis otros paquetes estadísticos, podéis beneficiaros de conocer las diferencias de enfoque con R. Estos enlaces las resumen para SPSS, para Stata, matlab y para excel. Este habla de algunas de las diferencias con Eviews. Este habla sobre los motivos para usar R
| Modelo | Aporta | Referencia | Función | Paquete |
|---|---|---|---|---|
| Lineal generalizado (glm) | Binomial, poisson, … | Faraway (2016) | glm |
base |
| Variables instrumentales | Cuando fallan otros estimadores | Hanck et al. (2018) | ivreg |
AER |
| Regresión de cuantiles | Foco en la distribución, no en la media | Ford(2018) | rq |
quantreg |
| Sistemas de ecuaciones | Relaciones bidireccionales | Colonescu (2018) | systemfit |
systemfit |
| Datos de panel | Estructura especial de datos | Hanck et al. (2018) | plm |
plm |
| Series temporales | Dependencia temporal | Hyndman and Athanasopoulos (2018) | ets,Arima |
forecast |
Hay que decir que algunos de los métodos anteriores se pueden ver como un caso particular de los métodos que vamos a tratar. Es el caso de los datos de panel, que son un caso particular de modelos lineales mixtos, o la regresión de cuantiles, que puede tratarse también como un gamlss: la diferencia está entre modelar directamente el cuantil de interés, o la distribución en su conjunto. También hay clases, como los glm, que incluyen muchos modelos como subclases (lm (family=gaussian()),logit, probit, cloglog (family=binomial()),regresión de poisson (family=poisson()), …). Los glm a su vez están integrados dentro de los gam y de los gamlss. Una referencia muy completa sobre las alternativas de modelización existentes con R es Clark (2018cClark, Michael. 2018c. R Models, Quick Reference. University of Michigan. https://m-clark.github.io/R-models/.). Una referencia seminal sobre el desarrollo y la lógica del nuevo enfoque de modelización es Breiman (2001Breiman, Leo. 2001. “Statistical Modeling: The Two Cultures (with Comments and a Rejoinder by the Author).” Statistical Science 16 (3): 199–231. doi:10.1214/ss/1009213726.).
Por último, y precisamente debido a la infinita variedad de paquetes existentes, cran ha organizado task views, listas curadas y documentadas de los paquetes de mayor uso dentro de distintos campos de conocimiento. Son un buen lugar para comenzar a buscar cuando los paquetes que hemos visto no se adaptan a nuestras necesidades, o para fases del análisis empírico distintas de la modelización. Estos son algunas de las vistas temáticas que pueden ser más útiles:
| Tema | task view |
|---|---|
| Econometría | Econometrics |
| Finanzas | Finance |
| Series temporales | TimeSeries |
| Aprendizaje automático | MachineLearning |
| Ciencias Sociales | SocialSciences |
| Estadística oficial | OfficialStatistics |
| Análisis de supervivencia | Survival |
| Análisis espacial | Spatial |
Para introducirnos en la modelización vamos a centrarnos ahora en modelos de la relación entre dos únicas variables. La teoría se puede encontrar más desarrollada en James et al. (2014James, Gareth, Daniela Witten, Trevor Hastie, and Robert Tibshirani. 2014. An Introduction to Statistical Learning: With Applications in R. Springer Publishing Company, Incorporated. http://www-bcf.usc.edu/~gareth/ISL/.) y Shalizi (2018Shalizi, Cosma Rohilla. 2018. Advanced Data Analysis from an Elementary Point of View. Forthcoming, Cambridge University Press. http://www.stat.cmu.edu/~cshalizi/ADAfaEPoV/.). Es extensible al caso en que \(x\) sea un vector sin más que cambiar \(x\) por \(X\).
La función \(f(x)\) capta la parte sistemática de \(y\), mientras que el término de error o componente no observado, \(\varepsilon\), capta la parte no explicada por el modelo.
\[y= f(x) + \varepsilon\]
Ajuste de un modelo dado por \(\hat{f}\) y residuos como distancia vertical a la función. La suma de cuadrados de los residuos es proporcional a las áreas de los puntos, cuyo radio es proporcional al residuo.
Supuesto básico: Esperanza condicionada nula \(E(\varepsilon/x)=0\)
Desde el punto de vista de la estimación, en todos los casos tendremos una función de R que escoge de entre qué familia de funciones escogemos en el ajuste.
Ajuste: Se basa en optimizar una función objetivo. La más común es la suma de cuadrados de los residuos que da lugar a los estimadores mínimo cuadráticos.
Valor ajustado y predicción: La función resultante \(\hat{f}\) me define, cuando la utilizo dentro de la muestra, el valor ajustado, \(\hat{y}\), y cuando la utilizo para observaciones fuera de la muestra, la predicción puntual.
\[\hat{y}=\hat{f}(x)\]
Error reducible e irreducible: Otra forma de ver el término de error es como componente no reducible de error en la predicción. En la medida en que mi método de estimación sea adecuado y se adapte a la naturaleza del problema puedo aspirar a que mi función \(\hat{f}\) se aproxime a la verdadera función \(f\). Llamamos error reducible a la relación \(f-\hat{f}\) que determina en qué medida mi estimación está sesgada. Si consigo reducir el sesgo conseguiré predecir mejor, pero no acertaré debido a la variabilidad que tiene la \(y\) para un valor dado de la \(x\). Este término de error no es reducible utilizando sólo la información de la variable \(x\), por ello lo llamamos error no reducible. Podemos conseguir reducir el error si existe información relevante fuera del modelo que se puede utilizar. Esto implica cambiar de modelo.
Residuos: El residuo es el equivalente muestral del término de error, \(e=y-\hat{f}(x)\). Puede contener dos partes: una sistemática asociada a los sesgos en que hemos incurrido al estimar, \(f-\hat{f}\), y otra aleatoria asociada al término de error, \(\varepsilon\).
Error cuadrático medio: Supongamos que utilizamos una función \(\hat{f}\) fija para estimar, que no es la verdadera \(f\). El valor esperado de los errores de estimación al cuadrado se puede descomponer como
\[ECM_{\hat{f}}(x)=E_{\hat{f}}(y - \hat{f}(x))^2 = [f(x) - \hat{f}(x)]^2 + Var_x(\varepsilon)= \text{Sesgo}^2+ Varianza\]
Conflicto sesgo-varianza en la estimación según Fortmann-Roe(2012).
\[ECM(x)=\left(f(x)-E[\hat{f}(x)]\right)^2 + Var\left(\hat{f}(x)\right) + Var_x(\varepsilon)\] \[(\text{Sesgo de la predicción})^2+ \text{Varianza de la predicción} + \text{Error irreducible}\]
Complejidad del modelo, subajuste y sobreajuste en un ejemplo Fuente.
De este modo tenemos que lo bien que puede funcionar nuestro modelo se topa con tres limitaciones:
¿Cómo tiran los dardos el que sobreajusta y el que subajusta? Fuente.
Con modelos más complejos podemos reducir el sesgo, pero el coste es, en general, aumentar la varianza del estimador.
Para cada problema habrá un grado de complejidad óptimo. Si la relación entre las variables es aproximadamente lineal, los modelos lineales serán aproximaciones difíciles de mejorar, pero si la relación no es lineal, habrá otros estimadores más complejos que serán mejores, porque el modelo lineal será sesgado.
Sobreajuste y complejidad del modelo según Fortmann-Roe(2012).
Vamos a centrarnos en un modelo para los datos de ingresos de las películas tras la primera semana utilizando como única variable los ingresos de la primera semana. En este caso los datos ya están preparados para poder estimar los modelos. Por razones de conveniencia y claridad, únicamente renombraremos las variables: lboopen, el logaritmo de la taquilla de la primera semana, como taq0, y lborev1, el logaritmo del taquillaje en el resto de semanas como taq1.
film90 = film90 %>% rename(taq0=lboopen,taq1=lborev1)
Desde el punto de vista del comportamiento predictivo tenemos dos pasos que hacer con cada modelo: estimación y predicción. Para comparar inicialmente cómo funcionan los modelos emplearemos la función predict. La función predict es una función genérica de R que generalmente toma como primer argumento el modelo y como segundo, opcional, un objeto de datos en el que predecir. A menudo también es posible obtener intervalos de predicción analíticos o errores estándar de predicción analiticos especificando los argumentos adecuados.
Las funciones genéricas de R examinan la clase del objeto del primer argumento. La clase se puede obtener como class(modelo). Por ejemplo, para modelos lineales es lm. Esto implica que la función que realmente se ejecuta es predict.lm. Para cada tipo de modelos existirá la función predict.estimador correspondiente. Una de las partes desafortunadas es que los argumentos y los resultados de la función pueden ser distintos dependiendo del modelo.
mxxx = estimador(lborev1~lboopen, data=film90)summary(mxxx)predict(mxxx,tibble()) o con la función de mosaic, mod_fun(modelo)lmpoly(var,k) añade un polinomio de orden k para la variable var. Por defecto los polinomios se parametrizan utilizando una base ortogonal. Si se desea utilizar el polinomio con la fórmula habitual especificamos poly(var,k,raw=TRUE). ns(var,df), del paquete splines, añade splines cúbicos naturales. bs(var,df,degree=k) permite añadir splines de distinto orden. Es posible controlar la posición de los nodos con knots y Boundary.knots
## Estimación de modelos
# Paramétricos lineales
ml=lm(taq1~taq0,film90) # lineal
mp2=lm(taq1~poly(taq0,2),film90) #cuadrático
mp3=lm(taq1~poly(taq0,3),film90) #cúbico
mp4=lm(taq1~poly(taq0,4),film90) #pol. grado 4º
mp6=lm(taq1~poly(taq0,6),film90) #pol. grado 6º
mp8=lm(taq1~poly(taq0,8),film90) #pol. grado 8º
library(splines)
msl=lm(taq1~bs(taq0,df=4,degree=1),film90)
msc5=lm(taq1~ns(taq0,5),film90) #Splines cúbicos
msc6=lm(taq1~ns(taq0,6),film90) # con más grados de libertad
msc7=lm(taq1~ns(taq0,7),film90)
msc8=lm(taq1~ns(taq0,8),film90)
Los
splines definen funciones polinomiales locales en tramos definidos por los nodos. Desde el punto de vista de la estimación son modelos lineales donde hay que estimar el peso de cada elemento de la base. En el diagrama la base de splines del modelo msc6
Todos los modelos estimados son paramétricos y lineales en los parámetros. Por ello se han estimado todos con lm. Que los modelos son lineales en los parámetros quiere decir que la función de predicción consiste en una combinación lineal de funciones que se pueden calcular con los datos, no que el modelo sea lineal en la variable. Únicamente sería lineal el ml según ese criterio. En lo que difieren los modelos estimados es en su complejidad. La complejidad aumenta con el orden del polinomio o con los grados de libertad de los splines.
Podemos “ver” las estimaciones de uno de estos modelos. Los gráficos de diagnóstico en dos formatos, y el ajuste del modelo msc6 comparado con uno de complejidad similar, el mp6, y con el modelo lineal. Para esto último utilizamos una función del paquete mosaic que es más cómoda que predict en este contexto: mod_fun(modelo) da como resultado una función que evalua la predicción del modelo para los valores de las x que se especifiquen.
library(mosaic); library(mosaicModel)
film90 %>% ggplot(aes(y=taq1,x=taq0))+ geom_point(size=0.2) +
geom_smooth(aes(color="ml"),method="lm",se=FALSE,size=1.2) +
geom_line(aes(y=model_output,color="mp6"),size=1.2,data=mod_fun(mp6)(taq0=seq(4,18,by=.1))) +
geom_line(aes(y=model_output,color="msc6"),size=1.2,data=mod_fun(msc6)(taq0=seq(4,18,by=.1))) +
labs(color="Modelo")+
theme_bw(18)
En este caso podemos ver como el modelo lineal es un caso típico de subajuste: no es capaz de adaptarse a la naturaleza de los datos y será un estimador sesgado. El modelo polinómico, por su parte, presenta síntomas de sobreajuste e inestabilidad, como puede ser la caída fuerte para valores muy altos. Esto ocurre porque al tratarse de un ajuste global, y de haber pocos puntos en la zona alta, al modelo “no le importa” que pasa con esos puntos si puede mejorar el ajuste en otra región. El modelo de splines parece adaptarse a los datos sin que se observe sobreajuste.
Gráficos de diagnóstico por defecto, con R base.
Gráficos de diagnóstico por defecto, con R base.
Gráficos de diagnóstico por defecto, con R base.
Gráficos de diagnóstico por defecto, con R base.
plot(msc6)
summary(msc6)
##
## Call:
## lm(formula = taq1 ~ ns(taq0, 6), data = film90)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.4842 -0.7057 -0.0786 0.6799 7.8536
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.5235 0.3315 13.64 <2e-16 ***
## ns(taq0, 6)1 8.0214 0.3094 25.92 <2e-16 ***
## ns(taq0, 6)2 10.3332 0.3856 26.80 <2e-16 ***
## ns(taq0, 6)3 8.9515 0.3458 25.89 <2e-16 ***
## ns(taq0, 6)4 11.6965 0.2199 53.20 <2e-16 ***
## ns(taq0, 6)5 16.6452 0.7248 22.97 <2e-16 ***
## ns(taq0, 6)6 12.8291 0.3184 40.30 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.397 on 4024 degrees of freedom
## Multiple R-squared: 0.8161, Adjusted R-squared: 0.8159
## F-statistic: 2977 on 6 and 4024 DF, p-value: < 2.2e-16
Vemos que el ajuste dentro de la muestra es muy bueno, de un 81.6135444% de la variabilidad de la y. Por otro lado, la alta significatividad de los parámetros estimados es también síntoma de que no estamos sobreajustando. En todo caso podríamos utilizar modelos más complejos.
library(ggfortify)
autoplot(msc6,size=.2) + theme_bw(14)
Gráficos de diagnóstico ggplot con ggfortify::autoplot
Las gráficas de diagnóstico nos muestran que capta bastante bien la forma funcional sin que se detecten no linealidades en el gráfico de residuos frente a valores ajustados. Esta es la hipótesis más fundamental del modelo. Sin embargo, se detectan problemas en los otros tres gráficos:
El gráfico qqplot (normal Q-Q) indica que la distribución del término de error dista bastante de la normal. En particular tiene colas anchas tanto en la parte alta como baja de la distribución. Esto afecta también a los intervalos de predicción que no tendrían el nivel nominal si se han construido para una normal.
El gráfico scale-location sirve para detectar heterocedasticidad. En este caso se ve qu hay un nivel (en torno a 14) en que cambia la varianza. Las implicaciones no son en principio graves: no afecta a la consistencia o insesgadez de los estimadores, pero exige que si queremos realizar inferencia sobre los parámetros deberíamos utilizar un estimador robusto frente a heterocedasticidad. La fórmula por defecto no es válida. También implica que las bandas de predicción puedan calcularse mal cuando se utilice predict. Por otro lado, si combinamos la información del qqplot con ésta y el gráfico del modelo, podríamos decir que el problema es que hay dos tipos de películas de entre las que hicieron una taquilla media la primera semana: algunas que consiguen mejorar y hacer taquillajes estupendos, y otras que se quedan más abajo. Esto no ocurre cuando taq0 es muy alta o baja. El problema, visto de esta manera, es que la distribución de la variable es distinta para distintos niveles de taq0. lm, aunque pueda funcionar cuando la distribución no es normal o no es homogénea, deja de ser el mejor estimador posible, y es posible que existan estimadores mejores que se adapten a esta distribución complicada.
El gráfico del apalancamiento frente a los residuos. nos muestra que hay ciertas observaciones influyentes, y otras con residuos estandarizados muy elevados, por encima de 6.
Los paquetes mosaic y mosaicModel tienen herramientas útiles para visualizar deprisa el ajuste de un modelo. Hay alternativas más sofisticadas que veremos como visreg, LinRegInteractive o ggeffects:
modelPlot(modelo): Gráfico de los puntos y del ajuste. Sólo para modelos lineales y glm.plot_mod(): Gráfico del ajuste del modelo. Se le pueden añadir capas ggplot con +.
Gráficos de modelo con
mosaic y mosaicModel
plotModel(msc8)
Gráficos de modelo con
mosaic y mosaicModel
mod_plot(msc8)
Gráficos de modelo con
mosaic y mosaicModel
mod_plot(msc8) +
geom_point(aes(y=taq1),data=film90,color="orange",alpha=0.4,size=0.4) +
labs(title="Modelo de splines cúbicos `msc8`") + theme_dark(20)
Trabajamos exactamente igual con los estimadores paramétricos que con los no paramétricos. La diferencia es que no tenemos que especificar la forma de la función, sino que ésta será una función flexible, en general suave. Por ello a menudo se llama a estos estimadores smoothers. La mayor parte de los estimadores no paramétricos se basan en captar la esperanza condicionada de manera local. Para ello dan unos pesos a las observaciones más cercanas al valor de la x para el que queremos predecir, y esto lo hacen para todos los valores de las x.
Es decir: la función objetivo tiene que optimizarse PARA CADA PUNTO de \(\hat{f}(x)\).
\[\hat{f}(x) = argmin \sum_{i=1}^{n}\omega_{i}(x) (y_{i} - \tilde{f}(x))^2\]
Esto los hace computacionalmente complejos, pero conceptualmente simples. Muchos de ellos son, además, estimadores lineales: se pueden escribir como:
\[\hat{f}(x) = \sum_{i=1}^{n}w_{i}(x_i) y_{i}\]
Ejemplo de pesos en la regresión cuadrática local
Ajuste loess lineal en un punto
En cada método hay un parámetro que gobierna cómo de local definimos los intervalos:
| Método | Función R | Paquete | Parámetro | Peso w | Peso \(\omega\) |
|---|---|---|---|---|---|
| k-vecinos | knn.reg |
FNN | k |
\(\frac{1}{k}\) para k vecinos, \(0\) resto | \(1\) para k vecinos, 0 resto |
| Kernel | ksmooth |
R base | \(h\), bandwidth |
\(K(x_i,X)\) suave | \(K(x_i,X)\) suave |
| Loess Lineal | loess |
R base | degree=1,span |
MCP: prod de \(K(x_i,X)\) y pesos MLS | Pesos \(h\) de est MCP del MLS |
| Loess cuadrático | loess |
R base | degree=2,span |
MCP: prod de \(K(x_i,X)\) y pesos ML cuadrático | Pesos \(h\) de est MCP de mod cuadr |
| Regr. kernel | npreg |
np | bws |
Variedad de métodos | Variedad de métodos |
Una diferencia importante está entre los estimadores de media local y de regresión local. Los estimadores de media local, como knn o kernel, se basan en estimar medias ponderadas locales, y en los extremos tienden a ser más planos. Los estimadores de regresión local, como loess se basan en regresiones lineales o cuadráticas locales. En los extremos se comportan como la función de regresión local (una recta, una parábola).
Todos los métodos tienen al menos un parámetros que determina cómo de local es el entorno. Pueden denominarse ancho de banda o nombre parecidos. En el caso de loess, span implica qué proporción de los puntos globales se utiliza en la estimación. Normalmente los algoritmos también dependen de otra elección respecto a qué tipo de función se utiliza para caracterizar el decaimiento en los entornos locales.
Ajuste de modelos
loess. Los dos son bastante similares salvo en los bordes
mloess1=loess(taq1~taq0,data=film90,span=0.1)
mloess2=loess(taq1~taq0,data=film90,span=0.5)
tibble(taq0=seq(4.4,18,by=0.2)) %>%
mutate(mloess1=predict(mloess1,.),
mloess2=predict(mloess2,.)) %>%
ggplot(aes(x=taq0))+
geom_point(aes(y=taq1),color="grey80",size=0.4,alpha=0.4,data=film90)+
geom_line(aes(y=mloess1,color="mloess1"))+
geom_line(aes(y=mloess2,color="mloess2"))+
labs(title="Modelos loess con `span` 0.1 y 0.50",color="Modelo")+
theme_bw(20)
El paquete np permite definir distintos tipos de regresión local, basadas tanto en medias locales como regresiones locales, y escoge los hiperparámetros por validación cruzada, es decir: comprobando cuáles predicen mejor fuera de la muestra. El coste es que es más caro computacionalmente. Aquí mostramos el resultado de ajustar en una muestra de 1000 películas. Pudieramos estar sobreajustando. Por defecto utiliza un procedimiento de media local (local constant), que se corresponde con regtype="lc". Se puede cambiar a regresión lineal local (local-linear) con regtype="ll".
library(np)
mkernel = npregbw(taq1~taq0,data=film90 %>% sample_n(1000))
##
Multistart 1 of 1 |
Multistart 1 of 1 |
Multistart 1 of 1 |
Multistart 1 of 1 /
Multistart 1 of 1 |
Multistart 1 of 1 |
Ajuste de modelo de kernel de media local con
np y validación cruzada.
plot(mkernel,plot.errors.method="asymptotic",
plot.errors.style="band")
Por último vamos a considerar los modelos de àrboles que son de los más populares del machine-learning. rpart estima un sólo árbol de decisión que clasifica las predicciones en varios grupos dependiendo de tramos de la variable. randomForest utiliza un procedimiento denominado bagging que consiste en combinar árboles distintos a partir de muestras bootstrap. Los dos procedimientos son no paramétricos, pero difieren en las funciones que generan. rpart serán funciones definidas por tramos, y randomForest se adaptará a funciones irregulares. Se estiman de manera análoga al resto de modelos
Ajuste de modelos de árboles de regresión,
rpart
library(rpart); library(randomForest)
mrpart=rpart(taq1~taq0, data=film90)
mod_plot(mrpart) + geom_point(aes(y=taq1),data=film90,size=.4,alpha=.4,color="orange") +
labs(title="Modelo `rpart`") + theme_bw(20)
Ajuste de modelos de árboles de regresión,
rpart
mrf=randomForest(taq1~taq0, data=film90)
mod_plot(mrf) + geom_point(aes(y=taq1),data=film90,size=.4,alpha=.4,color="orange") +
labs(title="Modelo `randomForest`") + theme_bw(20)
Como vemos el modelo rpart por defecto presenta signo de subajuste, mientras que el randomForest de sobreajuste.
bootstrapLa irrupción de la técnica bootstrap es, probablemente, la revolución mayor que se ha producido en la estadística. Permiten derivar la distribución en el muestreo de los estimadores por medios exclusivamente computacionales. Aunque la discusión y aplicación a la estimación de intervalos la dejamos para más tarde, vamos a ver una aplicación muy interesante para la “comprensión” de lo que hacen distintos métodos de estimación, y la evaluación rápida de su robustez. Para ello vamos a utilizar la opción bootstrap que ofrece mod_plot en mosaicModel.
mod_plot(mp3,bootstrap=10)+labs(title="mp3")
mod_plot(msc5,bootstrap=10)+labs(title="msc5")
mod_plot(msc8,bootstrap=10)+labs(title="msc8")
mod_plot(mrpart,bootstrap=10)+labs(title="mrpart")
A simple vista se puede apreciar estimadores que tienen menos varianza de aquellos que tienen más varianza. En general, y como estudiamos, los estimadores más complejos tienden a ser más inestables, y los más sencillos pueden ser más sesgados. El mp3 polinomial cúbico es un ejemplo de modelo con subajuste y poca variabilidad: cambia poco en función de los datos, pero para equivocarse siempre. Se aprecia más inestabilidad en el msc8 que en el msc5, y se trata de modelos con un buen balance. El modelo de árboles rpart también subajusta. Por cierto, randomForest precisamente utiliza un recurso parecido al del gráfico para combinar distintos modelos: se trata de obtener distintos árboles sencillos como los que se muestran a partir de muestras bootstrap y luego combinarlos mediante bagging.
Desde el punto de vista de la estimación en R no hay diferencias en la estimación de modelos con una y más variables salvo en la utilización de fórmulas que incluyan las distintas variables. Estos son algunas de las convenciones en las fórmulas de R:
+ Añade variables: y ~ x1 + x2. Selecciona el resto de variables (ej: y ~ .), o las variables anteriores update(mod,.~.+x3)-Quita variables: y ~ x1 + x2 - 1 (1, la constante). Se utiliza en combinación con update.: Añade interacciones: y ~ x1 + x2 + x1:x2* Añade las variables e interacciones: y ~ x1*x2 es igual que y ~ x1 + x2 + x1:x2.^2 Añade variables e interacciones hasta de orden 2: (x1+x2+x3)^2 introduce x1+x2+x3+x1:x2+x2:x3+x1:x3%in%. x1 %in% f introduce las interacciones de x1 con todos los niveles de f (sin categoria de referencia: nesting)poly(x1,degree=\(k\),raw=TRUE): Añade polinomio de orden k. Equivale a x1+I(x1^2)+ …+ I(x1^k)poly(x1,degree=\(k\)): Añade polinomio de orden k (ortogonal). Mismo ajuste.ns(var,df), del paquete splines, añade splines cúbicos naturales.bs(var,df,degree=k), de splines, permite añadir splines de distinto orden.I(.): Para efectuar alguna operación de las que tienen un significado especial que hemos visto arriba. Por ejemplo, para meter una variable al cuadrado I(x^2), o I(x1*x2^2)|: Tiene el significado general de condicionar. Se utiliza en distintos paquetes para separar partes de fórmulas (ej: instrumentos en ivreg, random effects en modelos lineales mixtos.)El modelo lineal más sencillo con las variables de film90 será:
mod0=lm(taq1~taq0+lnosc+dist, data=film90)
summary(mod0)
##
## Call:
## lm(formula = taq1 ~ taq0 + lnosc + dist, data = film90)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.7037 -0.6093 -0.0370 0.5604 6.6343
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -5.31064 0.15243 -34.84 <2e-16 ***
## taq0 1.88574 0.01837 102.67 <2e-16 ***
## lnosc -1.06824 0.01715 -62.30 <2e-16 ***
## distMajor 0.60189 0.04691 12.83 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.101 on 4027 degrees of freedom
## Multiple R-squared: 0.8856, Adjusted R-squared: 0.8855
## F-statistic: 1.039e+04 on 3 and 4027 DF, p-value: < 2.2e-16
autoplot(mod0,size=.3,alpha=0.4)
mod_plot(mod0)
El ajuste es bueno pero presenta posibles problemas de linealidad. La parte buena del modelo lineal es la interpretabilidad de los parámetros, pero al coste de estar imponiendo una visión simplificada de la realidad. En este caso el coeficiente negativo de lnosc es lógico en el sentido de que serían películas que han conseguido el mismo taquillaje estrenándose en más cines. Por lo tanto consiguieron menos ingreso por pantalla.
Para conseguir modelos más flexibles podemos permitir splines o polinomios para las variables cuantitativas e introducir interacciones. Este sería un ejemplo de modelo que, probablemente, implique sobreajuste.
mod1=lm(taq1~(ns(taq0, 5)+poly(lnosc,2)+dist)^2, data=film90)
summary(mod1)
##
## Call:
## lm(formula = taq1 ~ (ns(taq0, 5) + poly(lnosc, 2) + dist)^2,
## data = film90)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.6952 -0.5563 -0.0497 0.4834 6.7567
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 22.8016 15.4458 1.476 0.1400
## ns(taq0, 5)1 -11.2774 15.3548 -0.734 0.4627
## ns(taq0, 5)2 -6.8699 15.5001 -0.443 0.6576
## ns(taq0, 5)3 1.6300 10.0970 0.161 0.8718
## ns(taq0, 5)4 -18.3129 33.6021 -0.545 0.5858
## ns(taq0, 5)5 4.6894 22.3991 0.209 0.8342
## poly(lnosc, 2)1 1177.3814 1056.3393 1.115 0.2651
## poly(lnosc, 2)2 242.3497 266.9729 0.908 0.3641
## distMajor 0.6146 0.9832 0.625 0.5319
## ns(taq0, 5)1:poly(lnosc, 2)1 -1423.5809 1050.7854 -1.355 0.1756
## ns(taq0, 5)2:poly(lnosc, 2)1 -1434.3901 1057.7521 -1.356 0.1752
## ns(taq0, 5)3:poly(lnosc, 2)1 -900.7836 709.3884 -1.270 0.2042
## ns(taq0, 5)4:poly(lnosc, 2)1 -2257.1053 2077.4965 -1.086 0.2773
## ns(taq0, 5)5:poly(lnosc, 2)1 -481.6520 1266.7160 -0.380 0.7038
## ns(taq0, 5)1:poly(lnosc, 2)2 -239.4557 266.6321 -0.898 0.3692
## ns(taq0, 5)2:poly(lnosc, 2)2 -225.6316 265.8145 -0.849 0.3960
## ns(taq0, 5)3:poly(lnosc, 2)2 -170.5628 178.4988 -0.956 0.3394
## ns(taq0, 5)4:poly(lnosc, 2)2 -494.5523 551.4341 -0.897 0.3699
## ns(taq0, 5)5:poly(lnosc, 2)2 -239.4729 298.6366 -0.802 0.4227
## ns(taq0, 5)1:distMajor -0.5207 0.9114 -0.571 0.5678
## ns(taq0, 5)2:distMajor 0.3292 1.0916 0.302 0.7630
## ns(taq0, 5)3:distMajor -1.3705 0.8277 -1.656 0.0979 .
## ns(taq0, 5)4:distMajor 1.1083 2.4349 0.455 0.6490
## ns(taq0, 5)5:distMajor 1.3627 2.1451 0.635 0.5253
## poly(lnosc, 2)1:distMajor 0.9000 11.3919 0.079 0.9370
## poly(lnosc, 2)2:distMajor 9.3479 4.8776 1.916 0.0554 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.039 on 4005 degrees of freedom
## Multiple R-squared: 0.8987, Adjusted R-squared: 0.8981
## F-statistic: 1421 on 25 and 4005 DF, p-value: < 2.2e-16
autoplot(mod1,size=.3,alpha=0.4)
mod_plot(mod1)
Es un modelo que presenta algunos de los problemas que veíamos en la regresión de una variable: no normalidad con alta probabilidad en las colas, varianza no constante (decreciente con el valor ajustado).
Las herramientas de inferencia disponibles con R incluyen la realización de contrastes anova para comparación de modelos. El modelo 0 está anidado en el modelo 1 y se puede contrastar si las restricciones que impone son consistentes con los datos con la función anova
anova(mod0,mod1)
## Analysis of Variance Table
##
## Model 1: taq1 ~ taq0 + lnosc + dist
## Model 2: taq1 ~ (ns(taq0, 5) + poly(lnosc, 2) + dist)^2
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 4027 4883.9
## 2 4005 4323.4 22 560.55 23.603 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Vemos que se rechaza con gran claridad. El modelo 0 no es adecuado. Esto no quiere decir que el modelo 1 lo sea. Podría haber sobreajuste. Una forma útil de verlo consiste en la utilización de la función mod_plot de mosaic con los gráficos bootstrap.
mod_plot(mod1,bootstrap=10)
Se confirma que, en particular, en las colas estamos sobreajustando, en particular en las interacciones entre lnosc y taq0. Otra pista nos la dan los tests anova de los distintos términos. Estos los podemos obtener con la función Anova del paquete car
library(car)
Anova(mod1)
## Anova Table (Type II tests)
##
## Response: taq1
## Sum Sq Df F value Pr(>F)
## ns(taq0, 5) 12390.0 5 2295.5192 < 2.2e-16 ***
## poly(lnosc, 2) 3120.7 2 1445.4339 < 2.2e-16 ***
## dist 78.0 1 72.2793 < 2.2e-16 ***
## ns(taq0, 5):poly(lnosc, 2) 100.5 10 9.3135 1.995e-15 ***
## ns(taq0, 5):dist 12.3 5 2.2876 0.04356 *
## poly(lnosc, 2):dist 5.8 2 2.6947 0.06769 .
## Residuals 4323.4 4005
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Los contrastes sugieren que aunque los coeficientes individuales no sean significativos, si que lo son los distintos grupos salvo quizás la interacción del polinomio del número de pantallas con la distribuidora.
Este puede ser un buen momento para explorar la estimación de modelos lineales con el paquete Rcmdr que abre una ventana que funciona por menús.
library(Rcmdr)
Escogemos un modelo para trabajar con el, por ejemplo el modelo 1, mod1. Esto nos permite hacer gran cantidad de análisis (gráficos, contrastes, …) a través de menús. La parte más atractiva es que el código que se genera aparece en la ventana de Rcmdr de modo que lo podemos pegar e integrar en nuestros análisis. Entre los aspectos que podemos explorar está la inferencia robusta.
gam: Generalized Additive ModelsLos modelos gam son una extensión de los modelos lineales en varias direcciones:
glm. Los glm se basan en modelizar la esperanza condicionada de la variable, o de una transformación de la variable, a través de un modelo lineal en los parámetros.\[g(\mu) = \eta = X\beta; \ \ E(y) = \mu = g^{-1}(\eta)\] A la transformación \(g(.)\) se la llama función de enlace, link function. Además permite escoger la familia de distribución de \(y\). Por ejemplo, binomial, poisson, gaussiana, …
\[y \sim Fam(\mu, etc.) \\ E(y) = \mu \\ g(\mu) = b_0 + s_1(x_1) + s_2(x_2) +...+s_p(x_p) \\ s_j = \tilde{X}_j \tilde{\beta}_j\]
En particular, los gam pueden integrar funciones suaves de distintos tipos. En R hay diferentes paquetes que implementan los gam. Los más utilizados son gam y mgcv. Utilizaremos este últimos. Dos buenas referencias en R son Clark (2018aClark, Michael. 2018a. Generalized Additive Models. University of Michigan. https://m-clark.github.io/generalized-additive-models/.) o Wood (2017Wood, Simon N. 2017. Generalized Additive Models: An Introduction with R. 2nd ed. CRC Press. https://www.crcpress.com/Generalized-Additive-Models-An-Introduction-with-R-Second-Edition/Wood/p/book/9781498728331.), esta última del autor del paquete mgcv. Las posibilidades que ofrecen los gam son una generalización de los splines de regresión. En las fórmulas añadimos términos del tipo:
\[s(var,\text{bs}=\text{"tp"},\text{by}=fac)\]
Donde bs selecciona la base que define la función suave. Existen muchas opciones:
"tp": Por defecto. Thin plate regression spline."ts": Thin plate regression splines penalizados (shrinkage) hacia cero. Esto tiende a reducir los problemas de sobreajuste."cr": Splines cúbicos."cs": Splines cúbico penalizados (shrinkage) hacia cero."fs": Interacción suave con un factorLa parte más útil desde el punto de vista práctico es que gam escoge el grado de complejidad automáticamente por validación cruzada. La parte mala, es que esto los hace más intensos computacionalmente. Los t son algo más complejos que los c. Este sería un modelo sin interacciones entre lnosc y taq0, pero si entre las variables cuantitativas y el factor.
library(mgcv)
modgam1=gam(taq1~s(taq0,by=dist)+s(lnosc,dist,bs="fs"),data=film90)
summary(modgam1)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## taq1 ~ s(taq0, by = dist) + s(lnosc, dist, bs = "fs")
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 13.1832 0.4771 27.63 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(taq0):distIndependent 5.514 6.560 942.2 <2e-16 ***
## s(taq0):distMajor 6.054 7.229 661.2 <2e-16 ***
## s(lnosc,dist) 11.893 19.000 158.5 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.898 Deviance explained = 89.8%
## GCV = 1.0892 Scale est. = 1.0826 n = 4031
Gráfico del modelo
gam por defecto
Gráfico del modelo
gam por defecto
Gráfico del modelo
gam por defecto
plot(modgam1)
Los gráficos nos muestran las funciones aditivas. En este caso tenemos cuatro asociadas a las combinaciones de las variables cuantitativas con la distribuidora. Todas ellas parecen muy significativas. El edf me da el grado de complejidad de los modelos seleccionados medido por el equivalente en grados de libertad. Vemos que el ajuste en la muestra es muy bueno. También nos proporciona el GCV que es una estimación del ECM predictivo (a diferencia del Scale est. que sería del ajuste en la muestra).
El paquete mgcViz, que hemos instalado previamente, proporciona gráficos mejores:
library(mgcViz)
## Loading required package: qgam
## Loading required package: rgl
##
## Attaching package: 'mgcViz'
## The following object is masked from 'package:lattice':
##
## qq
## The following objects are masked from 'package:stats':
##
## qqline, qqnorm, qqplot
pgam1=getViz(modgam1)
plot(pgam1)
## Hit <Return> to see next plot:
## Hit <Return> to see next plot:
## Hit <Return> to see next plot:
También nos proporciona herramientas de diagnóstico como qqplots, que nos indica cambios en la situación: ahora tenemos muchas probabilidad en la cola superior. Los residuos no tienen mal aspecto.
check(pgam1)
##
## Method: GCV Optimizer: magic
## Smoothing parameter selection converged after 24 iterations.
## The RMS GCV score gradient at convergence was 1.123817e-07 .
## The Hessian was positive definite.
## Model rank = 39 / 39
##
## Basis dimension (k) checking results. Low p-value (k-index<1) may
## indicate that k is too low, especially if edf is close to k'.
##
## k' edf k-index p-value
## s(taq0):distIndependent 9.00 5.51 0.98 0.12
## s(taq0):distMajor 9.00 6.05 0.98 0.08 .
## s(lnosc,dist) 20.00 11.89 0.87 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
gamlss: Generalized additive models for location, scale and shapeLos gamlss generalizan el modelo GAM en tres direcciones:
library(gamlss)
args(gamlss)
## function (formula = formula(data), sigma.formula = ~1, nu.formula = ~1,
## tau.formula = ~1, family = NO(), data = sys.parent(), weights = NULL,
## contrasts = NULL, method = RS(), start.from = NULL, mu.start = NULL,
## sigma.start = NULL, nu.start = NULL, tau.start = NULL, mu.fix = FALSE,
## sigma.fix = FALSE, nu.fix = FALSE, tau.fix = FALSE, control = gamlss.control(...),
## i.control = glim.control(...), ...)
## NULL
Es mucho más rico en la variedad de familias que permite, de modo que se puede utilizar una distribución que se adapta a las características de nuestros datos. Incluye distribuciones mixtas, truncadas, …
Es también más rico en la variedad de términos suaves que se pueden incluir para captar las relaciones entre las variables.
La ventaja de tener un modelo que se adapta a las cararcterísticas de nuestros datos es que facilita la predicción, al proporcionar intervalos de predicción creibles, por ejemplos. Estos se pueden extraer a partir de los centiles de la distribución ajustada. En esta exposición seguiremos el siguiente texto de introducción a gamlss
Veamos el ejemplo primero con una variable utilizando smoothing splines penalizadas, pb y una distribución normal:
library(broom)
m_1 = gamlss(taq1~pb(taq0), data=film90, family=NO)
## GAMLSS-RS iteration 1: Global Deviance = 14109.58
## GAMLSS-RS iteration 2: Global Deviance = 14109.58
augment(m_1) %>% ggplot(aes(y=taq1,x=pb.taq0.))+geom_point(size=0.4,alpha=0.4,color="grey80")+geom_line(aes(y=.fitted),color="red")+ theme_bw()
## ******************************************************************
## Family: c("NO", "Normal")
##
## Call: gamlss(formula = taq1 ~ pb(taq0), family = NO, data = film90)
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: identity
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.347147 0.087053 26.96 <2e-16 ***
## pb(taq0) 0.928889 0.007149 129.93 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## Sigma link function: log
## Sigma Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.33120 0.01114 29.74 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## NOTE: Additive smoothing terms exist in the formulas:
## i) Std. Error for smoothers are for the linear effect only.
## ii) Std. Error for the linear terms maybe are not accurate.
## ------------------------------------------------------------------
## No. of observations in the fit: 4031
## Degrees of Freedom for the fit: 12.73672
## Residual Deg. of Freedom: 4018.263
## at cycle: 2
##
## Global Deviance: 14109.58
## AIC: 14135.05
## SBC: 14215.32
## ******************************************************************
## Don't know how to automatically pick scale for object of type smooth. Defaulting to continuous.
Vemos como el resumen nos proporciona información de los distintos momentos. También nos proporciona criterios estadísticos que nos pueden ayudar en la selección de modelos, en particular el
AIC y el SBC o BIC. El AIC tiende a seleccionar el modelo que mejor comportamiento predictivo tiene. El BIC lleva a escoger modelos más parsimoniosos al penalizar más cuando tenemos más observaciones.
Podemos estimar y comparar con el modelo que también modeliza la desviación típica, sigma.
m_2 = gamlss(taq1~pb(taq0), ~pb(taq0),data=film90, family=NO)
## GAMLSS-RS iteration 1: Global Deviance = 12263.21
## GAMLSS-RS iteration 2: Global Deviance = 12263.55
## GAMLSS-RS iteration 3: Global Deviance = 12263.54
## GAMLSS-RS iteration 4: Global Deviance = 12263.54
augment(m_2) %>% ggplot(aes(y=taq1,x=pb.taq0.))+geom_point(size=0.4,alpha=0.4,color="grey80")+geom_line(aes(y=.fitted),color="red")+ theme_bw()
## ******************************************************************
## Family: c("NO", "Normal")
##
## Call: gamlss(formula = taq1 ~ pb(taq0), sigma.formula = ~pb(taq0),
## family = NO, data = film90)
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: identity
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.343498 0.081359 28.8 <2e-16 ***
## pb(taq0) 0.929188 0.005519 168.4 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## Sigma link function: log
## Sigma Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.905753 0.044196 43.12 <2e-16 ***
## pb(taq0) -0.153053 0.003629 -42.17 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## NOTE: Additive smoothing terms exist in the formulas:
## i) Std. Error for smoothers are for the linear effect only.
## ii) Std. Error for the linear terms maybe are not accurate.
## ------------------------------------------------------------------
## No. of observations in the fit: 4031
## Degrees of Freedom for the fit: 22.82189
## Residual Deg. of Freedom: 4008.178
## at cycle: 4
##
## Global Deviance: 12263.54
## AIC: 12309.19
## SBC: 12453
## ******************************************************************
## Don't know how to automatically pick scale for object of type smooth. Defaulting to continuous.
Podemos ver los gráficos de diagnóstico con plot
plot(m_2)
## ******************************************************************
## Summary of the Quantile Residuals
## mean = 0.0006979142
## variance = 1.000248
## coef. of skewness = 0.5907226
## coef. of kurtosis = 3.940587
## Filliben correlation coefficient = 0.9909749
## ******************************************************************
Y una herramienta muy útil para poner la lupa de aumento en la distribución del término de error: los gráficos de gusano, **worm plots*, que consisten en una versión de qqplot quitando la tendencia lineal.
wp(m_2, ylim.all=3)
Vemos que la distribución dista mucho de estar bien capturada debido a la presencia de mucha probabilidad en las colas. Vamos a utilizar distribuciones más generales que permiten modelizar la asimetría o la kurtosis. La distribución Box-Cox-Cole-Green (BCCG) permite especificar tambíén la asimetría
m_3 <-gamlss(taq1~pb(taq0), ~pb(taq0), ~pb(taq0), data=film90, family=BCCG)
## GAMLSS-RS iteration 1: Global Deviance = 11888.56
## GAMLSS-RS iteration 2: Global Deviance = 11818.61
## GAMLSS-RS iteration 3: Global Deviance = 11811.08
## GAMLSS-RS iteration 4: Global Deviance = 11809.64
## GAMLSS-RS iteration 5: Global Deviance = 11809.64
wp(m_3, ylim.all=0.5)
Ajusta bastante mejor sobre todo por la cola de la izquierda. La familia Box-Cox power exponential (BCPE) permite modelizar los cuatro momentos:
m_4 <-gamlss(taq1~pb(taq0), ~pb(taq0), ~pb(taq0), ~pb(taq0),data=film90, family=BCPE,start.from=m_3)
## GAMLSS-RS iteration 1: Global Deviance = 11738.54
## GAMLSS-RS iteration 2: Global Deviance = 11735.35
## GAMLSS-RS iteration 3: Global Deviance = 11734.89
## GAMLSS-RS iteration 4: Global Deviance = 11734.67
## GAMLSS-RS iteration 5: Global Deviance = 11734.41
## GAMLSS-RS iteration 6: Global Deviance = 11734.08
## GAMLSS-RS iteration 7: Global Deviance = 11733.81
## GAMLSS-RS iteration 8: Global Deviance = 11733.66
## GAMLSS-RS iteration 9: Global Deviance = 11733.6
## GAMLSS-RS iteration 10: Global Deviance = 11733.59
## GAMLSS-RS iteration 11: Global Deviance = 11733.59
## GAMLSS-RS iteration 12: Global Deviance = 11733.6
## GAMLSS-RS iteration 13: Global Deviance = 11733.6
## GAMLSS-RS iteration 14: Global Deviance = 11733.61
## GAMLSS-RS iteration 15: Global Deviance = 11733.62
## GAMLSS-RS iteration 16: Global Deviance = 11733.62
## GAMLSS-RS iteration 17: Global Deviance = 11733.62
## GAMLSS-RS iteration 18: Global Deviance = 11733.63
## GAMLSS-RS iteration 19: Global Deviance = 11733.63
## GAMLSS-RS iteration 20: Global Deviance = 11733.63
## Warning in RS(): Algorithm RS has not yet converged
# Hay problemas de convergencia. Le decimos que continue donde lo dejó. Y en una iteración más termina.
m_4 <-gamlss(taq1~pb(taq0), ~pb(taq0), ~pb(taq0), ~pb(taq0),data=film90, family=BCPE,start.from=m_4)
## GAMLSS-RS iteration 1: Global Deviance = 11733.63
wp(m_4, ylim.all=0.5)
Que nos avisa de que el algoritmo no ha conseguido converger en 20 iteraciones, por lo que reestimamos hasta conseguir convergencia.
La selección entre estos modelos en base al AIC sería:
AIC(m_1,m_2,m_3,m_4)
## df AIC
## m_4 44.97853 11823.59
## m_3 36.06436 11881.77
## m_2 22.82189 12309.19
## m_1 12.73672 14135.05
Sugiere que el mejor modelo es el m_4, que además, capta la distribución del término de error.
Podemos comparar los modelos m_3 y m_4 con:
fittedPlot(m_3, m_4, x=film90$taq0, line.type = TRUE)
Funciones estimadas para cada uno de los momentos en los modelos 3, con distribución BCCG, y 4, con distribución BCPE, en verde.
Que nos muestra las funciones suaves asociadas a los cuatro momentos.
En cuanto a la distribución de la variable, podemos captarla con los centiles correspondientes.
centiles.fan(m_4, xvar=film90$taq0, cent=c(3,10,25,50,75,90,97),
colors="terrain",ylab="taq1", xlab="taq0")
Percentiles 3%, 10%, 25%, 50% y simétricos del modelo gamlss preferido
Podemos también estimar por separado las distribuciones condicionales en distintos valores de la variable. Para ello se utiliza PlotSimpleGamlss del paquete gamlss.util.
library(gamlss.util)
library(colorspace)
plotSimpleGamlss(taq1,taq0, model=m_4, data=film90,
x.val=seq(6,16,2), val=5, N=1000, ylim=c(0,25),
cols=heat_hcl(100))
Distribución condicionada del ingreso en el resto de semanas en función del ingreso en la primera semana
## new prediction
## New way of prediction in pb() (starting from GAMLSS version 5.0-3)
## new prediction
## New way of prediction in pb() (starting from GAMLSS version 5.0-3)
## new prediction
## New way of prediction in pb() (starting from GAMLSS version 5.0-3)
## new prediction
## New way of prediction in pb() (starting from GAMLSS version 5.0-3)
Podemos explorar las distribuciones que nos ofrece gamlss con esta demostración
library(gamlss.demo)
gamlss.demo()
Asimismo, podemos estimar un modelo completo que incluya a las otras variables con
m_f <- gamlss(taq1 ~ pb(taq0)+pb(lnosc) + dist,
sigma.fo = ~ pb(taq0)+pb(lnosc) + dist,
nu.fo = ~ pb(taq0)+pb(lnosc) + dist,
tau.fo = ~ pb(taq0)+pb(lnosc) + dist,
family = BCPE, data = film90, trace=FALSE)
# Vemos las características de las funciones ajustadas con `tidy` del paquete broom
tidy(m_f)
## # A tibble: 16 x 6
## parameter term estimate std.error statistic p.value
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 mu (Intercept) -5.66 0.106 -53.3 0.
## 2 mu pb(taq0) 1.92 0.0126 152. 0.
## 3 mu pb(lnosc) -1.04 0.0126 -82.2 0.
## 4 mu distMajor 0.211 0.0316 6.66 3.04e-11
## 5 sigma (Intercept) -0.0872 0.0978 -0.891 3.73e- 1
## 6 sigma pb(taq0) -0.225 0.0119 -18.8 5.21e-76
## 7 sigma pb(lnosc) -0.0125 0.0113 -1.11 2.68e- 1
## 8 sigma distMajor 0.0617 0.0314 1.97 4.91e- 2
## 9 nu (Intercept) -5.56 0.826 -6.73 1.90e-11
## 10 nu pb(taq0) 0.742 0.108 6.90 5.99e-12
## 11 nu pb(lnosc) -0.905 0.126 -7.18 8.50e-13
## 12 nu distMajor -1.06 0.329 -3.24 1.21e- 3
## 13 tau (Intercept) 1.27 0.306 4.16 3.19e- 5
## 14 tau pb(taq0) -0.0710 0.0366 -1.94 5.23e- 2
## 15 tau pb(lnosc) 0.0224 0.0339 0.661 5.09e- 1
## 16 tau distMajor 0.142 0.0918 1.54 1.22e- 1
wp(m_f, xvar=~taq0+lnosc, ylim.worm=1) # Worm plot por tramos
## number of missing points from plot= 1 out of 835
## Warning in panel(x[id], y[id], col = col[id], pch = pch[id], ...): Some points are missed out
## increase the y limits using ylim.worm
## number of missing points from plot= 0 out of 382
## number of missing points from plot= 0 out of 12
## number of missing points from plot= 0 out of 318
## number of missing points from plot= 0 out of 689
## number of missing points from plot= 0 out of 143
## number of missing points from plot= 2 out of 5
## Warning in panel(x[id], y[id], col = col[id], pch = pch[id], ...): Some points are missed out
## increase the y limits using ylim.worm
## number of missing points from plot= 0 out of 146
## number of missing points from plot= 0 out of 688
## number of missing points from plot= 0 out of 174
## number of missing points from plot= 0 out of 174
## number of missing points from plot= 0 out of 834
Ajuste de la distribución en el modelo final con todas las variables por tramos.
# Gráficos de las funciones suaves
term.plot(m_f,pages=1,main="mu")
Gráficos de las funciones suaves para los momentos en el gamlss final.
term.plot(m_f,what="sigma",pages=1,main="sigma")
Gráficos de las funciones suaves para los momentos en el gamlss final.
term.plot(m_f,what="nu",pages=1,main="nu")
Gráficos de las funciones suaves para los momentos en el gamlss final.
term.plot(m_f,what="tau",pages=1,main="tau")
Gráficos de las funciones suaves para los momentos en el gamlss final.
Una vez estimado el modelo general, podemos comprobar que mejora sustancialmente respecto al AIC al mejor de los modelos que habíamos estimado hasta ahora (el gam). Es también posible realizar validación cruzada con gamlss para comprobar qué modelos funcionan mejor en la predicción.
Para un modelo tan complejo como éste, es dificil la interpretación, incluso a partir de los gráficos anteriores. Existe un paquete, distreg.vis, que permite trabajar de forma interactiva a través de una aplicación shiny. Lo podeís iniciar con
library(distreg.vis)
vis()
Sin embargo, todavía no incluye las distribuciones que hemos empleado BCCG y BCCP. Podéis seleccionar m_2. Siguiendo las instrucciones podéis crear escenarios (condicionar a valores de las variables), y observar cómo cambia la distribución condicionada. Además tienes la opción de obtener el código para replicar el análisis desde R.
broom: 3 funciones genéricas para extraer resultados asociados al modelo (glance), los coeficientes (tidy), y las observaciones (augment).car (en el Rcmdr) y rms: Funciones para contrastes de hipótesis y gráficas de diagnóstico.mosaicModel y sjstats: Funciones para evaluación, diagnóstico y validación de modelosprediction: Como predict, pero fuerza a que el resultado sea siempre un data.frame.visreg, ggeffects: Visualización del ajuste de modeloscaret: Aprendizaje automático, validación de modelos.tidymodels: La nueva generación de paquetes para la modelización está en el horno. La versión actual es la 0.0.1. Está aún empezando. Pero la idea es conseguir uniformizar el modo de manejar los modelos entre paquetes. Entre otros, incluye broom.La idea de la validación consiste en escoger los modelos que mejor predicen fuera de la muestra. Para saberlo, el procedimiento general consiste en dividir nuestra muestra en dos partes: la muestra de trabajo en la que estimamos, y la muestra de test en la que predecimos. De este modo podemos obtener una idea de cómo funcionan los modelos en la práctica y evitar los problemas ligados al sobreajuste.
La parte mala de validar de este modo es que perdemos una parte de los datos para la validación,y también que nada nos garantiza que la muestra de test que estemos utilizando no sea “rara”. Un método que evita ambos problemas es la validación cruzada k-veces (k-fold cross-validation). La validación cruzada se basa en dividir la muestra en k-partes, y estimar el modelo k veces dejando como muestra de test sucesivamente cada una de las partes. Podemos entonces comparar el ajuste del modelo con toda la muestra con las predicciones obtenidas por la validación cruzada. En general será peor el ajuste. La diferencia entre ambas mide el sobreajuste que, como decíamos, es en general mayor cuanto más complejo es el modelo.
Para muchas funciones de R existen equivalentes que escogen los parámetros por validación cruzada. Un ejemplo era el paquete np que comentamos. Otro es
Lógica de la validación cruzada Fuente wikipedia
Lógica de la validación cruzada Fuente wikipedia
Hay muchas funciones en R que realizan automáticamente la validación cruzada. Por ejemplo, para loess, tenemos la función loess.wrapper que evalua loess con distintos valores de span para ver cuál se comporta mejor en validación cruzada.
library(bisoreg)
mloesscv=loess.wrapper(film90$taq0, film90$taq1, span.vals = seq(0.05, 0.5, by = 0.05), folds = 10)
summary(mloesscv)
## Call:
## loess(formula = y ~ x, span = span)
##
## Number of Observations: 4031
## Equivalent Number of Parameters: 30.66
## Residual Standard Error: 1.39
## Trace of smoother matrix: 33.9 (exact)
##
## Control settings:
## span : 0.1
## degree : 2
## family : gaussian
## surface : interpolate cell = 0.2
## normalize: TRUE
## parametric: FALSE
## drop.square: FALSE
En nuestro caso tenemos que, dada la gran cantidad de puntos disponibles, y lo complejo de la función subyacente, el span óptimo está en torno a 0.1. Si quisieramos podríamos estimar con más precisión utilizando span.vals en un entorno de 0.1.
Esto que hemos hecho a pequeña escala, es lo que hacen automáticamente gam y gamlss cuando estiman el grado de complejidad de las funciones suaves.
Existen distintos paquetes para ayudar en la validación cruzada. Un instrumento muy sencillo es mod_error y mod_cv del paquete mosaic. Para las familias que lo tienen definido, que incluyen los modelos lineales, podemos obtener el ECM (rmse) dentro de la muestra (o prediciendo si se especifica newdata) con mod_error y la predicción fuera de la muestra por validación cruzada k-veces (por defecto 10) con mod_cv(). Además, para evitar problemas de varianza al estimar, lo hace por defecto 5 veces (se puede controlar con ntrials)
mod_error(mp8)
## mse
## 1.947198
mod_cv(mp8)
## mse model
## 1 1.954641 mp8
## 2 1.957472 mp8
## 3 1.961952 mp8
## 4 1.958341 mp8
## 5 1.957114 mp8
mod_cv(ml,mp2,mp3,mp4,mp6,mp8,mrf,mrpart,msl,msc5,msc6,msc7,msc8,mod0,mod1,ntrials=1,k=5) %>%
arrange(mse)
## mse model
## 1 1.093432 mod1
## 2 1.214031 mod0
## 3 1.951899 msc8
## 4 1.953624 mp8
## 5 1.955262 msc6
## 6 1.959387 msc7
## 7 1.965561 mp6
## 8 1.968247 msc5
## 9 2.015312 msl
## 10 2.048654 mp4
## 11 2.150606 mp3
## 12 2.252271 mp2
## 13 2.257199 mrpart
## 14 2.468782 ml
## 15 2.601622 mrf
broomEl paquete broom, que ya hemos utilizado, tiene tres funciones genéricas:
augment: Medidas asociadas a las observaciones.tidy: Medidas asociadas a los coeficientes en los modelos paramétricos. Con conf.int=TRUE proporciona intervalo de confianza al nivel de confianza conf.level (por defecto 95%)glance: medidas asociadas al modelo.# Augment para distintos tipos de modelos
augment(mod0) %>% head() #lm
## # A tibble: 6 x 11
## taq1 taq0 lnosc dist .fitted .se.fit .resid .hat .sigma .cooksd
## <dbl> <dbl> <dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 13.9 14.1 6.93 Inde~ 13.8 0.0462 0.130 1.76e-3 1.10 6.12e-6
## 2 9.16 9.72 1.79 Inde~ 11.1 0.0263 -1.95 5.70e-4 1.10 4.47e-4
## 3 14.3 14.2 7.29 Inde~ 13.7 0.0484 0.569 1.93e-3 1.10 1.29e-4
## 4 8.44 8.22 1.39 Inde~ 8.70 0.0333 -0.262 9.15e-4 1.10 1.29e-5
## 5 12.1 9.24 0 Inde~ 12.1 0.0347 -0.0387 9.92e-4 1.10 3.06e-7
## 6 10.7 7.92 0 Inde~ 9.62 0.0287 1.12 6.81e-4 1.10 1.77e-4
## # ... with 1 more variable: .std.resid <dbl>
augment(mloesscv) %>% head() #loess
## # A tibble: 6 x 5
## y x .fitted .se.fit .resid
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 13.9 14.1 14.6 0.117 -0.710
## 2 9.16 9.72 12.2 0.118 -3.03
## 3 14.3 14.2 14.8 0.113 -0.533
## 4 8.44 8.22 9.16 0.110 -0.719
## 5 12.1 9.24 11.5 0.118 0.614
## 6 10.7 7.92 8.70 0.121 2.04
augment(modgam1) %>% head() #gam
## # A tibble: 6 x 10
## taq1 taq0 dist lnosc .fitted .se.fit .resid .hat .sigma .cooksd
## <dbl> <dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <dbl>
## 1 13.9 14.1 Indepe~ 6.93 14.3 0.121 -0.345 0.0134 NA 6.21e-5
## 2 9.16 9.72 Indepe~ 1.79 11.5 0.0594 -2.38 0.00326 NA 7.04e-4
## 3 14.3 14.2 Indepe~ 7.29 14.3 0.163 -0.0349 0.0245 NA 1.19e-6
## 4 8.44 8.22 Indepe~ 1.39 8.66 0.0722 -0.222 0.00481 NA 9.07e-6
## 5 12.1 9.24 Indepe~ 0 11.8 0.0547 0.242 0.00277 NA 6.13e-6
## 6 10.7 7.92 Indepe~ 0 9.01 0.0481 1.73 0.00213 NA 2.41e-4
augment(m_4) %>% head() #gamlss
## ******************************************************************
## Family: c("BCPE", "Box-Cox Power Exponential")
##
## Call: gamlss(formula = taq1 ~ pb(taq0), sigma.formula = ~pb(taq0),
## nu.formula = ~pb(taq0), tau.formula = ~pb(taq0),
## family = BCPE, data = film90, start.from = m_4)
##
## Fitting method: RS()
##
## ------------------------------------------------------------------
## Mu link function: identity
## Mu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.40437 0.07743 18.14 <2e-16 ***
## pb(taq0) 0.97076 0.00528 183.85 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## Sigma link function: log
## Sigma Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.145623 0.041874 3.478 0.000511 ***
## pb(taq0) -0.220777 0.003568 -61.871 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## Nu link function: identity
## Nu Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.54502 0.43420 -1.255 0.209
## pb(taq0) 0.04146 0.04428 0.936 0.349
##
## ------------------------------------------------------------------
## Tau link function: log
## Tau Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.38463 0.14240 9.724 < 2e-16 ***
## pb(taq0) -0.04542 0.01144 -3.969 7.34e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## ------------------------------------------------------------------
## NOTE: Additive smoothing terms exist in the formulas:
## i) Std. Error for smoothers are for the linear effect only.
## ii) Std. Error for the linear terms maybe are not accurate.
## ------------------------------------------------------------------
## No. of observations in the fit: 4031
## Degrees of Freedom for the fit: 44.97853
## Residual Deg. of Freedom: 3986.021
## at cycle: 1
##
## Global Deviance: 11733.63
## AIC: 11823.59
## SBC: 12107.03
## ******************************************************************
## # A tibble: 6 x 5
## taq1 pb.taq0. .fitted .se.fit .resid
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 13.9 14.1 14.5 0.0345 -1.23
## 2 9.16 9.72 12.2 0.0656 -1.73
## 3 14.3 14.2 14.7 0.0288 -0.933
## 4 8.44 8.22 9.14 0.0603 -0.485
## 5 12.1 9.24 11.3 0.0620 0.446
## 6 10.7 7.92 8.56 0.0587 1.17
# Tidy para modelos paramétricos o semiparamétricos
broom::tidy(mp4)
## # A tibble: 5 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 13.3 0.0225 590. 0.
## 2 poly(taq0, 4)1 181. 1.43 127. 0.
## 3 poly(taq0, 4)2 -29.6 1.43 -20.7 8.08e-91
## 4 poly(taq0, 4)3 20.4 1.43 14.2 6.87e-45
## 5 poly(taq0, 4)4 20.5 1.43 14.4 1.35e-45
tidy(modgam1)
## # A tibble: 3 x 5
## term edf ref.df statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 s(taq0):distIndependent 5.51 6.56 942. 0
## 2 s(taq0):distMajor 6.05 7.23 661. 0
## 3 s(lnosc,dist) 11.9 19 159. 0
tidy(m_4)
## # A tibble: 8 x 6
## parameter term estimate std.error statistic p.value
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 mu (Intercept) 1.40 0.0685 20.5 7.48e-89
## 2 mu pb(taq0) 0.971 0.00471 206. 0.
## 3 sigma (Intercept) 0.146 0.0405 3.60 3.26e- 4
## 4 sigma pb(taq0) -0.221 0.00344 -64.1 0.
## 5 nu (Intercept) -0.545 0.432 -1.26 2.07e- 1
## 6 nu pb(taq0) 0.0415 0.0442 0.939 3.48e- 1
## 7 tau (Intercept) 1.38 0.146 9.49 3.87e-21
## 8 tau pb(taq0) -0.0454 0.0117 -3.87 1.12e- 4
# Glance para distintos tipos de modelo
glance(mp4)
## # A tibble: 1 x 11
## r.squared adj.r.squared sigma statistic p.value df logLik AIC
## * <dbl> <dbl> <dbl> <dbl> <dbl> <int> <dbl> <dbl>
## 1 0.807 0.807 1.43 4213. 0 5 -7159. 14329.
## # ... with 3 more variables: BIC <dbl>, deviance <dbl>, df.residual <int>
glance(modgam1)
## # A tibble: 1 x 6
## df logLik AIC BIC deviance df.residual
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 24.5 -5867. 11786. 11946. 4338. 4007.
glance(m_4)
## # A tibble: 1 x 6
## df logLik AIC BIC deviance df.residual
## <int> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0 -5867. 11824. 12107. 11734. 3986.
# Comparación de los modelos gam y gamlss
list(gam1=modgam1,gamlss1=m_1,gamlss2=m_2,gamlss3=m_3,gamlss4=m_4) %>%
map_df(glance,.id="Model") %>% arrange(AIC)
## # A tibble: 5 x 7
## Model df logLik AIC BIC deviance df.residual
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 gam1 24.5 -5867. 11786. 11946. 4338. 4007.
## 2 gamlss4 0 -5867. 11824. 12107. 11734. 3986.
## 3 gamlss3 0 -5905. 11882. 12109. 11810. 3995.
## 4 gamlss2 0 -6132. 12309. 12453. 12264. 4008.
## 5 gamlss1 0 -7055. 14135. 14215. 14110. 4018.
Es lógico que sea mejor el modelo gam puesto que incluía información del resto de variables. Sería interesante comparar incorporando el resto de variables al gamlss.
texregtexreg es un paquete que incluye funciones para mostrar de manera sencilla tablas de modelos que se pueden incluir en artículos de investigación. Funciona casi con todos los modelos. Un ejemplo:
library(texreg)
list(gam1=modgam1,gamlss1=m_1,gamlss2=m_2,gamlss3=m_3,gamlss4=m_4) %>% htmlreg()
| gam1 | gamlss1 | gamlss2 | gamlss3 | gamlss4 | ||
|---|---|---|---|---|---|---|
| (Intercept) | 13.18*** | |||||
| (0.48) | ||||||
| EDF: s(taq0):distIndependent | 5.51*** | |||||
| (6.56) | ||||||
| EDF: s(taq0):distMajor | 6.05*** | |||||
| (7.23) | ||||||
| EDF: s(lnosc,dist) | 11.89*** | |||||
| (19.00) | ||||||
| mu (Intercept) | 2.35*** | 2.34*** | 1.38*** | 1.40*** | ||
| (0.09) | (0.08) | (0.08) | (0.08) | |||
| mu pb(taq0) | 0.93*** | 0.93*** | 1.00*** | 0.97*** | ||
| (0.01) | (0.01) | (0.01) | (0.01) | |||
| sigma (Intercept) | 0.33*** | 1.91*** | 0.19*** | 0.15*** | ||
| (0.01) | (0.04) | (0.05) | (0.04) | |||
| sigma pb(taq0) | -0.15*** | -0.23*** | -0.22*** | |||
| (0.00) | (0.00) | (0.00) | ||||
| nu (Intercept) | 1.17* | -0.55 | ||||
| (0.56) | (0.43) | |||||
| nu pb(taq0) | -0.17** | 0.04 | ||||
| (0.06) | (0.04) | |||||
| tau (Intercept) | 1.38*** | |||||
| (0.14) | ||||||
| tau pb(taq0) | -0.05*** | |||||
| (0.01) | ||||||
| AIC | 11785.81 | |||||
| BIC | 11946.27 | |||||
| Log Likelihood | -5867.44 | |||||
| Deviance | 4337.50 | |||||
| Deviance explained | 0.90 | |||||
| Dispersion | 1.08 | |||||
| R2 | 0.90 | |||||
| GCV score | 1.09 | |||||
| Num. obs. | 4031 | 4031 | 4031 | 4031 | 4031 | |
| Num. smooth terms | 3 | |||||
| Nagelkerke R2 | 0.82 | 0.88 | 0.88 | 0.88 | ||
| Generalized AIC | 14135.05 | 12309.19 | 11881.77 | 11823.59 | ||
| p < 0.001, p < 0.01, p < 0.05 | ||||||
Al meterlo en un .Rmd hay que especificar la opción results=\asis`en el encabezamiento del *chunk*. Si queremos tablas interactivas empleamosscreenreg`
screenreg(list(ml,mp2,mod0))
##
## =====================================================
## Model 1 Model 2 Model 3
## -----------------------------------------------------
## (Intercept) 2.35 *** 13.29 *** -5.31 ***
## (0.10) (0.02) (0.15)
## taq0 0.93 *** 1.89 ***
## (0.01) (0.02)
## poly(taq0, 2)1 180.94 ***
## (1.50)
## poly(taq0, 2)2 -29.65 ***
## (1.50)
## lnosc -1.07 ***
## (0.02)
## distMajor 0.60 ***
## (0.05)
## -----------------------------------------------------
## R^2 0.77 0.79 0.89
## Adj. R^2 0.77 0.79 0.89
## Num. obs. 4031 4031 4031
## RMSE 1.57 1.50 1.10
## =====================================================
## *** p < 0.001, ** p < 0.01, * p < 0.05
mosaic y bcabootmosaictiene una forma muy sencilla para realizar inferencia bootstrap. Por ejemplo, si nos interesa obtener los intervalos de confianza de los parámetros del mod0 podemos hacer
mod0bs=do(500)*lm(formula = taq1 ~ taq0 + lnosc + dist, data = resample(film90))
## Using parallel package.
## * Set seed with set.rseed().
## * Disable this message with options(`mosaic:parallelMessage` = FALSE)
mod0bs %>% head
## Intercept taq0 lnosc distMajor sigma r.squared F
## 1 -5.480574 1.906551 -1.083093 0.5510458 1.092290 0.8831479 10145.119
## 2 -5.279149 1.874485 -1.053040 0.6312589 1.093452 0.8855268 10383.843
## 3 -5.286818 1.890631 -1.077345 0.5756066 1.086774 0.8862589 10459.320
## 4 -5.261512 1.884019 -1.084497 0.6800503 1.094319 0.8859579 10428.175
## 5 -5.307785 1.881260 -1.065819 0.6603023 1.123564 0.8801964 9862.118
## 6 -5.346134 1.888138 -1.075851 0.6523747 1.121774 0.8802147 9863.831
## numdf dendf .row .index
## 1 3 4027 1 1
## 2 3 4027 1 2
## 3 3 4027 1 3
## 4 3 4027 1 4
## 5 3 4027 1 5
## 6 3 4027 1 6
confint(mod0bs)
## name lower upper level method estimate
## 1 Intercept -5.6989615 -4.9449678 0.95 percentile -5.3106371
## 2 taq0 1.8417811 1.9300239 0.95 percentile 1.8857356
## 3 lnosc -1.1062116 -1.0274760 0.95 percentile -1.0682367
## 4 distMajor 0.5082691 0.7031641 0.95 percentile 0.6018866
## 5 sigma 1.0697557 1.1319340 0.95 percentile 1.1012705
## 6 r.squared 0.8796699 0.8927855 0.95 percentile 0.8855833
## 7 F 9813.0887111 11177.7414357 0.95 percentile 10389.6342338
confint(mod0bs,method="se")
## Warning: confint: Using df=Inf.
## name lower upper level method estimate
## 1 Intercept -5.6889488 -4.9579960 0.95 stderr -5.3106371
## 2 taq0 1.8439679 1.9302900 0.95 stderr 1.8857356
## 3 lnosc -1.1078467 -1.0313439 0.95 stderr -1.0682367
## 4 distMajor 0.5000552 0.7046069 0.95 stderr 0.6018866
## 5 sigma 1.0663044 1.1321089 0.95 stderr 1.1012705
## 6 r.squared 0.8791340 0.8930040 0.95 stderr 0.8855833
## 7 F 9732.8228461 11169.1766966 0.95 stderr 10389.6342338
## margin.of.error
## 1 3.654764e-01
## 2 4.316107e-02
## 3 3.825139e-02
## 4 1.022758e-01
## 5 3.290228e-02
## 6 6.934971e-03
## 7 7.181769e+02
var(mod0bs %>% dplyr::select(1:4)) #Matriz de varianzas-covarianzas estimada por el método bootstrap.
## Intercept taq0 lnosc distMajor
## Intercept 0.034771420 -0.0040327474 0.0032107708 0.0030271412
## taq0 -0.004032747 0.0004849402 -0.0004004957 -0.0004249267
## lnosc 0.003210771 -0.0004004957 0.0003808889 0.0001268334
## distMajor 0.003027141 -0.0004249267 0.0001268334 0.0027230146
library(sandwich)
vcovBS(mod0) # Se puede obtener directamente a partir del paquete sandwich, pero sólo hace 100 réplicas (`R`) por defecto.
## (Intercept) taq0 lnosc distMajor
## (Intercept) 0.032400390 -0.0036809123 2.809613e-03 2.984384e-03
## taq0 -0.003680912 0.0004346287 -3.479184e-04 -3.934319e-04
## lnosc 0.002809613 -0.0003479184 3.325880e-04 6.946027e-05
## distMajor 0.002984384 -0.0003934319 6.946027e-05 2.760402e-03
La ventaja de los intervalos bootstrap es que replican la distribución de los estimadores al remuestrar las observaciones.
Los métodos de bootstrap de mosaic son facilmente trazables, pero no incorporan todos los avances de la técnica bootstrap. Esto es lo que hace el paquete bcaboot que estima intervalos bootstrap con corrección de sesgo. Veamos un ejemplo prediciendo para taq=8 con el modelo msc5.
library(bcaboot)
##
## Attaching package: 'bcaboot'
## The following object is masked from 'package:bootstrap':
##
## diabetes
Xy <- film90 %>% dplyr::select(x=taq0,y=taq1)
rfun <- function(Xy) {
lm(y ~ ns(x,5),data=Xy) %>% predict(data.frame(x=8))
}
set.seed(1234)
result <- bcajack(x = Xy, B = 200, func = rfun, verbose = FALSE)
kable(result$lims, digits = 3) # Percentiles de la distribución bootstrap y su error estándar.
| bca | jacksd | std | pct | |
|---|---|---|---|---|
| 0.025 | 8.919 | 0.018 | 8.898 | 0.055 |
| 0.05 | 8.938 | 0.010 | 8.915 | 0.100 |
| 0.1 | 8.951 | 0.010 | 8.936 | 0.180 |
| 0.16 | 8.965 | 0.007 | 8.952 | 0.265 |
| 0.5 | 9.019 | 0.012 | 9.007 | 0.645 |
| 0.84 | 9.077 | 0.011 | 9.062 | 0.915 |
| 0.9 | 9.092 | 0.008 | 9.078 | 0.950 |
| 0.95 | 9.108 | 0.009 | 9.098 | 0.975 |
| 0.975 | 9.115 | 0.004 | 9.116 | 0.990 |
kable(result$stats, digits = 3) # Estadísticos del ajuste. El z0 mide el sesgo. La segunda fila, los errores estándar de los estimadores
| theta | sdboot | z0 | a | sdjack | |
|---|---|---|---|---|---|
| est | 9.007 | 0.056 | 0.189 | 0.002 | 0.056 |
| jsd | 0.000 | 0.004 | 0.077 | 0.000 | 0.000 |
Percentiles bootstrap de acuerdo al estimador
naive (verdes), y corregidos de sesgo (negros)
bcaplot(result)