[英]Count occurrences of factors across multiple columns in grouped dataframe
我有以下 dataframe 並希望按grp
列進行分組,以查看每個組中出現每個列值的數量。
> data.frame(grp = unlist(strsplit("aabbccca", "")), col1=unlist(strsplit("ABAABBAB", "")), col2=unlist(strsplit("BBCCCCDD", "")))
grp col1 col2
1 a A B
2 a B B
3 b A C
4 b A C
5 c B C
6 c B C
7 c A D
8 a B D
期望的結果:
grp col1A col1B col2B col2C col2D
1 a 1 2 2 0 1
2 b 2 0 0 2 0
3 c 1 2 0 2 1
如果我只看grp
和col1
列,使用table()
很容易解決這個問題,當只有 2 列時,我可以將table(df[c('grp', 'col1')])
與table(df[c('grp', 'col2')])
合並table(df[c('grp', 'col2')])
。 但是,隨着因子列數量的增加,這會變得非常麻煩,並且如果col1
和col2
之間存在共享值,則會出現問題。
請注意,dplyr 的計數不起作用,因為它會查找 col1 和 col2 的唯一組合。
我嘗試使用 tidyr 熔化和傳播 dataframe,但沒有任何運氣
> pivot_longer(df, c(col1, col2), names_to= "key", values_to = "val") %>% pivot_wider("grp", names_from = c("key", "val"), values_from = 1, values_fn = sum)
Error in `stop_subscript()`:
! Can't subset columns that don't exist.
x Column `grp` doesn't exist.
我可以找到很多解決方案,適用於我有 1 個組列和 1 個值列的情況,但我不知道如何將它們推廣到更多列。
您可以將col1
& col2
堆疊在一起,計算每個組合的數量,然后將表格轉換為寬表格。
library(dplyr)
library(tidyr)
df %>%
pivot_longer(col1:col2) %>%
count(grp, name, value) %>%
pivot_wider(grp, names_from = c(name, value), names_sort = TRUE,
values_from = n, values_fill = 0)
# A tibble: 3 x 6
grp col1_A col1_B col2_B col2_C col2_D
<chr> <int> <int> <int> <int> <int>
1 a 1 2 2 0 1
2 b 2 0 0 2 0
3 c 1 2 0 2 1
base
解決方案(感謝@GKi完善代碼):
table(cbind(df["grp"], col=do.call(paste0, stack(df[-1])[2:1])))
col
grp col1A col1B col2B col2C col2D
a 1 2 2 0 1
b 2 0 0 2 0
c 1 2 0 2 1
使用reshape2
package 的recast
:
reshape2::recast(df, grp~variable+value,id.var = 'grp', fun = length)
grp col1_A col1_B col2_B col2_C col2_D
1 a 1 2 2 0 1
2 b 2 0 0 2 0
3 c 1 2 0 2 1
在基礎 R 中,您可以執行以下操作:
with(df, cbind(table(grp, paste0('col1_', col1)), table(grp, paste0('col2_', col2))))
col1_A col1_B col2_B col2_C col2_D
a 1 2 2 0 1
b 2 0 0 2 0
c 1 2 0 2 1
如果您有很多列,請考慮這樣做:
do.call(cbind, Map(function(x, y) table(df$grp, paste(x,y, sep = '_')),
names(df)[-1], df[,-1]))
col1_A col1_B col2_B col2_C col2_D
a 1 2 2 0 1
b 2 0 0 2 0
c 1 2 0 2 1
然后你可以把它變成 dataframe
你在melt
和spread
的正確軌道上。 這是一個整潔的解決方案。 我首先使用pivot_longer
泛化到任意數量的列,然后pivot_wider
返回所需的 output 格式。 output 數據幀中的列順序取決於數據。 如果這是一個問題,只需 append 和select
到 pipe 的末尾即可獲得所需的順序。 (或者在@DarrenTsai的回答中使用names_sort
。)
library(tidyverse)
d %>%
pivot_longer(
starts_with("col"),
names_to="Column",
values_to="Value"
) %>%
group_by(grp, Column, Value) %>%
summarise(N=n(), .groups="drop") %>%
group_by(grp) %>%
pivot_wider(
id_cols=grp,
values_from=N,
names_from=c(Column, Value),
names_sep="",
values_fill=0
) %>%
ungroup()
# A tibble: 3 × 6
grp col1A col1B col2B col2D col2C
<chr> <int> <int> <int> <int> <int>
1 a 1 2 2 1 0
2 b 2 0 0 0 2
3 c 1 2 0 1 2
另一種可能的解決方案,基於tidyr::pivot_longer
后跟tidyr::pivot_wider
並使用values_fn = length
:
library(tidyverse)
df %>%
pivot_longer(c(col1, col2)) %>%
mutate(name = str_c(name, value)) %>%
pivot_wider(grp, values_fn = length, values_fill = 0, names_sort = T)
#> # A tibble: 3 x 6
#> grp col1A col1B col2B col2C col2D
#> <chr> <int> <int> <int> <int> <int>
#> 1 a 1 2 2 0 1
#> 2 b 2 0 0 2 0
#> 3 c 1 2 0 2 1
在data.table
中,我們可以使用dcast
+ melt
如下所示
dcast(
melt(setDT(df), id.vars = "grp")[
, value := paste(variable, value, sep = "_")
], grp ~ value
)
生產
grp col1_A col1_B col2_B col2_C col2_D
1: a 1 2 2 0 1
2: b 2 0 0 2 0
3: c 1 2 0 2 1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.