统计数据框中列可互换的无序组合数:问题命名与多列解法
无向组合计数:问题名称与通用tidyverse解决方案
嘿,这个问题其实有个明确的称呼——无向多列组合频数统计(也可以简单叫“不考虑顺序的分组计数”),核心就是把每行里的多个列值当成一个不区分顺序的集合,统计不同集合出现的次数。
先看你的示例数据:
df = data.frame(fruit1 = c("apple", "orange", "orange", "banana", "kiwi"), fruit2 = c("orange", "apple", "banana", "orange", "apple"), stringsAsFactors = FALSE)
你之前的方法只适用于2列,这里给你一个能轻松扩展到3列、4列甚至更多列的tidyverse方案,分两种常用场景:
场景1:只想得到组合标识和计数
这种方式最简洁,把每行的所有值排序后合并成一个字符串,用这个字符串作为唯一的组合标识,再统计次数:
library(tidyverse) df %>% rowwise() %>% # 抓取当前行所有列的值,排序后拼成统一格式的字符串 mutate(standardized_comb = paste(sort(c_across(everything())), collapse = "-")) %>% ungroup() %>% # 按标准化后的组合统计出现次数 count(standardized_comb, name = "combinations")
跑出来的结果正好匹配你的需求:
# A tibble: 3 × 2 standardized_comb combinations <chr> <int> 1 apple-orange 2 2 apple-kiwi 1 3 banana-orange 2
场景2:需要保留拆分后的组合列
如果你想把排序后的组合拆成单独的列(比如3列的话拆成3个独立列),方便后续进一步分析,可以用这个方法:
df %>% rowwise() %>% # 把每行的所有值排序后存成列表格式 mutate(sorted_vals = list(sort(c_across(everything())))) %>% ungroup() %>% # 把列表拆分成多个命名列 unnest_wider(sorted_vals, names_sep = "_") %>% # 按所有拆分出的列分组计数 count(matches("sorted_vals_"), name = "combinations")
针对你的示例数据,输出会是:
# A tibble: 3 × 3 sorted_vals_1 sorted_vals_2 combinations <chr> <chr> <int> 1 apple orange 2 2 apple kiwi 1 3 banana orange 2
为什么这个方法能通用?
c_across(everything())会自动抓取当前行的所有列,不管你是2列还是10列,都不用修改代码;sort()帮你把同一组合的不同顺序统一成相同的排序结果,比如(orange, apple)和(apple, orange)都会变成(apple, orange);- 后续的分组计数逻辑完全和列数无关,直接复用就行。
举个3列的测试例子验证下:
df_3col = data.frame(fruit1 = c("apple", "orange", "banana"), fruit2 = c("orange", "apple", "orange"), fruit3 = c("banana", "banana", "apple"), stringsAsFactors = FALSE)
用上面的方法处理后,这三行都会被识别为同一个apple-banana-orange组合,统计次数为3,完美适配多列场景~
内容的提问来源于stack exchange,提问作者Joy
相关产品推荐
相关产品推荐

