Frecuencias / proporciones relativas con dplyr

Supongamos que quiero calcular la proporción de diferentes valores dentro de cada grupo. Por ejemplo, usando los datos de mtcars , ¿cómo puedo calcular la frecuencia relativa del número de marchas en am (automático / manual) de una vez con dplyr ?

 library(dplyr) data(mtcars) mtcars % group_by(am, gear) %>% summarise(n = n()) # am gear n # 0 3 15 # 0 4 4 # 1 4 8 # 1 5 5 

Lo que me gustaría lograr:

 am gear n rel.freq 0 3 15 0.7894737 0 4 4 0.2105263 1 4 8 0.6153846 1 5 5 0.3846154 

Prueba esto:

 mtcars %>% group_by(am, gear) %>% summarise (n = n()) %>% mutate(freq = n / sum(n)) # am gear n freq # 1 0 3 15 0.7894737 # 2 0 4 4 0.2105263 # 3 1 4 8 0.6153846 # 4 1 5 5 0.3846154 

De la viñeta dplyr :

Cuando agrupa por múltiples variables, cada resumen se desprende de un nivel de la agrupación. Eso hace que sea más fácil acumular progresivamente un conjunto de datos.

Por lo tanto, después del summarise , la variable de agrupamiento ‘engranaje’ se despega, y los datos se agrupan ‘solo’ por ‘am’ (simplemente verifíquelo con groups en los datos resultantes), en los que luego realizamos el cálculo mutate .

El resultado del “peeling” depende, por supuesto, del orden de las variables de agrupamiento en la llamada group_by . Tuvimos suerte esta vez, que se desprendió de la variable deseada. Es posible que desee hacer un group_by(am) posterior, para que su código sea más explícito.

Para redondeo y embellecimiento, consulte la respuesta agradable de @Tyler Rinker.

Puede usar la función count() , que tiene sin embargo un comportamiento diferente según la versión de dplyr :

  • dplyr 0.7.1: devuelve una tabla desagrupada : debe volver a agrupar antes de la am

  • dplyr <0.7.1: devuelve una tabla agrupada , por lo que no es necesario volver a agrupar, aunque es posible que desee ungroup() para manipulaciones posteriores

dplyr 0.7.1

 mtcars %>% count(am, gear) %>% group_by(am) %>% mutate(freq = n / sum(n)) 

dplyr <0.7.1

 mtcars %>% count(am, gear) %>% mutate(freq = n / sum(n)) 

Esto da como resultado una tabla agrupada , si desea usarla para un análisis posterior, puede ser útil eliminar el atributo agrupado con ungroup() .

@ Henrik’s es mejor para la usabilidad, ya que hará que el carácter de la columna ya no sea numérico, sino que coincide con lo que pediste …

 mtcars %>% group_by (am, gear) %>% summarise (n=n()) %>% mutate(rel.freq = paste0(round(100 * n/sum(n), 0), "%")) ## am gear n rel.freq ## 1 0 3 15 79% ## 2 0 4 4 21% ## 3 1 4 8 62% ## 4 1 5 5 38% 

EDIT porque Spacedman lo solicitó 🙂

 as.rel_freq <- function(x, rel_freq_col = "rel.freq", ...) { class(x) <- c("rel_freq", class(x)) attributes(x)[["rel_freq_col"]] <- rel_freq_col x } print.rel_freq <- function(x, ...) { freq_col <- attributes(x)[["rel_freq_col"]] x[[freq_col]] <- paste0(round(100 * x[[freq_col]], 0), "%") class(x) <- class(x)[!class(x)%in% "rel_freq"] print(x) } mtcars %>% group_by (am, gear) %>% summarise (n=n()) %>% mutate(rel.freq = n/sum(n)) %>% as.rel_freq() ## Source: local data frame [4 x 4] ## Groups: am ## ## am gear n rel.freq ## 1 0 3 15 79% ## 2 0 4 4 21% ## 3 1 4 8 62% ## 4 1 5 5 38% 

Aquí hay una función general que implementa la solución de Henrik en dplyr 0.7.1.

 freq_table <- function(x, group_var, prop_var) { group_var <- enquo(group_var) prop_var <- enquo(prop_var) x %>% group_by(!!group_var, !!prop_var) %>% summarise(n = n()) %>% mutate(freq = n /sum(n)) %>% ungroup } 

Esta respuesta se basa en la respuesta de Matifou.

Primero lo modifiqué para asegurarme de que no obtuviera la columna de frecuencia como una columna de notación científica utilizando la opción scipen.

Luego multiplico la respuesta por 100 para obtener un porcentaje en lugar de un decimal para que la columna de frecuencia sea más fácil de leer como un porcentaje.

 getOption("scipen") options("scipen"=10) mtcars %>% count(am, gear) %>% mutate(freq = (n / sum(n)) * 100)