如何用tidyverse实现向量与多值数据框列的交集计数?
用tidyverse统计每行字符串中指定元素的数量
初始变量与需求
给定R语言变量:
vals <- c("b", "c") foo <- data.frame(x=c("a|b", "b|c", "c|d", "e|f|g"))
需要为foo新增一列y,统计每行x中包含vals内元素的个数,期望结果如下:
> foo2 x y 1 a|b 1 2 b|c 2 3 c|d 1 4 e|f|g 0
失败的尝试及原因
尝试1:直接对列表列使用intersect
library(dplyr) library(magrittr) foo2 <- foo %>% mutate(x1=str_split(x, "\\|"), y=intersect(vals, x1))
报错信息:
Error in `mutate()`: ℹ In argument: `y = intersect(vals, x1)`. Caused by error: ! `y` must be size 4 or 1, not 0.
原因:str_split返回的是列表列,intersect无法直接处理列表结构,需要逐行对列表元素操作。
尝试2:固定取第一行的拆分结果
foo2 <- foo %>% mutate(x1=str_split(x, "\\|"), y=intersect(vals, x1[[1]]))
不符合预期的结果:
> foo2 x x1 y 1 a|b a, b b 2 b|c b, c b 3 c|d c, d b 4 e|f|g e, f, g b
原因:x1[[1]]仅提取了第一行的拆分向量,所有行都基于这个值计算,导致结果错误。
正确的tidyverse实现
解法1:分步实现(清晰易懂)
library(dplyr) library(stringr) library(purrr) foo2 <- foo %>% # 将每行x按|拆分为列表列 mutate(x_split = str_split(x, "\\|")) %>% # 逐行计算拆分结果与vals的交集长度 mutate(y = map_int(x_split, ~ length(intersect(vals, .x)))) %>% # 可选:移除中间辅助列x_split select(-x_split) # 查看最终结果 foo2
输出符合预期:
x y 1 a|b 1 2 b|c 2 3 c|d 1 4 e|f|g 0
解法2:简化版(无中间列)
将拆分与计算合并为一步,代码更简洁:
foo2 <- foo %>% mutate(y = map_int(str_split(x, "\\|"), ~ length(intersect(vals, .x))))
核心原理
str_split(x, "\\|"):将每行字符串按|拆分,返回列表列,每个元素是该行拆分后的字符串向量。map_int(...):遍历列表列的每个元素,对每个拆分后的向量计算与vals的交集长度,返回整数向量作为新列y的值。
内容的提问来源于stack exchange,提问作者dfrankow
相关产品推荐
相关产品推荐

