Відносні частоти / пропорції з dplyr


153

Припустимо, я хочу обчислити частку різних значень у кожній групі. Наприклад, використовуючи mtcarsдані, як можна обчислити відносну частоту кількості передач за am (автоматичною / ручною) за один перехід 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 

Чого я хотів би досягти:

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

1
Чи є ці відсотки справжніми цифрами, які ви хочете? Звідки вони беруться, алгебраїчно? Ах, 79% - це 15 / (15 + 4), 21% - 4 / (15 + 4), а тоді для am == 1 62% - 8 / (8 + 5) і т.д.
Spacedman

1
@Spacedman Так, це кількість, яку я хочу, і Френк правильний, вони дорівнюють 100% змінною am (79 + 21) та (62 + 38) ..
jenswirf

2
Це справді, здається, шукає нативного dplyr реалізації prop.table()/ sweep(). Також в інших питаннях деякі люди просять можливість включити нульові підрахунки для змінних або змінних взаємодій
smci

Відповіді:


285

Спробуйте це:

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

Від віньєтки dplyr :

Коли ви групуєте за кількома змінними, кожен зведення відшаровується від одного рівня групування. Це дозволяє легко поступово збирати набір даних.

Таким чином, після summarise, остання змінна групування, зазначена в group_by, "gear", знімається. На mutateетапі дані групуються за допомогою змінної (-ів), що залишилася, тут "я". Ви можете перевірити групування на кожному кроці з groups.

Результат пілінгу, звичайно, залежить від порядку групування змінних у group_byвиклику. Ви можете зробити наступне group_by(am), щоб зробити ваш код більш явним.

Щодо округлення та попередньої обробки, зверніться до приємної відповіді від @Tyler Rinker.


5
Я також виявив це рішення теж, але не знаю, чому sum(n)працює над amгрупою, а не над gearгрупою ...
Spacedman

7
Дивіться віньетку : "Коли ви групуєте за кількома змінними, кожен зведення відшаровується від одного рівня групування."
Генрік

7
Приємно - якщо ти просто зупинишся після цього, summariseто каже, які групи залишилися. Ой дплир скелі ...
Spacedman

Простий і зрозумілий. Я ніколи раніше не знав теорії відшарувань, дякую!
Shixiang Wang

приємно. простий і ефективний. чудова робота!
користувач2550228

38

Ви можете використовувати count()функцію, яка має різну поведінку залежно від версії dplyr:

  • dplyr 0.7.1: повертає негруповану таблицю: вам потрібно знову групуватися заam

  • dplyr <0.7.1: повертає згруповану таблицю, тому більше не потрібно групуватися, хоча, можливо, ви хочете зробити це ungroup()для наступних маніпуляцій

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

Це призводить до згрупованої таблиці , якщо ви хочете використовувати її для подальшого аналізу, може бути корисним видалити згрупований атрибут ungroup().


1
Здається, невірна відповідь на dplyr0.7.1. Це робить обчислення частоти в цілому на "передачі", а не в межах кожного рівня "am".
Едвін

30

@ Генрік краще для зручності використання, оскільки це зробить символом стовпця більше не числовим, але відповідає тому, що ви просили ...

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 Тому що Spacedman попросив це :-)

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%

6
Ви завжди можете створити S3 "відсотковий" клас formatметодом, який додає знак відсотка ... #overkill
Spacedman

Реалізація цього може бути цікава також: stackoverflow.com/questions/13483430 / ...
Spacedman

Що робити, якщо в цьому прикладі можна було б обчислити середнє значення, sd та SE?
користувач3655531

6

Ось загальна функція, що реалізує рішення Генріка на dplyr0.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
}

Error in bind_rows_(x, .id) : Column am` неможливо перетворити з числового на символьний`
f0nzie

5

Я написав невелику функцію для цього повторюваного завдання:

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

Тоді я можу використовувати його так:

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

Він повертає:

# 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

3

Незважаючи на безліч відповідей, ще один підхід, який використовується prop.tableв поєднанні з dplyrабо 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"]

1
На сьогодні найпростіший підхід
Parseltongue

1

Ця відповідь заснована на відповіді Матіфу.

Спочатку я змінив її, щоб гарантувати, що я не повертаю стовпець freq як колонку наукових позначень, використовуючи параметр scipen.

Тоді я множив відповідь на 100, щоб отримати відсоток, а не десятковий, щоб полегшити стовпчик частоти у відсотках.

getOption("scipen") 
options("scipen"=10) 
mtcars %>%
count(am, gear) %>% 
mutate(freq = (n / sum(n)) * 100)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.