rnorm: Función para crear vector de distribución normal.
sample_: Familia de funciones de dplyr para muestreo aleatorio en data frames.
for loops: instrucción programática para ejecutar la misma lógica sobre distintos objetos o grupos.
lm: Función para estimar coeficentes de un modelo lineal con MCO (entre otros).
Librería Broom: Extrae elementos de la regresión lineal.
Librería modelsummary: Presenta los resultados de una o varias regresiones lineales en una tabla estándar.
Varianza muestral
¿Para qué nos sirven los errores estándar y los p-valores asociados a los estimadores? En esta parte del tutorial abordaremos esta pregunta mientras repasamos conceptos básicos de incertidumbre, regresión lineal, y testeo de hipótesis. Empecemos.
Se acercan las elecciones
Se vienen las elecciones en Villa Inferencia y las encuestadoras están dando todo de si para estimar la cantidad de votos que obtendrá cada partido.
¿Podrá el intendente ser reelecto? ¿Será vencido por los opositores?
¿Hay alguna diferencia en el ingreso promedio de opositores y no-opositores?
Preferencias y características de la población
Imagina que eres Dios. Vos creaste a los habitantes de Villa Inferencia, y conoces las preferencias políticas y el nivel de ingresos de cada uno de ellos.
Data generating process
Este es el modelo que describe las carácteristicas y preferencias de los habitantes, el data generating process:
tamano =1000000# Un millón de Ciudadanostasa_opositora = .51# Dios sabe que la oposición es mayoría.poblacion =tibble(index =1:tamano,opositora = (index <= tasa_opositora * tamano), # Index 1-510.000: opositoresingresos =rnorm(tamano, mean =1000000, sd=30000)+ (15000* opositora) # Antis $15k más ricos.)
Ten en mente que nadie lo conoce (si lo conociéramos, no haria falta tener una encuesta).
Data generating process
Este es el modelo que describe las carácteristicas y preferencias de los habitantes, el data generating process:
Ten en mente que nadie lo conoce (si lo conociéramos, no haria falta tener una encuesta).
Resultados de la primera encuesta
Te llegan los resultados de una encuesta a 1000 individuos seleccionados aleatoriamente.
# Una encuestadoraset.seed(13) # Importante para replicar muestra aleatoriatamano_muestra =1000muestra <- poblacion %>%sample_n(tamano_muestra) # Tomamos muestra aleatoria de tamaño 1000(prop_opo_muestra <- muestra %>%summarize(mean(opositora)) %>%# Promedio de variable binaria: suma de valores == 1 / cantidad de obs.as.numeric() )
[1] 0.495
¿Que piensas de los resultados de la primera encuesta? Ponen cómo ganador al actual intendente. Veamos los resultados de dos encuestadoras mas.
Segunda y Tercera encuesta
# Otra encuestadoramuestra_2 <- poblacion %>%sample_n(tamano_muestra)prop_opo_muestra_2 <- muestra_2 %>%summarize(mean(opositora)) %>%as.numeric()# Otra encuestadoramuestra_3 <- poblacion %>%sample_n(tamano_muestra)prop_opo_muestra_3 <- muestra_3 %>%summarize(mean(opositora)) %>%as.numeric()# Guardemos resultados en vector y hechemos un vistazo(muestras_opo <-c(prop_opo_muestra, prop_opo_muestra_2, prop_opo_muestra_3))
[1] 0.495 0.493 0.501
Se repite el mismo patrón. La segunda da como ganador al actual intendente, mientras que la tercera anticipa un empáte técnico. Hechemos un vistazo al resto de las encuestas.
Siete encuestas adicionales
# Siete encuestadoras más y promediafor(i in1:7){ muestra <- poblacion %>%sample_n(tamano_muestra) muestras_opo[i+3] <- muestra %>%# Añadimos resultados a muestras_opo, sin cambiar los generados arriba. summarize(mean(opositora)) %>%as.numeric()}
promedio_10 <-mean(muestras_opo)h1 <-ggplot(data =tibble(x = muestras_opo), aes(x = x, y =stat(density*width))) +geom_histogram() +geom_vline(xintercept = promedio_10)+labs(title="Histograma de 10 encuestas",subtitle =paste0("Promedio de las estimaciones: ", promedio_10),x ="Opositores (%)", y ="Proporcion de encuestas")h1
Resumen de los resultados
Distrubición de la media muestral
Supongamos que disponemos de 500 muestras independientes en total, de 1000 observaciones cada una.
# 500 encuestas...? Aproximando la distribución muestral.for(i in1:500){ muestra <- poblacion %>%sample_n(tamano_muestra) muestras_opo[i+10] <- muestra %>%summarize(mean(opositora)) %>%as.numeric()}media_dist_muestral <-mean(muestras_opo)EE_dist_muestral <-sd(muestras_opo)dist_muestral <-ggplot(data =tibble(x = muestras_opo), aes(x = x, y =stat(density*width))) +geom_histogram() +geom_vline(xintercept = media_dist_muestral)+labs(title="Distribución Muestral",subtitle=paste0("Promedio de las estimaciones: ",round(media_dist_muestral,4)),x="Opositores (%)", y ="Proporción de encuestas")dist_muestral
Distrubición de la media muestral
Distrubición de la media muestral
Todos los estimadores tienen su propia distribución.
La ley de los grandes números postula que el promedio de una muestra lo suficientemente grande de promedios muestrales es igual al promedio poblacional. Es una de las razones por las cuales nos gusta tanto promedio cómo estimador.
En las próximas secciones discutiremos los estimadores de la regresión lineal.
Cómo son promedios, aplica la misma lógica.
¿Cual es la relación entre los ingresos y las preferencias políticas?
Si alguno de los comandos de campaña haya alguna relación entre ingreso y preferencias, podría sacar ventaja diseñando campañas electorales acordes. 1
Comparemos el ingreso promedio de opositores \(E(income|opo=1)\) y no-opositores \(E(income|opo=0)\) en nuestras encuestas.
¿Cómo puedo hacer esa comparación de forma sitemática? Propongo un modelo lineal sencillo y lo estimo con el método de MCO.
La regresión lineal: una máquina de promedios condicionales.
El modelo y su estimación
El modelo sería:
\[
income_i=\alpha + \beta_1 opo_i + u_i
\]
Y al estimarlo con MCO nos devolvaría:
\(\hat\alpha\): \(\hat{E(income|opo=0)}\), el ingreso promedio del grupo de control, osea \(\bar{income_{gob}}\).
\(\hat\beta_1\): \(\hat{E(income|opo=1)}-\hat{E(income|opo=0)}\), la diferencia entre ingresos promedio del grupo de tratamiento y control, osea \(\bar {income_{opo}}- \bar {income_{gob}}\).
Distribución de estimaciones
A continuación simularemos las estimaciones en 500 encuestas diferentes, y veremos la distribución de las mismas.
set.seed(123)# Distribución muestral de la correlación entre ingresos y oposición al gobierno actualmuestras_diff_mean <-c()muestras_B1 <-c()for(i in1:500){ muestra <- poblacion %>%sample_n(tamano_muestra)# Primero de forma manual (no sistematica)## Ingreso promedio de los opositores (promedio de variable ingreso, ponderado por si es opositor o no.) mean_opo<-sum(muestra$opositora*muestra$ingresos)/sum(muestra$opositora)## Ingreso promedio de pro-intendente (recuerde que el operador lógico "!" es de negación) mean_pro<-sum((!muestra$opositora)*muestra$ingresos)/sum(!muestra$opositora)# Diferencia muestras_diff_mean[i] <- mean_opo-mean_pro# Ahora con MCO## regresion: Ing=B1*op + e modelo <-lm(ingresos ~ opositora, muestra) muestras_B1[i] <- modelo$coefficients[2] %>%as.numeric()}## Calculamos promedios del estimador y el error estandar observado entre una muestra y otra.media_dist_muestral_diff <-mean(muestras_diff_mean)EE_dist_muestral_diff <-sd(muestras_diff_mean)
#|output-location: column## Manualdist_muestral_diff <-ggplot(data =tibble(x = muestras_B1), aes(x = x, y =stat(density*width))) +geom_histogram() +geom_vline(xintercept = media_dist_muestral_diff)+labs(title="Distribución Muestral",subtitle =paste0("Promedio de las estimaciones: ",round(media_dist_muestral_diff,2) ),x ="Estimaciones de E(Income|anti)-E(Income|pro)", y ="Proporción de encuestas")
dist_muestral_diff
#|output-location: column## MCOmedia_dist_muestral_B1<-mean(muestras_B1)EE_dist_muestral_B1 <-sd(muestras_B1)dist_muestral_B1 <-ggplot(data =tibble(x = muestras_B1), aes(x = x, y =stat(density*width))) +geom_histogram() +geom_vline(xintercept = media_dist_muestral_B1)+labs(title="Distribución Muestral",subtitle =paste0("Promedio de las estimaciones: ",round(media_dist_muestral_B1,2) ),x="Estimaciones de B1", y ="Proporción de encuestas")
dist_muestral_B1
Ventajas de usar la regresión lineal
Las distrbuciones anteriores son identicas.
Lección: Podemos usar la regresión para ver las diferencias promedio entre distintos grupos de individuos.
¿Por qué es mejor usar la regresión?
Te permite testear hipótesis acerca de la estimación.
Te lo permite hacer de manera mucho más cómoda, pero esto será mas evidente luego (i.e. cuando la var X sea continua, o cuando tengamos que controlar por una tercera variable).
Situación: Tu jefe no te cree nada
Eres un analista de campaña.
Tienes una sóla encuesta a tu disposición para responder la pregunta anterior.
Calculas promedios y encuentras que los opositores son, en promedio, mas ricos por una diferencia:
# Ingreso promediod de opositoresmean_opo<-sum(muestra$opositora*muestra$ingresos)/sum(muestra$opositora)# Ingreso promediod de promean_pro<-sum((!muestra$opositora)*muestra$ingresos)/sum(!muestra$opositora)# Diferenciamean_opo-mean_pro
[1] 19061.91
Tu supervisor dice que tus resultados son bullshit y que el candidato es transversal (igual de popular en todos los niveles de ingreso). ¿Con qué confianza puedes defender tus resultados?
Usamos las estimaciones de MCO
Puedes defenderte con los errores estándar (SE) de \(\hat\beta\). Recuerda que bajo los supuestos clásicos:
\[
\beta=0 \Longrightarrow \hat{\beta} \sim N\left(0, \sigma^2 / \sum_{i=1}^n x_i\right)
\] Rechazo HO a favor de H1 si nuestro \(\hat\beta\) tiene bajas chances de provenir de ahí.
Obtenemos una diferencia de ingresos promedio (\(\hat\beta_1\)) alrededor de $16.000, con un error estándar de $1.880.
Recuerda, tu supervisor dice que la verdadera relación es cero. Es decir \(\beta_1=0\).
#|output-location: slidemodelo_tidy<-broom::tidy(modelo) # Almacenamos coeficientes y SEs en un tibblestd_opositora<-modelo_tidy %>%# Extraemos SE de B1filter(term=="opositoraTRUE") %>%select(std.error) %>%as.numeric()B1_opositora<-modelo_tidy %>%# Extraemos el estimado de B1filter(term=="opositoraTRUE") %>%select(estimate) %>%as.numeric()supervisor_ho<-tibble( # Tu supervisor dice que B1 poblacional es cero.x=1:5000,y=rnorm(5000,mean =0,sd =std_opositora) # Simulamos distribucion teorica de B1 estimado alrededor de esa hipotesis)p1<-ggplot() +geom_histogram(data=supervisor_ho,aes(x = y))+geom_vline(xintercept =c(0),color="red",linetype="dashed")+labs(title =paste0("Distribución de estimadores si B1=0 y std.error(B1)=",round(std_opositora)))p2<-p1+geom_vline(xintercept =c(0+std_opositora*1,0-std_opositora*1),color="darkslategray1")+labs(subtitle="+/- 1 sd cubren 68% del area")p3<-p1+geom_vline(xintercept =c(0+std_opositora*2,0-std_opositora*2),color="darkslategray4")+labs(subtitle="+/- 2 sd cubren 95.4% del area")p4<-p1+geom_vline(xintercept =c(0+std_opositora*3,0-std_opositora*3),color="darkslategray")+labs(subtitle="+/- 3 sd cubren 99.7% del area")p5 <-p4+geom_vline(xintercept = B1_opositora,color="red",linetype="solid")+labs(subtitle="Es improbable que nuestra estimación de B1 haya salido de esta distribución")
Welch Two Sample t-test
data: ingresos by opositora
t = -10.076, df = 996.24, p-value < 2.2e-16
alternative hypothesis: true difference in means between group FALSE and group TRUE is not equal to 0
95 percent confidence interval:
-22774.25 -15349.57
sample estimates:
mean in group FALSE mean in group TRUE
998514.4 1017576.3
Toolkit para analizar el output de una regresión
Asi luce el output de la regresión que acabamos de fijar
Aveces necesitamos extraer los datos para calculos y presentaciones. La librería broom fácilita el proceso.
broom::augment
# y augment genera una copia del data.frame con los fitted values (predicciones) y los residuos con intervalos de confianza. Tambien sirve para producir predicciones en nueva data.broom::augment_columns(x = modelo,data = muestra)
Recomiendo usar el comando View(modelo) para observar todos los datos que almacena y ver como están organizados.
## Remueve los "#" y corre este código en tu consola# View(modelo)## Fijate que:# modelo$fitted.values# modelo$resid## tienen la misma info mostrada por # broom: augment
Más sobre regresión lineal
Variables discretas y continuas
Variables discretas y continuas
Inspección sanitaria en restaurantes
Cada país dispone de agencias o departamentos que resguardan la salubridad de los alimentos vendidos al público.
En el caso de Argentina, este lugar es ocupado por la Administración Nacional de Medicamentos, Alimentos y Tecnología Médica (ANMAT).
Suponga que es un analista de datos en la ANMAT y le interesa saber si los restaurantes con mayor cantidad de sedes tienen puntajes peores o mejores al resto.
Cuenta con una base de datos que muestra el puntaje que esta le asignó a una muestra de restaurantes inspeccionados.
res <- causaldata::restaurant_inspectionshead(res)
# A tibble: 6 × 5
business_name inspection_score Year NumberofLocations Weekend
<chr> <dbl> <dbl> <dbl> <lgl>
1 MCGINLEYS PUB 94 2017 9 FALSE
2 VILLAGE INN #1 86 2015 66 FALSE
3 RONNIE SUSHI 2 80 2016 79 FALSE
4 FRED MEYER - RETAIL FISH 96 2003 86 FALSE
5 PHO GRILL 83 2017 53 FALSE
6 TACO KING #2 95 2008 89 FALSE
Visualizando los datos
Primer vistazo
(p1<-res %>%ggplot(aes(x=NumberofLocations, y= inspection_score))+geom_point()+labs(title="Relación entre puntaje de inspección y número de locales",subtitle="Parece que los restaurantes con pocos locales tienen mayores clasificaciones (en promedio.)"))
Visualizando los datos
Visualizando los datos
Bondades de visualización en capas
(p2<-p1 +# no repetimos el código, añadimos una capa adicionalgeom_point(data= res %>%# la capa adicional muestra un data.frame nuevogroup_by(NumberofLocations) %>%# con datos agregados a nivel de numero de locacionessummarise(inspection_score=mean(inspection_score)),aes(x=NumberofLocations,y=inspection_score),color="red", shape=18)+labs(subtitle="Lo confirmamos al proyectar los promedios condicionales sobre el gráfico") )
Visualizando los datos
Estimemos un modelo lineal con MCO
¿Cual es la diferencia entre el promedio de restaurantes con un local y el de aquellos con dos locales?
¿Cual es la diferencia entre el promedio de aquellos con 100 y aquellos con 101?
¿Si me dan datos nuevos con un restaurante de 250 ubicaciones, que score puedo esperar que tenga?
Podemos usar la siguiente regresión lineal sobre esta base de datos de restaurantes (r) para generalizar este resultado:
\(\beta_1\) arroja la diferencia entre el score de inspección promedio de un restaurante y el de cualquier restaurante con un local menos que este.
Estimemos un modelo lineal con MCO
# Perform the first, one-predictor regression# use the lm() function, with ~ telling us what # the dependent variable varies overm1 <-lm(inspection_score ~ NumberofLocations, data = res)summary(m1)
Call:
lm(formula = inspection_score ~ NumberofLocations, data = res)
Residuals:
Min 1Q Median 3Q Max
-27.1673 -3.5449 0.9835 5.4362 17.3253
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 94.8656964 0.0462975 2049.05 <2e-16 ***
NumberofLocations -0.0188715 0.0004356 -43.32 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 6.052 on 27176 degrees of freedom
Multiple R-squared: 0.0646, Adjusted R-squared: 0.06456
F-statistic: 1877 on 1 and 27176 DF, p-value: < 2.2e-16
Estimemos un modelo lineal con MCO
Sabemos que el equipo de inspección tuvo una rotación alta a lo largo del proceso de recolección de datos y que los procesos de inspección no estaban 100% estandarizados.
¿Cómo podemos hacer si queremos comparar evaluaciones que ocurrieron en el mismo año? Añadimos la variable año como control.
# Now add year as a control# Just use + to add more terms to the regressionm2 <-lm(inspection_score ~ NumberofLocations + Year, data = res)summary(m2)
Call:
lm(formula = inspection_score ~ NumberofLocations + Year, data = res)
Residuals:
Min 1Q Median 3Q Max
-27.4407 -3.5210 0.9142 5.3014 18.0722
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.253e+02 1.241e+01 18.16 <2e-16 ***
NumberofLocations -1.919e-02 4.358e-04 -44.03 <2e-16 ***
Year -6.489e-02 6.173e-03 -10.51 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 6.039 on 27175 degrees of freedom
Multiple R-squared: 0.06838, Adjusted R-squared: 0.06832
F-statistic: 997.4 on 2 and 27175 DF, p-value: < 2.2e-16
Toolkit para presentar tus regresiones
Librería modelsummary
La libreria modelsummary dispone de alternativas para generar estos resumenes de forma práctica y flexible.1
# Default significance stars are: +/*/**/*** .1/.05/.01/.001. # Social science standard */**/*** .1/.05/.01 can be restored withmodelsummary::msummary(list(m1, m2),stars=c('*'= .1, '**'= .05, '***'= .01),gof_omit ="AIC|BIC|Log")
Librería modelsummary
(1)
(2)
(Intercept)
94.866***
225.333***
(0.046)
(12.411)
NumberofLocations
−0.019***
−0.019***
(0.000)
(0.000)
Year
−0.065***
(0.006)
Num.Obs.
27178
27178
R2
0.065
0.068
R2 Adj.
0.065
0.068
F
1876.705
997.386
RMSE
6.05
6.04
* p < 0.1, ** p < 0.05, *** p < 0.01
Otras opciones
Tambien comparto otras librerías similares para que las tengas en mente:
huxtable (lo usaremos la semana que viene)
Stargazer
jtools
De vuelta a nuestro problema
Predicciones del modelo
Podemos presentar los promedios condicionales provenientes de ambas regresiones (fitted values) en nuestro gráfico de la siguiente manera:
Podemos presentar los promedios condicionales provenientes de ambas regresiones (fitted values) en nuestro gráfico de la siguiente manera:
Modelo 1
p3<-p1 +geom_line(data=predicciones ,aes(x=NumberofLocations,y=.fitted,color="B1*NumberofLocations\n(M1)"))+labs(title="Fitted values del modelo 1",subtitle=NULL)
Predicciones del modelo
Podemos presentar los promedios condicionales provenientes de ambas regresiones (fitted values) en nuestro gráfico de la siguiente manera:
Modelo 2
predicciones_05_25<-broom::augment_columns(x=m2,data=res) %>%filter(Year %in%seq(2005,2020,by=5))p4<-p1 +geom_line(data = predicciones_05_25,aes(x=NumberofLocations,y=.fitted,group=Year,color=paste("B1*NumberofLocations\n + B2*",Year)))+labs(title="Fitted values del modelo 2",subtitle="Para 2005 y 2015",color="Fitted values")
Note que los fitted values del modelo 2 los score promedio del año 2005 son mayores a los de 2015, pero la relación lineal es la misma.
Fin
Ejercicio
Encontramos que el coeficiente del Número de Sedes sobre el Score de Inspección era negativo y significativo. En sus palabras, ¿Que significa este resultado? ¿Nos permite afirmar que los restaurantes relajan sus estándares de calidad e higiene cuando se expanden?
Tratemos de replicar los resultados de Lalonde (1986).[Lalonde, Robert. 1986. “Evaluating the Econometric Evaluations of Training Programs with Experimental Data.” American Economic Review 76 (4): 604–20 ]
El estudio busca identificar el efecto de un programa de entrenamiento laboral sobre los ingresos de los asistentes tras dos años de su culminación, en el período 1975-1978.
La primera parte estudia el efecto sobre personas que fueron asignadas a programas de entrenamiento experimentalmente. Esos datos se encuentran en el data.frame jtrain2.
Luego se analizan datos donde las personas podian escoger si asistir o no de forma voluntaria. Estos datos se encuentran en el data.frame jtrain3.
Ejercicio
El ejercicio consite en:
Reproduzca ambos resultados usando un modelo de regresión lineal.
Presente ambos en la misma tabla de regresión e interprete los coeficientes. Si las hubieren, discuta las diferencias entre los coefientes de cada regresión.
Le dejo el siguiente código para empezar:
library(wooldridge)library(modelsummary)# Leo los datos a sesion de Rdata(jtrain2) # Registros Experimentalesdata(jtrain3) # Registros Observacionales# Distribución de variables de interés antes del tratamiento serán importantes para la interpretaciónselect(jtrain2,train,re75) %>%summary()select(jtrain3,train,re75) %>%summary()# Distribución de variables de interés después del tratamientoselect(jtrain2,train,re78) %>%summary()select(jtrain3,train,re78) %>%summary()# Regresiones: ingresos en 78 = B0 + B1*(tratamiento) + um1<-lm(___~___, data=____)m2<-lm(___~___, data=____)# Tabla de regresion usando libreria modelsummary_______________________