在dplyr::case_when()左侧使用列表列作为输入遇问题
解决dplyr::case_when处理列表列时的逐行匹配问题
问题原因
你遇到的核心问题是:case_when(a %in% b ~ 1, TRUE ~ 0)里的a %in% b并非逐行判断a的值是否在对应行的b列表向量中,而是把整个a向量和整个b列表做全局匹配。另外,tbl$b[1]返回的是长度为1的子列表(而非列表内的数值向量),%in%会判断1是否属于这个子列表的元素,而非判断1是否在列表内的向量中,这就是tbl$a[1] %in% tbl$b[1]返回FALSE的原因;而tbl$b[[1]]能提取出列表里的数值向量,此时判断才会正确。
解决方案
方法1:rowwise()+case_when实现逐行判断
通过rowwise()让dplyr按行处理,此时b会被识别为当前行的列表元素,再用[[1]]提取内部向量:
tbl %>% rowwise() %>% mutate(a_in_b = case_when(a %in% b[[1]] ~ 1, TRUE ~ 0)) %>% ungroup() # 记得取消按行分组,避免后续操作受影响
输出结果:
# A tibble: 3 × 3 a b a_in_b <dbl> <list> <dbl> 1 1 <dbl [3]> 1 2 2 <dbl [3]> 0 3 3 <dbl [3]> 1
方法2:purrr::map2_int直接生成数值列
比case_when更简洁,直接逐行匹配并返回整数结果,无需分组:
tbl %>% mutate(a_in_b = map2_int(a, b, \(x, y) ifelse(x %in% y, 1, 0)))
这个方法扩展方便,若要处理多列表列,只需调整lambda函数的逻辑即可。
方法3:dplyr::if_any/if_all适配多列表列场景
如果实际数据有多列表列(比如b、c、d都是列表列),要判断a是否存在于任意一个对应行的列表向量中,可结合rowwise()和if_any:
# 先新增示例列表列c和d tbl = tbl %>% mutate(c = list(c(2,5), c(3,6), c(4,7)), d = list(c(3,8), c(1,9), c(1,2))) tbl %>% rowwise() %>% mutate(a_in_any = if_any(c(b, c, d), ~a %in% .x) %>% as.integer()) %>% ungroup()
输出结果:
# A tibble: 3 × 5 a b c d a_in_any <dbl> <list> <list> <list> <int> 1 1 <dbl [3]> <dbl [2]> <dbl [2]> 1 2 2 <dbl [3]> <dbl [2]> <dbl [2]> 1 3 3 <dbl [3]> <dbl [2]> <dbl [2]> 1
关键知识点总结
tbl$b[i]返回包含第i个元素的子列表,tbl$b[[i]]才会提取第i个列表元素内的实际向量- 处理列表列的逐行操作时,要么用
rowwise()让dplyr按行处理,要么用purrr的map系列函数做元素级映射
内容的提问来源于stack exchange,提问作者RichPatter
相关产品推荐
相关产品推荐

