为何dplyr分组验证DataFrame与原型一致性时总是返回FALSE?
问题描述
我有一个名为data的DataFrame,结构如下:
vp | v1 | v2 | v3 | v4 0 | a | b | c | ... 0 | d | e | f | ... 0 | g | h | i | ... 1 | a | b | c | ... 1 | d | e | f | ... 1 | g | h | i | ... ...
我希望验证每个vp分组下的v1、v2、v3组合是否符合要求,为此创建了保存必要组合的原型DataFrame prototype:
v1 | v2 | v3 a | b | c d | e | f g | h | i
我尝试通过dplyr的group_by和summarise函数,结合identical验证每个vp分组对应的v1、v2、v3部分与排序后的prototype是否一致:
data %>% group_by(vp) %>% summarise(identical = identical(. %>% # as.data.frame() %>% select(v1, v2, v3) %>% arrange(v1, v2, v3), prototype %>% arrange(v1, v2, v3))
预期可以验证分组后的对应数据框部分与原型一致,但结果始终返回FALSE。而手动使用filter(vp == ...)逐个分组验证时,结果符合预期。请问问题出在哪里?如何用dplyr/tidyr风格实现预期验证?
最小可复现代码(MVC)
library(dplyr) data <- data.frame(vp = c(rep(0,3), rep(1,3), rep(2,3)), v1 = rep(c("a", "d", "g"), 3), v2 = rep(c("b", "e", "h"), 3), v3 = c(rep(c("c", "f", "i"), 2), c("c", "f", "x"))) prototype <- data.frame(v1 = c("a", "d", "g"), v2 = c("b", "e", "h"), v3 = c("c", "f", "i")) expected_result <- data.frame(vp=c(0,1,2), identical=c(TRUE, TRUE, FALSE)) data %>% group_by(vp) %>% summarise(identical = identical(. %>% select(v1, v2, v3) %>% arrange(v1, v2, v3), prototype %>% arrange(v1, v2, v3)))
问题原因与解决方案
问题根源
identical是严格对象比较函数,不仅会对比数据内容,还会检查对象的类型、属性甚至内部环境:
- 在
group_by的分组环境中,.指代的子数据框带有dplyr的分组元数据(比如.group属性),和prototype的普通data.frame属性不一致 - 即使手动转成data.frame,分组环境下的子对象仍可能残留dplyr内部属性,导致
identical返回FALSE
另外,你提供的expected_result中vp值有误,正确应为0、1、2而非1、2、3。
可行解决方案
方案1:用all.equal替换identical
all.equal会忽略无关属性差异,仅对比数据内容的等价性,需将其返回的字符串结果转为布尔值:
data %>% group_by(vp) %>% summarise(identical = all.equal( . %>% select(v1, v2, v3) %>% arrange(v1, v2, v3), prototype %>% arrange(v1, v2, v3) ) == TRUE)
方案2:扁平化向量后比较
将分组数据和原型都转换为扁平化的原子向量,再用identical对比:
# 预处理得到排序后的原型向量 proto_vec <- prototype %>% arrange(v1, v2, v3) %>% unlist() data %>% group_by(vp) %>% summarise(identical = identical( . %>% select(v1, v2, v3) %>% arrange(v1, v2, v3) %>% unlist(), proto_vec ))
方案3:逐元素+行数双重验证
先确认分组数据和原型行数一致,再逐元素对比内容,逻辑更直观:
data %>% group_by(vp) %>% summarise(identical = { group_data <- select(cur_data(), v1, v2, v3) %>% arrange(v1, v2, v3) proto_sorted <- prototype %>% arrange(v1, v2, v3) nrow(group_data) == nrow(proto_sorted) && all(group_data == proto_sorted) })
最终结果
以上方案均会返回符合预期的结果:
# A tibble: 3 × 2 vp identical <dbl> <lgl> 1 0 TRUE 2 1 TRUE 3 2 FALSE
内容的提问来源于stack exchange,提问作者derM
相关产品推荐
相关产品推荐

