En la práctica es frecuente, cuando se hacen resúmenes de datos, presentar la media de la variable acompañada por su desviación típica entre paréntesis; o la mediana acompañada de los percentiles 25 y 75. Podemos construir fácilmente sendas funciones en R encargadas de presentar esos valores de esta forma.
La siguiente función calcula la media de una variable y la acompaña de su desviación típica entre paréntesis:
meanSd <- function(x, digits=2, na.rm=TRUE){
if ((!na.rm & any(is.na(x)))|all(is.na(x)))
NA else{
x <- na.omit(x)
media <- round(mean(x),digits)
desv <- round(sd(x),digits)
sprintf("%s (%s)",media,desv)
}
}
Como vemos, hemos definido tres argumentos para la función:
x
: la variable cuya media y desviación típica se van a calcular
digits
: el número de decimales; por defecto especificamos 2
na.rm
: qué queremos hacer con los valores perdidos. Por defecto especificamos na.rm=TRUE
para que los ignore.
Ejemplos de uso:
## [1] "4.56 (3.24)"
## [1] "4.5556 (3.2447)"
## [1] NA
La siguiente función calcula la mediana de una variable y la acompaña con los percentiles 25 y 75 entre paréntesis:
medianQ <- function(x, digits=2,na.rm=TRUE){
if ((!na.rm & any(is.na(x)))|all(is.na(x)))
NA else{
x <- na.omit(x)
mediana <- round(median(x),digits)
q25 <- round(quantile(x, 0.25, names=FALSE), digits)
q75 <- round(quantile(x, 0.75, names=FALSE), digits)
sprintf("%s (%s, %s)", mediana, q25, q75)
}
}
Ejemplos de uso:
## [1] "4 (2, 5)"
## [1] "4 (2, 5)"
## [1] NA
Esta función ya la hemos visto en una práctica anterior, pero no está de más recordarla aquí. Muchas veces entre nuestros datos hay valores perdidos; puede que una variables tenga más valores perdidos que otra, y suele resultar de interés saber cuántos valores válidos (no perdidos) tiene cada variable. La siguiente función calcula precisamente el número de valores no perdidos:
Puede observarse que hemos puesto tres puntos suspensivos como argumento de la función, que en el cuerpo de la misma no se usan para nada. Este se hace por razones técnicas: cuando n_valid()
sea llamada desde una lista de funciones, los puntos suspensivos significan que puede recibir cualquier otro argumento (normalmente na.rm
) que no se vaya a usar, sin por ello devolver un error.
Ejemplos de uso:
## [1] 9
IMPORTANTE: Si se desea utilizar estas funciones para hacer estadística descriptiva en un archivo Rmarkdown, deben incluirse las tres en un chunk
al principio del archivo, normalmente después de la carga de librerías. De esta forma estas funciones estarán disponibles para todas las tareas que se desarrollen después.
pivot_longer()
.En prácticas anteriores hemos visto como construir una lista de funciones que, aplicada a una variable, nos calcula varios estadísticos descriptivos. Por ejemplo, con los datos de las tortugas hicimos el siguiente ejemplo:
library(tidyverse)
library(flextable)
library(moments)
tortugas <- read_csv2("tortugas.csv")
listaFunciones=list(mediaSd=meanSd,
medianaQ=medianQ,
asimetria=skewness,
curtosis=kurtosis,
min=min,
max=max,
n_valid=n_valid)
tortugas %>%
summarise_at(vars(LCC), listaFunciones) %>%
flextable() %>%
colformat_num(j=3:4,digits=2) %>%
autofit()
mediaSd | medianaQ | asimetria | curtosis | min | max | n_valid |
81.91 (4.61) | 81.9 (79.2, 84.1) | 0.67 | 5.76 | 63.2 | 105.3 | 1277 |
Ahora bien, si queremos resumir no solo la variable LCC, sino también ACC, el resultado es bastante feo:
tortugas %>%
summarise_at(vars(LCC,ACC), listaFunciones) %>%
flextable() %>% fontsize(size=13,part="all") %>%
autofit()
LCC_mediaSd | ACC_mediaSd | LCC_medianaQ | ACC_medianaQ | LCC_asimetria | ACC_asimetria | LCC_curtosis | ACC_curtosis | LCC_min | ACC_min | LCC_max | ACC_max | LCC_n_valid | ACC_n_valid |
81.91 (4.61) | 77.19 (3.99) | 81.9 (79.2, 84.1) | 77.1 (74.5, 79.5) | 0.6699956 | 0.3821651 | 5.760301 | 3.743594 | 63.2 | 63.2 | 105.3 | 92.1 | 1277 | 1277 |
Lo ideal sería que los datos de cada variable se fuesen situando en filas sucesivas. Para conseguir este objetivo usaremos la función pivot_longer()
. Para entender lo que hace esta función, seleccionemos primero un conjunto de datos pequeño; los valores de LCC, ACC, distancia y profNido de las primeras 3 tortugas de nuestra base de datos:
## # A tibble: 3 x 4
## LCC ACC distancia profNido
## <dbl> <dbl> <dbl> <dbl>
## 1 77.4 71.6 28 61.3
## 2 82 76.3 35 59.5
## 3 87.3 85.5 28.8 60.2
La función pivot_longer
permite colocar “a lo largo” datos que están “a lo ancho”. Para entender qué significa ésto, veamos la función en acción:
## # A tibble: 12 x 2
## Medida Valor
## <chr> <dbl>
## 1 LCC 77.4
## 2 ACC 71.6
## 3 distancia 28
## 4 profNido 61.3
## 5 LCC 82
## 6 ACC 76.3
## 7 distancia 35
## 8 profNido 59.5
## 9 LCC 87.3
## 10 ACC 85.5
## 11 distancia 28.8
## 12 profNido 60.2
Como vemos, se han creado dos variables, una que hemos llamado “Medida” y otra que hemos llamado “Valor”; en “Medida” se han ido colocando los nombres de las variables originales (por eso la sintaxis names_to="Medidas"
), mientras que en “Valor” se han colocado sus valores (y por eso la sintaxis values_to="Valor"
). Esta conversión de formato ancho a formato largo se ha aplicado a las columnas 1 a 4 de la base de datos (por eso hemos puesto cols=1:4
; podían haberse puesto también los nombres de las variables)
Teniendo en cuenta este efecto del comando pivot_longer
, podemos ahora combinarlo con un group_by
y un summarise
para obtener el resultado que íbamos buscando:
peque %>%
pivot_longer(cols=1:4,names_to="Medida",values_to="Valor") %>%
group_by(Medida) %>%
summarise_at(vars(Valor), listaFunciones) %>%
flextable() %>% fontsize(size=13,part="all") %>%
colformat_num(j=4:5,digits=2) %>%
autofit()
Medida | mediaSd | medianaQ | asimetria | curtosis | min | max | n_valid |
ACC | 77.8 (7.07) | 76.3 (73.95, 80.9) | 0.37 | 1.50 | 71.6 | 85.5 | 3 |
distancia | 30.6 (3.83) | 28.8 (28.4, 31.9) | 0.67 | 1.50 | 28.0 | 35.0 | 3 |
LCC | 82.23 (4.95) | 82 (79.7, 84.65) | 0.09 | 1.50 | 77.4 | 87.3 | 3 |
profNido | 60.33 (0.91) | 60.2 (59.85, 60.75) | 0.26 | 1.50 | 59.5 | 61.3 | 3 |
De esta forma hemos conseguido en pocas lineas hacer un resumen descriptivo elegante de muchas variables al mismo tiempo. Si en lugar de aplicarlo a nuestro pequeño conjunto de 3 tortugas queremos aplicarlo al total de tortugas, bastaría con ejecutar:
tortugas %>%
pivot_longer(c(LCC,ACC,distancia,profNido),names_to="Medida",values_to="Valor") %>%
group_by(Medida) %>%
summarise_at(vars(Valor), listaFunciones) %>%
flextable() %>%
fontsize(size=13,part="all") %>%
colformat_num(j=4:5,digits=2) %>%
autofit()
Medida | mediaSd | medianaQ | asimetria | curtosis | min | max | n_valid |
ACC | 77.19 (3.99) | 77.1 (74.5, 79.5) | 0.38 | 3.74 | 63.2 | 92.1 | 1277 |
distancia | 24.01 (7.29) | 23.3 (19, 28.5) | 0.62 | 3.78 | 7.3 | 64.7 | 1277 |
LCC | 81.91 (4.61) | 81.9 (79.2, 84.1) | 0.67 | 5.76 | 63.2 | 105.3 | 1277 |
profNido | 48.06 (10.27) | 47 (40.1, 56.4) | 0.22 | 2.49 | 22.5 | 86.9 | 1277 |
En el archivo de las tortugas, hay algunas variables (Nº de crías emergidas, nº de crías muertas, nº de huevos rotos) que tienen valores perdidos. Si tratamos de aplicar nuestro código anterior a estas variables obtenemos la tabla siguiente:
tortugas %>%
pivot_longer(c(crias_Emerg,crias_Muertas,hrotos),names_to="Medida",values_to="Valor") %>%
group_by(Medida) %>%
summarise_at(vars(Valor), listaFunciones) %>%
flextable() %>% fontsize(size=13,part="all") %>%
colformat_num(j=4:5,digits=2) %>%
autofit()
Medida | mediaSd | medianaQ | asimetria | curtosis | min | max | n_valid |
crias_Emerg | 27.09 (26.37) | 20 (0, 49) | NA | NA | 900 | ||
crias_Muertas | 1.28 (2.34) | 0 (0, 1) | NA | NA | 900 | ||
hrotos | 54.51 (27.83) | 59 (31, 77) | NA | NA | 900 |
Como vemos, la media (Sd), la mediana y cuartiles, así como el número de observaciones válidas se han calculado, porque tal como se han programado estas funciones, por defecto siempre eliminan los valores perdidos para calcular el resultado. Sin embargo las funciones de asimetría, curtosis, minimo y máximo por defecto desde que detectan que hay un valor perdido, devuelven a su vez un valor perdido (min y max) o no devuelven nada (asimetría y curtosis). Para que todas las funciones de la lista prescindan de los valores perdidos para calcular sus resultados, basta con añadir la opción na.rm=TRUE
en la función summarise_at()
del código anterior:
tortugas %>%
pivot_longer(c(crias_Emerg,crias_Muertas,hrotos),names_to="Medida",values_to="Valor") %>%
group_by(Medida) %>%
summarise_at(vars(Valor), listaFunciones, na.rm=TRUE) %>%
flextable() %>% fontsize(size=13,part="all") %>%
colformat_num(j=4:5,digits=2) %>%
autofit()
Medida | mediaSd | medianaQ | asimetria | curtosis | min | max | n_valid |
crias_Emerg | 27.09 (26.37) | 20 (0, 49) | 0.62 | 2.22 | 0 | 109 | 900 |
crias_Muertas | 1.28 (2.34) | 0 (0, 1) | 2.56 | 9.89 | 0 | 13 | 900 |
hrotos | 54.51 (27.83) | 59 (31, 77) | -0.19 | 2.00 | 0 | 124 | 900 |
Cuando se debe representar una variable continua frente a una variable discreta, uno de los gráficos más útiles en este sentido es el boxplot. El siguiente ejemplo muestra como representar la distancia desde los nidos de tortuga hasta la orilla (variable continua) según la playa (variable discreta):
ggplot(tortugas, aes(x=playa, y=distancia, fill=playa)) +
geom_boxplot()+
labs(y="Distancia a la línea de marea alta (m)",x="Playa")+
theme(legend.position = "none")
En cada “caja”, la linea negra central corresponde a la posición de la mediana, y los bordes superior e inferior a los cuantiles 0.75 y 0.25, respectivamente. Si nos fijamos en Calheta, por ejemplo, la distancia mediana de los nidos a la orilla en esta playa es un poco mayor de 20 metros, el cuantil 0.75 es aproximadamente 25 metros (esto es, tres cuartas partes de los nidos están a menos de 25 metros de la orilla) y el cuantil 0.25 es aproximadamente algo más de 15 metros (es decir, la cuarta parte de los nidos está a menos de 15 metros de la orilla). De esta forma, dentro de la “caja” está el 50% de las observaciones: en Calheta, la mitad de los nidos estan aproximadamente entre 15 y 25 metros de la playa, una cuarta parte a menos de 15 metros y otra cuarta parte a más de 25 metros.
Los “brazos” de los boxplots miden como mucho 1.5 veces la longitud de la “caja” (que se denomina recorrido intercuartílico); pueden ser más cortos o más largos si el mínimo o el máximo están a menos de esa distancia, en cuyo caso, el “brazo” solo se prolongaría hasta la posición del mínimo o el máximo respectivamente. Los puntos que están a una distancia mayor que 1.5 veces el tamaño de la caja se representan explícitamente. Estos puntos se denominan outliers.
Por todo ello, el boxplot informa de una manera visual como se reparten los valores de la variable: si es más o menos simétrica (la mediana justo en el centro de la caja), si tiene mucha o poca dispersión (mucha dispersión significa una caja más alargada, poca dispersión una caja más estrecha), y si presenta outliers, puntos que tienden a alejarse del resto.
Al representar un boxplot de una variable continua para cada valor de una variable discreta, se puede comparar en un solo golpe de vista si la variable continua manifiesta un comportamiento muy diferente para cada valor de la discreta; en este ejemplo, salta a vista que Ponta Cosme es la playa donde los nidos están más lejos de la orilla, y Porto Ferreiro donde están más lejos. Asimismo, al ser todas las cajas de tamaños muy parecidos, la variabilidad en las distancias es similar en las cuatro playas. Por último, si nos fijamos en los outliers, vemos que todas las playas tienen al menos un nido mucho más alejado de la orilla que el resto, estando el nido más alejado (algo más de 60 metros) en Ponta Cosme.
Por último, cuando se construye un boxplot como el anterior, es frecuente acompañarlo de una tabla en la que figure la descripción de la variable continua para cada valor de la discreta. En este caso, la tabla acompañante sería la siguiente:
tortugas %>%
group_by(playa) %>%
summarise_at(vars(distancia),listaFunciones) %>%
flextable() %>% fontsize(size=13,part="all") %>%
colformat_num(j=4:5,digits=2) %>%
autofit()
playa | mediaSd | medianaQ | asimetria | curtosis | min | max | n_valid |
Calheta | 21.04 (5.82) | 21.2 (16.98, 24.63) | 0.27 | 2.70 | 9.7 | 36.8 | 196 |
Ervatao | 23.59 (6.92) | 22.8 (18.75, 27.35) | 0.63 | 3.50 | 7.3 | 46.2 | 419 |
Ponta Cosme | 26.36 (7.52) | 25.45 (21, 30.8) | 0.58 | 4.01 | 7.3 | 64.7 | 498 |
Porto Ferreiro | 21.48 (6.77) | 20.75 (17.17, 25.33) | 0.60 | 3.19 | 8.1 | 41.5 | 164 |