比较 R 中相等的 2 个数据帧

Compare 2 dataframes for equality in R

提问人:Krithi07 提问时间:6/13/2019 更新时间:1/22/2021 访问量:1405

问:

我有 2 个数据帧,有 2 个相同的列。我想检查数据集是否相同。原始数据集有一些 700K 记录,但我正试图找到一种使用虚拟数据集的方法

我尝试使用比较、相同、全部、all_equal等。他们都没有给我一个 True。

虚拟数据集是 -

a <- data.frame(x = 1:10, b = 20:11)
c <- data.frame(x = 10:1, b = 11:20)

all(a==c)
[1] FALSE

compare(a,c)
FALSE [FALSE, FALSE]

identical(a,c)
[1] FALSE

 all.equal(a,c)
[1] "Component “x”: Mean relative difference: 0.9090909" "Component “b”: Mean relative difference: 0.3225806"

数据集完全相同,只是记录的顺序不同。如果这些函数仅在数据集是彼此的镜像时才起作用,那么我必须尝试其他方法。如果是这种情况,有人可以帮忙如何获得这 2 个数据集的 True(无序)

R DataFrame 比较 相等性

评论

0赞 Rui Barradas 6/13/2019
用户@Gregor的这个评论对吗?你想要和不平等吗?XZ
0赞 Krithi07 6/13/2019
是的,我不希望 x 和 z 相等。我只想比较相同记录的数据帧,而不考虑它们的顺序。lapply sort 正在做的是它正在更改数据集记录本身。

答:

5赞 Gregor Thomas 6/13/2019 #1

dplyr在数据帧上的工作,我建议setdiff

library(dplyr)
nrow(setdiff(a, c)) == 0 & nrow(setdiff(c, a)) == 0
# [1] TRUE

请注意,这不会考虑重复行的数量。(即,如果一行有多个副本,并且该行只有一个副本,它仍将返回 )。不确定您希望如何处理重复的行...acTRUE

如果您确实关心具有相同数量的重复项,那么我建议两种可能性:(a) 添加一个 ID 列来区分重复项并使用上述方法,或者 (b) 排序、重置行名(令人讨厌)并使用 .identical

(a) 添加 ID 列

library(dplyr)
a_id = group_by_all(a) %>% mutate(id = row_number())
c_id = group_by_all(c) %>% mutate(id = row_number())
nrow(setdiff(a_id, c_id)) == 0 & nrow(setdiff(c_id, a_id)) == 0
# [1] TRUE

(b) 排序

a_sort = a[do.call(order, a), ]
row.names(a_sort) = NULL
c_sort = c[do.call(order, c), ]
row.names(c_sort) = NULL
identical(a_sort, c_sort)
# [1] TRUE
1赞 Rui Barradas 6/13/2019 #2

也许您需要一个在比较之前对列进行排序的函数。但是在大型数据帧上会很慢。

unordered_equal <- function(X, Y, exact = FALSE){
  X[] <- lapply(X, sort)
  Y[] <- lapply(Y, sort)
  if(exact) identical(X, Y) else all.equal(X, Y)
}

unordered_equal(a, c)
#[1] TRUE
unordered_equal(a, c, TRUE)
#[1] TRUE

a$x <- a$x + .Machine$double.eps
unordered_equal(a, c)
#[1] TRUE
unordered_equal(a, c, TRUE)
#[1] FALSE

评论

0赞 Gregor Thomas 6/13/2019
lapply(X, sort)将独立对列进行排序,这似乎很糟糕。给定 , , , 我认为 OP 希望 X 和 Y 为 TRUE,但 X 和 Z 为 FALSE,但这会说所有对都为 TRUE。X = data.frame(1:2, 1:2)Y = data.frame(2:1, 2:1)Z = data.frame(1:2, 2:1)
0赞 Gregor Thomas 6/13/2019
如果您使用 etc.,它将按所有列对行进行排序。do.call(order, X)
0赞 Rui Barradas 6/13/2019
@Gregor谢谢,我没有想到这一点。我已经在评论中询问了 OP 是否这是预期的问题。
0赞 Krithi07 6/13/2019
@Gregor,do.call(order, X) 似乎并没有按照我们想要的方式工作。x = data.frame(1:2, 2:1), do.call(order,x) 给出一个具有 2 个值的对象,(1,2)
0赞 Gregor Thomas 6/13/2019
“是”给出了行顺序,因此实际上要像我的答案一样对数据框进行重新排序。do.call(order, x)x[do.call(order, x), ]
0赞 jay.sf 6/13/2019 #3

基本上,您想要的可能是比较有序的基础矩阵。

all.equal(matrix(unlist(a[order(a[1]), ]), dim(a)),
          matrix(unlist(c[order(c[1]), ]), dim(c)))
# [1] TRUE
identical(matrix(unlist(a[order(a[1]), ]), dim(a)),
          matrix(unlist(c[order(c[1]), ]), dim(c)))
# [1] TRUE

为了方便起见,您可以将其包装成一个函数:

om <- function(d) matrix(unlist(d[order(d[1]), ]), dim(d))

all.equal(om(a), om(c))
# [1] TRUE
0赞 George Pipis 1/22/2021 #4

您可以使用名为 waldo 的新软件包

library(waldo)
a <- data.frame(x = 1:10, b = 20:11)
c <- data.frame(x = 10:1, b = 11:20)

compare(a,c)

你会得到:

`old$x`: 1 2 3 4 5 6 7 8 9 10 and 9 more...
`new$x`:                   10           ...

`old$b`: 20 19 18 17 16 15 14 13 12 11 and 9 more...
`new$b`: