如何用dplyr计算双列表格中所有集合对的重叠系数?
问题描述
我需要计算集合间的重叠系数(overlap coefficient),数据为双列表格,示例数据如下:
df_example <- tibble::tribble(~my_group, ~cities, "foo", "london", "foo", "paris", "foo", "rome", "foo", "tokyo", "foo", "oslo", "bar", "paris", "bar", "nyc", "bar", "rome", "bar", "munich", "bar", "warsaw", "bar", "sf", "baz", "milano", "baz", "oslo", "baz", "sf", "baz", "paris")
在df_example中,my_group的foo、bar、baz各自对应一个集合,cities列是集合成员。我希望生成一个表格,包含所有可能的集合对的:
- 交集成员数量
- 对应集合对中较小集合的大小
- 重叠系数(计算公式:
重叠系数 = 共同成员数 / 较小集合大小)
预期输出如下:
## # A tibble: 3 × 4 ## combination n_instersected_members size_of_smaller_set overlap_coeff ## <chr> <dbl> <dbl> <dbl> ## 1 foo*bar 2 5 0.4 ## 2 foo*baz 3 4 0.75 ## 3 bar*baz 2 4 0.5
我尝试过以下dplyr代码,但无法运行,因为dplyr::intersect()需要两个向量:
df_example |> group_by(my_group) |> summarise(intersected = dplyr::intersect(cities))
请问有没有基于dplyr动词的简便方法实现该需求?
解决方案
可以通过生成不重复集合对+嵌套集合运算的方式,全程用dplyr动词完成需求:
步骤1:整理每个集合的成员与大小
先把每个my_group对应的城市集合、集合大小单独提取出来:
library(dplyr) library(tidyr) library(purrr) group_sets <- df_example |> group_by(my_group) |> summarise( city_set = list(unique(cities)), # 保存每组的城市集合(去重) set_size = n_distinct(cities), # 计算集合大小 .groups = "drop" )
步骤2:生成所有不重复的集合对
用cross_join生成所有可能的组对,再过滤掉重复的反向组合(比如foo*bar和bar*foo只保留一个):
pairwise_groups <- group_sets |> cross_join(group_sets, suffix = c("_x", "_y")) |> filter(my_group_x < my_group_y) |> # 避免重复组合 mutate(combination = paste(my_group_x, my_group_y, sep = "*"))
步骤3:计算交集、较小集合大小与重叠系数
通过map2_int对每一对集合计算交集长度,再推导所需指标:
result <- pairwise_groups |> mutate( n_instersected_members = map2_int(city_set_x, city_set_y, ~length(intersect(.x, .y))), size_of_smaller_set = pmin(set_size_x, set_size_y), overlap_coeff = n_instersected_members / size_of_smaller_set ) |> select(combination, n_instersected_members, size_of_smaller_set, overlap_coeff)
运行后结果与预期一致:
result ## # A tibble: 3 × 4 ## combination n_instersected_members size_of_smaller_set overlap_coeff ## <chr> <int> <int> <dbl> ## 1 bar*baz 2 4 0.5 ## 2 foo*bar 2 5 0.4 ## 3 foo*baz 3 4 0.75
简化链式调用版
如果想更简洁,可将所有步骤合并为一条链式操作:
df_example |> group_by(my_group) |> summarise( city_set = list(unique(cities)), set_size = n_distinct(cities), .groups = "drop" ) |> cross_join(., ., suffix = c("_x", "_y")) |> filter(my_group_x < my_group_y) |> mutate( combination = paste(my_group_x, my_group_y, sep = "*"), n_instersected_members = map2_int(city_set_x, city_set_y, ~length(intersect(.x, .y))), size_of_smaller_set = pmin(set_size_x, set_size_y), overlap_coeff = n_instersected_members / size_of_smaller_set ) |> select(combination, n_instersected_members, size_of_smaller_set, overlap_coeff)
关键说明
list(unique(cities)):将每组城市转为列表形式的集合,方便后续交集运算cross_join + filter(my_group_x < my_group_y):高效生成无重复的集合对map2_int:批量处理每一对集合的交集计算,返回整数结果pmin:直接取两个集合大小的最小值,简化条件判断逻辑
内容的提问来源于stack exchange,提问作者Emman
相关产品推荐
相关产品推荐

