假设我想计算每个组中不同值的比例。例如,使用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
试试这个:
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
步骤中,数据按照剩余的分组变量进行分组,这里是"am"。您可以使用groups
在每个步骤中检查分组。
剥离的结果当然取决于group_by
调用中分组变量的顺序。您可能希望执行后续的group_by(am)
,以使您的代码更显式。
关于四舍五入和修饰,请参考@Tyler Rinker的回答
您可以使用count()
函数,但是根据dplyr
的版本有不同的行为:
-
dplyr 0.7.1:返回未分组的表:您需要通过
am
重新分组 dplyr & lt;0.7.1:返回一个分组的表,因此不需要再次分组,尽管您可能希望在以后的操作中使用
ungroup()
dplyr是0.7.1
mtcars %>%
count(am, gear) %>%
group_by(am) %>%
mutate(freq = n / sum(n))
dplyr & lt;是0.7.1
mtcars %>%
count(am, gear) %>%
mutate(freq = n / sum(n))
这将产生一个分组表,如果您想使用它进行进一步分析,使用ungroup()
删除分组属性可能会很有用。
@Henrik's对于可用性更好,因为这将使列字符不再是数字,而是符合您的要求…
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%
尽管答案很多,但还有一种方法是将prop.table
与'dplyr'或'data.table'结合使用。
由于'dplyr' v.>= 1.1.0,我们可以在mutate
中使用.by
参数:
library(dplyr)
mtcars %>%
count(am, gear) %>%
mutate(freq = prop.table(n), .by = am)
#> 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' v. <1.1.0的一种方法是:
mtcars %>%
group_by(am, gear) %>%
tally() %>%
mutate(freq = prop.table(n))
#> # A tibble: 4 × 4
#> # Groups: am [2]
#> am gear n freq
#> <dbl> <dbl> <int> <dbl>
#> 1 0 3 15 0.789
#> 2 0 4 4 0.211
#> 3 1 4 8 0.615
#> 4 1 5 5 0.385
的数据。我们可以做的表:
library(data.table)
cars_dt <- as.data.table(mtcars)
cars_dt[, .(n = .N), keyby = .(am, gear)][, freq := prop.table(n), by = "am"][]
#> 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
创建于2022-10-22使用reprex v2.0.2
为了这个流行问题的完整性,从dplyr
的1.0.0版本开始,参数。groups控制group_by
汇总帮助后summarise
函数的分组结构。
对于.groups = "drop_last"
, summarise
删除最后一级分组。这是1.0.0版本之前获得的唯一结果。
library(dplyr)
library(scales)
original <- mtcars %>%
group_by (am, gear) %>%
summarise (n=n()) %>%
mutate(rel.freq = scales::percent(n/sum(n), accuracy = 0.1))
#> `summarise()` regrouping output by 'am' (override with `.groups` argument)
original
#> # A tibble: 4 x 4
#> # Groups: am [2]
#> am gear n rel.freq
#> <dbl> <dbl> <int> <chr>
#> 1 0 3 15 78.9%
#> 2 0 4 4 21.1%
#> 3 1 4 8 61.5%
#> 4 1 5 5 38.5%
new_drop_last <- mtcars %>%
group_by (am, gear) %>%
summarise (n=n(), .groups = "drop_last") %>%
mutate(rel.freq = scales::percent(n/sum(n), accuracy = 0.1))
dplyr::all_equal(original, new_drop_last)
#> [1] TRUE
对于.groups = "drop"
,删除所有级别的分组。结果被转换成一个独立的代码,没有之前的group_by
# .groups = "drop"
new_drop <- mtcars %>%
group_by (am, gear) %>%
summarise (n=n(), .groups = "drop") %>%
mutate(rel.freq = scales::percent(n/sum(n), accuracy = 0.1))
new_drop
#> # A tibble: 4 x 4
#> am gear n rel.freq
#> <dbl> <dbl> <int> <chr>
#> 1 0 3 15 46.9%
#> 2 0 4 4 12.5%
#> 3 1 4 8 25.0%
#> 4 1 5 5 15.6%
如果.groups = "keep"
,与.data(在本例中为mtcars)相同的组结构。summarise
不会剥离group_by
中使用的任何变量。
最后,对于.groups = "rowwise"
,每一行都是它自己的组。它相当于"keep";在这种情况下
# .groups = "keep"
new_keep <- mtcars %>%
group_by (am, gear) %>%
summarise (n=n(), .groups = "keep") %>%
mutate(rel.freq = scales::percent(n/sum(n), accuracy = 0.1))
new_keep
#> # A tibble: 4 x 4
#> # Groups: am, gear [4]
#> am gear n rel.freq
#> <dbl> <dbl> <int> <chr>
#> 1 0 3 15 100.0%
#> 2 0 4 4 100.0%
#> 3 1 4 8 100.0%
#> 4 1 5 5 100.0%
# .groups = "rowwise"
new_rowwise <- mtcars %>%
group_by (am, gear) %>%
summarise (n=n(), .groups = "rowwise") %>%
mutate(rel.freq = scales::percent(n/sum(n), accuracy = 0.1))
dplyr::all_equal(new_keep, new_rowwise)
#> [1] TRUE
另一点值得注意的是,有时在应用group_by
和summarise
之后,一个摘要行会有所帮助。
# create a subtotal line to help readability
subtotal_am <- mtcars %>%
group_by (am) %>%
summarise (n=n()) %>%
mutate(gear = NA, rel.freq = 1)
#> `summarise()` ungrouping output (override with `.groups` argument)
mtcars %>% group_by (am, gear) %>%
summarise (n=n()) %>%
mutate(rel.freq = n/sum(n)) %>%
bind_rows(subtotal_am) %>%
arrange(am, gear) %>%
mutate(rel.freq = scales::percent(rel.freq, accuracy = 0.1))
#> `summarise()` regrouping output by 'am' (override with `.groups` argument)
#> # A tibble: 6 x 4
#> # Groups: am [2]
#> am gear n rel.freq
#> <dbl> <dbl> <int> <chr>
#> 1 0 3 15 78.9%
#> 2 0 4 4 21.1%
#> 3 0 NA 19 100.0%
#> 4 1 4 8 61.5%
#> 5 1 5 5 38.5%
#> 6 1 NA 13 100.0%
由reprex包(v0.3.0)创建于2020-11-09
我为这个重复的任务编写了一个小函数:
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
下面是在dplyr
0.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
}
另外,尝试add_count()
(以绕过讨厌的group_by .groups)。
mtcars %>%
count(am, gear) %>%
add_count(am, wt = n, name = "nn") %>%
mutate(proportion = n / nn)
以下是使用aggregate
和ave
的基本R答案:
df1 <- with(mtcars, aggregate(list(n = mpg), list(am = am, gear = gear), length))
df1$prop <- with(df1, n/ave(n, am, FUN = sum))
#Also with prop.table
#df1$prop <- with(df1, ave(n, am, FUN = prop.table))
df1
# am gear n prop
#1 0 3 15 0.7894737
#2 0 4 4 0.2105263
#3 1 4 8 0.6153846
#4 1 5 5 0.3846154
我们也可以使用prop.table
,但输出显示不同。
prop.table(table(mtcars$am, mtcars$gear), 1)
# 3 4 5
# 0 0.7894737 0.2105263 0.0000000
# 1 0.0000000 0.6153846 0.3846154
这个答案是基于马福的回答。
首先,我修改了它,以确保不会通过使用scipen选项获得作为科学符号列返回的频率列。
然后我将答案乘以100,得到一个百分比,而不是小数,使频率列更容易作为百分比阅读。
getOption("scipen")
options("scipen"=10)
mtcars %>%
count(am, gear) %>%
mutate(freq = (n / sum(n)) * 100)