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 2014年

1
@Spacedman是的,这些是我想要和Frank是正确的数,它们总和为100质量%的点的变量(79 + 21)和(62 + 38)..
jenswirf

2
这似乎确实是在寻找prop.table()/ 的本地dplyr实现sweep()。另外,在其他问题中,有人要求选项包括变量或变量交互的零计数
smci 2016年

Answers:


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小插图

当您按多个变量分组时,每个摘要将剥离分组的一个级别。这样可以轻松地逐步汇总数据集。

因此,在之后,剥去summarisegroup_by'gear'中指定的最后一个分组变量。在该mutate步骤中,将数据按剩余的分组变量(此处为“ am”)进行分组。您可以使用在每个步骤中检查分组groups

剥皮的结果当然取决于group_by调用中分组变量的顺序。您可能希望执行后续操作group_by(am),以使您的代码更明确。

对于四舍五入和美化,请参阅@Tyler Rinker的漂亮答案。


5
我也刚刚发现了该解决方案,但我不知道为什么要sum(n)在整个am小组而不是整个gear小组工作……
Spacedman 2014年

7
请参见插图:“当您按多个变量分组时,每个摘要都会剥离分组的一个级别。”
亨里克

7
很好-如果您停下来之后summarise会说剩下的小组。哦,dplyr岩石……
Spacedman 2014年

简单明了。我以前从不知道剥离理论,谢谢!
王世祥

很好 简单有效。很好!
user2550228

37

您可以使用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”的每个级别内进行频率计算。
艾德温(Edwin)

30

@Henrik的可用性更好,因为这将使列字符不再是数字,而是与您的要求匹配...

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%

编辑因为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
您总是可以使用format添加百分号的方法来创建S3“百分比”类。#overkill
Spacedman 2014年

实现这个可能是有趣太:stackoverflow.com/questions/13483430/...
Spacedman

如果在此示例中也可以计算均值,sd和SE怎么办?
user3655531

6

这是在dplyr0.7.1 上实现Henrik解决方案的通用函数。

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.tabledplyr或结合使用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

该答案基于Matifou的答案。

首先,我对其进行了修改,以确保不会通过使用scipen选项将freq列作为科学计数列返回。

然后,我将答案乘以100以获得百分比而不是十进制,从而使freq列更易于阅读为百分比。

getOption("scipen") 
options("scipen"=10) 
mtcars %>%
count(am, gear) %>% 
mutate(freq = (n / sum(n)) * 100)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.