## Warning: package 'knitr' was built under R version 4.1.3
PASOS PARA RESOLVER ESTOS EJERCICIOS. En general, se trata de analizar si la medida de centralización (media o mediana) de cierta variable cuantitativa se comporta de la misma manera en ciertos grupos.
OJO: en las soluciones he ordenado las medias/medianas de distintas formas (diagrama, con desigualdades, explicándolo de palabra…). NO es necesario hacerlo de todas las formas posibles, aunque tienes que entenderlas todas (porque se usan indistintamente unas u otras).
Considera el ya famoso fichero robles.
Contrasta si la concentración media de Magnesio
es la misma
en las zonas muestreadas.
Descarga y lee el fichero robles. Puedes usar el
botón Import Datase
, o bien la orden
datos = read.table(file = "Practica06-robles.csv", sep = ";", header = T, dec = ".")
y asegurarnos de que han sido bien leídos
head(datos)
## Num Hierro Manganeso Zinc Calcio Magnesio Potasio Fosforo Nitrogeno Zona
## 1 1 0.058 0.0303 0.0089 2.365 0.400 2.632 0.145 2.776 1
## 2 2 0.060 0.0294 0.0109 2.745 0.432 2.495 0.161 2.918 1
## 3 3 0.058 0.0289 0.0090 2.513 0.349 2.396 0.169 4.826 1
## 4 4 0.059 0.0275 0.0090 2.361 0.349 1.979 0.155 4.893 1
## 5 5 0.061 0.0283 0.0087 2.330 0.350 2.197 0.139 3.386 1
## 6 6 0.027 0.0110 0.0097 2.450 0.367 2.233 0.148 3.776 1
## Variedad Tratamiento
## 1 A 0
## 2 A 0
## 3 A 0
## 4 A 0
## 5 A 0
## 6 B 1
y nos quedamos con las columnas que indica el enunciado
datos = datos[, c("Magnesio", "Zona")]
head(datos)
## Magnesio Zona
## 1 0.400 1
## 2 0.432 1
## 3 0.349 1
## 4 0.349 1
## 5 0.350 1
## 6 0.367 1
Decide si hay al menos una zona en la que la concentración media de Magnesio es diferente del resto al nivel de siginificación \(\alpha = 0.05\). A partir de aquí, seguiremos la plantilla ANOVA, e iremos comentando los resultados obtenidos. Los datos ya están en el formato correcto, es decir, una columna con la variable respuesta y otra con la explicativa; pondremos la explicativa primero y la respuesta después
datos = datos[, c(2,1)]
y asignamos a las columnas los nombres que se usan en el script
colnames(datos) <- c("Tratamiento", "Respuesta")
head(datos)
Tratamiento Respuesta
1 1 0.400
2 1 0.432
3 1 0.349
4 1 0.349
5 1 0.350
6 1 0.367
Se exploran los datas
#--- EXPLORACION
# boxplots
bp = boxplot(Respuesta ~ Tratamiento,
data=datos, col="tan")
stripchart(Respuesta ~ Tratamiento,
data=datos, col="red",
vertical = TRUE, method = "jitter",
cex=0.8, add=TRUE, pch=19)
No hay muchos datos en cada grupo, lo que permite que los boxplots
puedan tener formas más o menos “raras”. Nosotros asumimos la
hipótesis independencia en la toma de las muestras, y tenemos
que comprobar la normalidad y la homocedasticidad de la
muestra. Podemos usar métodos gráficos
###--- Graficos diagnostico condiciones ANOVA
modelo = lm(datos$Respuesta ~ datos$Tratamiento)
par(mfrow = c(1, 2), mar = c(5,5,2,1))
for(i in 1:2){
plot(modelo, which = i)
}
par(mfrow = c(1, 1))
El qqplot (panel derecho) muestra desviaciones respecto de la normalidad, y el izquierdo la dispersión de lso residuos (hace referencia a la homocedasticidad). Los gráficos no son del todo inequívocos, de modo que haremos los contrastes de hipótesis correspondientes para salir de dudas. A continuación, se incluye todo el bloque de contrastes que hay en el script: se hace uno de normalidad y otro de homocedasticidad
###--- contrastes normalidad
# test Shapiro-Wilks
# H0: las muestras provienen de poblaciones normales
shapiro.test(modelo$residuals)
Shapiro-Wilk normality test
data: modelo$residuals
W = 0.93733, p-value = 0.03435
# test Kolmogorov-Smirnov-Lilliefors
## H0: las muestras provienen de poblaciones normales
# library(nortest)
# lillie.test(modelo$residuals)
###--- contrastes homocedasticidad
## H0: varianzas iguales (caso de poblaciones normales)
bartlett.test(Respuesta ~ Tratamiento, data = datos)
Bartlett test of homogeneity of variances
data: Respuesta by Tratamiento
Bartlett's K-squared = 4.7222, df = 3, p-value = 0.1933
## H0: varianzas iguales (caso de poblaciones cualesquiera)
# library(car)
# leveneTest(Respuesta ~ Tratamiento, data = datos)
En todo caso, ambos p-valores son suficientemente grandes (mayores que 0.01, se suele usar este valor como referencia independientemente del valor que se use para el contraste ANOVA), por lo que no hay porqué dudar de que se cumplan las hipótesis. Procedemos a hacer el contraste ANOVA: \[H_0:\,\text{ Las medias son iguales en las cuatro zonas}\] \[H_1:\,\text{ Al menos una media es distinta en alguna de las cuatro zonas}\]
###--- TABLA ANOVA
# H0: las medias son iguales
# H1: al menos una media es diferente
modelo = lm(datos$Respuesta ~ datos$Tratamiento)
# p-valor de contraste
anova(modelo)
Analysis of Variance Table
Response: datos$Respuesta
Df Sum Sq Mean Sq F value Pr(>F)
datos$Tratamiento 1 0.022917 0.0229168 4.3506 0.04414 *
Residuals 36 0.189630 0.0052675
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
que resulta ser significativo: al menos en una zona hay una media (poblacional) diferente de la de los demás. Podemos calcular el coeficiente de determinación \(r^2\)
# coeficiente de determinacion r2
resumen = summary(modelo)
resumen$adj.r.squared
[1] 0.08303731
que resulta ser muy bajo, indicando que el modelo tiene poco poder explicativo.
En caso afirmativo, ordena las zonas de acuerdo con la concentración media de Magnesio (puedes usar Bonferroni o Tukey). Ahora nos interesa ordenar las medias poblacionales. Esencialmente, se hacen comparaciones de medias 2 a 2, pero teniendo la precaución de repartir el error de tipo I entre todas las comparaciones para que el error de tipo I acumulado no se dispare. Hay distintas formas de hacer esas comparaciones, nosotros analizamos dos de ellos. Podemos usar el ajuste de Bonferroni
# Ajuste Bonferroni
(ptt=pairwise.t.test(datos$Respuesta, datos$Tratamiento,
p.adj="bonferroni", pool.sd=FALSE))
Pairwise comparisons using t tests with non-pooled SD
data: datos$Respuesta and datos$Tratamiento
1 2 3
2 0.02569 - -
3 0.61144 1.00000 -
4 0.05561 0.00042 0.01943
P value adjustment method: bonferroni
Se puede usar los boxplots de la exploración inicial, o bien
aggregate(datos$Respuesta ~ datos$Tratamiento, FUN = mean)
datos$Tratamiento datos$Respuesta
1 1 0.381400
2 2 0.461400
3 3 0.429125
4 4 0.323300
para calcular las medias muestrales y tener una idea de cómo podrían estar ordenadas las medias poblacionales. Al nivel de significación \(\alpha = 0.05\) se tiene (aqui tienes el diagrama de medias ordenadas)
datos[ , 1] = as.factor(datos[, 1])
y ahora, al nivel de significación \(\alpha = 0.05\)
# Ajuste Tukey, representaciones graficas
# install.packages("multcomp") # por si no lo tienes instalado
library(multcomp)
datos.aov = aov(Respuesta ~ Tratamiento, data = datos)
glh = summary(glht(model=datos.aov, linfct = mcp(Tratamiento = "Tukey")))
par(mar=c(4,4,10,1))
plot(cld(glh, decreasing = FALSE, level = 0.05), col = "tan")
Los grupos que comparten letra incluyen tratamientos entre los que no
hay una respuesta significativamente diferente a nivel poblacional (ojo,
que las medias muestrales sí son distintas, pero la diferencia no es
estadísticamente significativa -relevante-). Podemos calcular las medias
de cada muestra
aggregate(datos$Respuesta ~ datos$Tratamiento, FUN = mean)
datos$Tratamiento datos$Respuesta
1 1 0.381400
2 2 0.461400
3 3 0.429125
4 4 0.323300
# Ajuste Tukey, representaciones graficas
# install.packages("multcomp") # por si no lo tienes instalado
library(multcomp)
datos.aov = aov(Respuesta ~ Tratamiento, data = datos)
glh = summary(glht(model=datos.aov, linfct = mcp(Tratamiento = "Tukey")))
par(mar=c(4,4,10,1))
plot(cld(glh, decreasing = FALSE, level = 0.01), col = "tan")
Este fichero de datos fue extraído de aquí el 30 de diciembre de 2020 a las 16:02, y contiene los datos de concentración de NOx medidos en 5 estaciones de la Comunidad de Madrid. En esta página hay datos de contaminantes atmosféricos en tiempo real D.G. del Medio Ambiente Area de Calidad Atmosférica - Red de Calidad del Aire de la comunidad de Madrid.
Se pide:
Descarga y lee el fichero de datos. Puedes usar
el botón Import Datase
, o bien la orden
datos = read.table(file = "Practica10-NOx-30-dic-2020_limpio.csv", sep = ";", header = T)
y asegurarnos de que han sido bien leídos
head(datos)
Fecha Getafe Leganes Alcala Alcobendas Fuenlabrada Mostoles Torrejon
1 29/12/20 17:00 33 42 32 15 24 17 25
2 29/12/20 18:00 55 61 58 25 53 33 34
3 29/12/20 19:00 73 42 43 26 41 22 47
4 29/12/20 20:00 45 33 80 23 30 17 63
5 29/12/20 21:00 28 31 138 15 21 18 47
6 29/12/20 22:00 23 29 107 12 16 13 28
Alcorcon Coslada
1 22 34
2 42 49
3 28 89
4 24 42
5 20 47
6 18 27
Nos quedamos con las columnas 2, 3, 4, 5 y 6 (la primera contiene la fecha y hora de la recogida de datos) ya que se pide las primeras 5 ciudades de la tabla; y hacemos una pequeña exploración inicial para asegurar la integridad de los datos:
datos = datos[ , 2:6]
summary(datos)
## Getafe Leganes Alcala Alcobendas
## Min. : 6.00 Min. : 6.00 Min. : 15.00 Min. : 5.00
## 1st Qu.:10.50 1st Qu.:10.00 1st Qu.: 33.00 1st Qu.: 9.00
## Median :27.00 Median :29.00 Median : 44.00 Median :12.00
## Mean :29.05 Mean :31.58 Mean : 93.16 Mean :19.84
## 3rd Qu.:46.50 3rd Qu.:42.00 3rd Qu.:122.50 3rd Qu.:22.00
## Max. :73.00 Max. :83.00 Max. :321.00 Max. :82.00
## Fuenlabrada
## Min. : 3.0
## 1st Qu.: 5.5
## Median :18.0
## Mean :20.0
## 3rd Qu.:30.0
## Max. :53.0
Analiza si la concentración del contaminante es análoga en todos ellos. Empezamos por poner datos en formato correcto (una columna con la variable respuesta y otra con la explicativa)
datos = stack(datos)
datos = datos[, c(2,1)]
colnames(datos) <- c("Tratamiento", "Respuesta")
A partir de aquí, seguiremos el script para explorar los datos
##--- EXPLORACION
# boxplots
bp = boxplot(Respuesta ~ Tratamiento,
data=datos, col="tan")
stripchart(Respuesta ~ Tratamiento,
data=datos, col="red",
vertical = TRUE, method = "jitter",
cex=0.8, add=TRUE, pch=19)
Los boxplots sugieren que no se va a cumplir ni la normalidad (bigote
inferior mucho más corto que el superior) ni la homocedasticidad
(boxplots de anchuras muy diferentes). Se puede corroborar mediante las
herramientas gráficas y numéricas (contrastes) que conoces.
###--- Graficos diagnostico condiciones ANOVA
modelo = lm(datos$Respuesta ~ datos$Tratamiento)
par(mfrow = c(1, 2), mar = c(5,5,2,1))
for(i in 1:2){
plot(modelo, which = i)
}
par(mfrow = c(1, 1))
Efectivamente, ni las columnas el gráfico de la izquierda tienen alturas similares (no hay homocedasticidad) ni la mayor parte de los puntos se disponen sobre la recta en el qqplot (gráfico de la derecha, no hay normalidad)
###--- contrastes normalidad
# test Shapiro-Wilks
# H0: las muestras provienen de poblaciones normales
shapiro.test(modelo$residuals)
Shapiro-Wilk normality test
data: modelo$residuals
W = 0.8008, p-value = 5.312e-10
# test Kolmogorov-Smirnov-Lilliefors
## H0: las muestras provienen de poblaciones normales
# library(nortest)
# lillie.test(modelo$residuals)
###--- contrastes homocedasticidad
## H0: varianzas iguales (caso de poblaciones normales)
bartlett.test(Respuesta ~ Tratamiento, data = datos)
Bartlett test of homogeneity of variances
data: Respuesta by Tratamiento
Bartlett's K-squared = 88.714, df = 4, p-value < 2.2e-16
## H0: varianzas iguales (caso de poblaciones cualesquiera)
# library(car)
# leveneTest(Respuesta ~ Tratamiento, data = datos)
Los p-valores son muy pequeños, por lo que hacer un ANOVA no tiene sentido. Es importante resaltar que, aunque la teoría no respalde el uso de ANOVA, matemáticamente es posible hacer los cálculos. Es decir, si se lo pides, el ordenador te hará el contraste ANOVA (tú mandas) aunque del resultado que arroje no se puedan extraer conclusiones.\[ \] Optamos, pues, por hacer un contraste de Kruskal-Wallis. Recuerda que ahora la hipótesis son \[H_0:\,\text{ Las medianas son iguales en las cuatro zonas}\] \[H_1:\,\text{ Al menos una mediana es distinta en alguna de las cuatro zonas}\]
###--- NO PARAMETRICOS: si falla la normalidad
# KRUSKAL-WALLIS equivale al ANOVA
# H0: medianas iguales
# H1: al menos una mediana diferente
kruskal.test(datos$Respuesta, datos$Tratamiento)
Kruskal-Wallis rank sum test
data: datos$Respuesta and datos$Tratamiento
Kruskal-Wallis chi-squared = 24.657, df = 4, p-value = 5.897e-05
el p-valor es muy pequeño, por lo que hay que rechazar H0. Para ordenar las medianas y determinar qué estaciones registran concentraciones de NOx significativamente diferentes (superiores/inferiores) usaremos el test de Dunnet (usamos un nivel de significación es del 10%)
# Dunnett es equivalente a ordenar las medias
# install.packages("dunn.test") # por si no lo tienes instalado
library(dunn.test)
dunn.test(datos$Respuesta, datos$Tratamiento, alpha = .1)
Kruskal-Wallis rank sum test
data: x and group
Kruskal-Wallis chi-squared = 24.6568, df = 4, p-value = 0
Comparison of x by group
(No adjustment)
Col Mean-|
Row Mean | Alcala Alcobend Fuenlabr Getafe
---------+--------------------------------------------
Alcobend | 4.399997
| 0.0000*
|
Fuenlabr | 4.182205 -0.217792
| 0.0000* 0.4138
|
Getafe | 2.719463 -1.680534 -1.462741
| 0.0033* 0.0464* 0.0718
|
Leganes | 2.692975 -1.707022 -1.489230 -0.026488
| 0.0035* 0.0439* 0.0682 0.4894
alpha = 0.1
Reject Ho if p <= alpha/2
junto con el valor muestral de la mediana de cada problación
aggregate(datos$Respuesta ~ datos$Tratamiento, FUN = median)
datos$Tratamiento datos$Respuesta
1 Getafe 27
2 Leganes 29
3 Alcala 44
4 Alcobendas 12
5 Fuenlabrada 18
Observa que desde un punto de vista muestral, de mayor a menor, las medianas se ordenan como sigue: Alcalá, Leganés, Getafe, Alcobendas y Fuenlabrada. Sin embargo, a nivel poblacional, no todas las diferencias son significativas, porque en la tabla de Dunnet no todos los p-valores tienen asignado un asterisco (ojo: con Dunnet se compara el p-valor de la comparación de cada par de medianas con \(\alpha/2\)). Observa que (aqui está el diagrama con las medianas ordenadas)
# Dunnett es equivalente a ordenar las medias
# install.packages("dunn.test") # por si no lo tienes instalado
library(dunn.test)
dunn.test(datos$Respuesta, datos$Tratamiento, alpha = .05)
Kruskal-Wallis rank sum test
data: x and group
Kruskal-Wallis chi-squared = 24.6568, df = 4, p-value = 0
Comparison of x by group
(No adjustment)
Col Mean-|
Row Mean | Alcala Alcobend Fuenlabr Getafe
---------+--------------------------------------------
Alcobend | 4.399997
| 0.0000*
|
Fuenlabr | 4.182205 -0.217792
| 0.0000* 0.4138
|
Getafe | 2.719463 -1.680534 -1.462741
| 0.0033* 0.0464 0.0718
|
Leganes | 2.692975 -1.707022 -1.489230 -0.026488
| 0.0035* 0.0439 0.0682 0.4894
alpha = 0.05
Reject Ho if p <= alpha/2
Observa que ahora