web-dev-qa-db-fra.com

Fréquences / proportions relatives avec dplyr

Supposons que je veuille calculer la proportion de valeurs différentes dans chaque groupe. Par exemple, en utilisant les données mtcars, comment calculer la fréquence relative du nombre de engrenages par am (automatique/manuel ) en une seule fois avec dplyr?

library(dplyr)
data(mtcars)
mtcars <- tbl_df(mtcars)

# count frequency
mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n())

# am gear  n
#  0    3 15 
#  0    4  4 
#  1    4  8  
#  1    5  5 

Ce que je voudrais réaliser:

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
120
jenswirf

Essaye ça:

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 vignette dplyr :

Lorsque vous regroupez plusieurs variables, chaque récapitulatif se détache d'un niveau du groupe. Cela facilite la mise en place progressive d'un jeu de données.

Ainsi, après la summarise, la dernière des variables de regroupement "engrenage" est décollée, puis les données sont regroupées "uniquement" par "am" (il suffit de les vérifier avec groups sur les données obtenues. ), sur lequel on effectue ensuite le calcul mutate.

Le résultat du peeling dépend bien sûr de l'ordre des variables de regroupement dans l'appel group_by. Vous voudrez peut-être faire une group_by(am) ultérieure pour rendre votre code plus explicite.

Pour l'arrondi et la purification, veuillez vous reporter à la réponse de Nice de @Tyler Rinker.

237
Henrik

Vous pouvez utiliser la fonction count(), qui a cependant un comportement différent en fonction de la version de dplyr:

  • dplyr 0.7.1: retourne une table non groupée : vous devez regrouper à nouveau par am

  • dplyr <0.7.1: retourne une table groupée , vous n'avez donc pas besoin de grouper à nouveau, bien que vous souhaitiez peut-être ungroup() pour des manipulations ultérieures

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))

Il en résulte un table groupée, si vous souhaitez l'utiliser pour une analyse plus approfondie, il peut être utile de supprimer le groupe groupé attribut avec ungroup().

33
Matifou

@ Henrik's est meilleur pour la facilité d'utilisation car cela rendra le caractère de colonne et non plus numérique mais correspond à ce que vous avez demandé ...

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 Parce que Spacedman l'a demandé :-)

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%
26
Tyler Rinker

Voici une fonction générale implémentant la solution de Henrik sur 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
}
5
Edwin

J'ai écrit une petite fonction pour cette tâche répétitive:

count_pct <- function(df) {
  return(
    df %>%
      tally %>% 
      mutate(n_pct = 100*n/sum(n))
  )
}

Je peux ensuite l'utiliser comme:

mtcars %>% 
  group_by(cyl) %>% 
  count_pct

Il retourne:

# A tibble: 3 x 3
    cyl     n n_pct
  <dbl> <int> <dbl>
1     4    11  34.4
2     6     7  21.9
3     8    14  43.8
2
slhck

Cette réponse est basée sur la réponse de Matifou.

Je l'ai d'abord modifié pour éviter que la colonne freq ne soit renvoyée sous forme de colonne de notation scientifique à l'aide de l'option scipen.

Ensuite, je multiplie la réponse par 100 pour obtenir un pourcentage plutôt que décimal afin de faciliter la lecture de la colonne freq en pourcentage.

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

Malgré les nombreuses réponses, une autre approche utilisant prop.table en combinaison avec dplyr ou data.table.

library("dplyr")
mtcars %>%
    group_by(am, gear) %>%
    summarise(n = n()) %>%
    mutate(freq = prop.table(n))

library("data.table")
cars_dt <- as.data.table(mtcars)
cars_dt[, .(n = .N), keyby = .(am, gear)][, freq := prop.table(n) , by = "am"]
0
TimTeaFan