R中如何按list类型列条件筛选data.table数据行
data.table筛选list类型列的实现方案
问题背景
现有一个data.table,部分列结构如下:
| Id | Period | Product |
|---|---|---|
| 1000797366 | 2018-Q1 | UG10000-WISD |
| 1000797366 | 2018-Q1 | NX11100, UG10000-WISD, UG12210 |
| 1000797366 | 2018-Q1 | UG10000-WISD, UG12210 |
| 1000797366 | 2018-Q1 | UG10000-WISD, UG12210 |
| 1000797366 | 2018-Q1 | UG12210 |
| 1000797366 | 2018-Q1 | NX11100 |
| 1000797366 | 2018-Q1 | NX11100 |
其中Product列是list()类型,后续业务需要保留该数据类型。
筛选要求:
Period取值属于c("2018-Q1")Product列的行内元素包含目标向量c("UG12210","UG10000-WISD")中的任意值
预期筛选后返回5行数据,排除仅包含NX11100的两行。
之前尝试的两种写法均无法得到正确结果:
data_test[Period %in% c("2018-Q1") & is.element("UG12210",Product),] data_test[Period %in% c("2018-Q1") & Product %in% c("UG12210"),]
可复现问题的测试数据:
structure( list( Id = c("1000797366", "1000797366", "1000797366", "1000797366", "1000797366", "1000797366", "1000797366"), Period = c("2018-Q1", "2018-Q1", "2018-Q1", "2018-Q1", "2018-Q1", "2018-Q1", "2018-Q1"), Product = list("UG10000-WISD", c("NX11100", "UG10000-WISD", "UG12210"), c("UG10000-WISD", "UG12210"), c("UG10000-WISD", "UG12210"), "UG12210", "NX11100", "NX11100") ), row.names = c(NA,-7L), class = c("data.table", "data.frame") )
错误原因
两种错误写法的核心问题是没有针对list列做逐行的子元素判断:
is.element("UG12210",Product):对整个Product列(长度为7的list)做顶层元素匹配,返回单个逻辑值,无法生成和行数等长的筛选向量,且不会进入list子元素内部判断Product %in% c("UG12210"):把list中每个子元素作为整体做相等匹配,只有子元素是长度为1、值恰好为UG12210的行会被命中,多产品的行会因为子元素是长度>1的向量,整体不匹配被漏掉
正确实现
不需要修改Product列的list类型,逐行判断子元素和目标向量是否存在交集即可。
写法1:基础R实现(无额外依赖,性能好)
用sapply逐行遍历Product列,搭配data.table原生支持的%chin%(字符向量匹配,性能优于基础%in%)判断:
library(data.table) # 定义筛选目标 target_period <- "2018-Q1" target_prod <- c("UG12210", "UG10000-WISD") # 执行筛选 result <- data_test[ Period %chin% target_period & sapply(Product, \(row_prod) any(row_prod %chin% target_prod)), ]
运行后返回的结果完全符合预期,Product列保留原有list类型。
写法2:purrr实现(代码简洁)
如果日常使用tidyverse生态,可以用purrr::map_lgl替代sapply做逐行逻辑判断,逻辑完全一致:
library(data.table) library(purrr) target_period <- "2018-Q1" target_prod <- c("UG12210", "UG10000-WISD") result <- data_test[ Period %chin% target_period & map_lgl(Product, ~ any(.x %in% target_prod)), ]
内容的提问来源于stack exchange,提问作者Shubham
相关产品推荐
相关产品推荐

