我有两个大数据框,
a
和b
,其中identical(a,b)
是TRUE
,all.equal(a,b)
也是,但是identical(digest(a),digest(b))
是FALSE
。是什么原因造成的?
更重要的是,我试图通过将摘要应用于行束来更深入地挖掘。令人难以置信的是,至少对我而言,子帧的摘要值一直到数据帧的最后一行都是一致的。
这是一系列比较:
> identical(a, b)
[1] TRUE
> all.equal(a, b)
[1] TRUE
> digest(a)
[1] "cac56b06078733b6fb520442e5482684"
> digest(b)
[1] "fdd5ab78ca961982d195f800e3cf60af"
> digest(a[1:nrow(a),])
[1] "e44f906723405756509a6b17b5949d1a"
> digest(b[1:nrow(b),])
[1] "e44f906723405756509a6b17b5949d1a"
我能想到的每一种方法都表明这两个对象是相同的,但它们的摘要值不同。数据框是否还有其他原因会产生这种差异?
有关详细信息:对象约为 10M 行 x 12 列。这是
str()
的输出:
'data.frame': 10056987 obs. of 12 variables:
$ V1 : num 1 11 21 31 41 61 71 81 91 101 ...
$ V2 : num 1 1 1 1 1 1 1 1 1 1 ...
$ V3 : num 2 3 2 3 4 5 2 4 2 4 ...
$ V4 : num 1 1 1 1 1 1 1 1 1 1 ...
$ V5 : num 1.8 2.29 1.94 2.81 3.06 ...
$ V6 : num 0.0653 0.0476 0.0324 0.034 0.0257 ...
$ V7 : num 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ...
$ V8 : num 0.00653 0.00476 0.00324 0.0034 0.00257 ...
$ V9 : num 1.8 2.3 1.94 2.81 3.06 ...
$ V10: num 0.1957 0.7021 0.0604 0.1866 0.9371 ...
$ V11: num 1704 1554 1409 1059 1003 ...
$ V12: num 23309 23309 23309 23309 23309 ...
> print(object.size(a), units = "Mb")
920.7 Mb
更新 1:一时兴起,我将这些转换为矩阵。摘要是相同的。
> aM = as.matrix(a)
> bM= as.matrix(b)
> identical(aM,bM)
[1] TRUE
> digest(aM)
[1] "c5147d459ba385ca8f30dcd43760fc90"
> digest(bM)
[1] "c5147d459ba385ca8f30dcd43760fc90"
然后我尝试转换回数据框,摘要值相等(并且等于
a
的先前值)。
> aMF = as.data.frame(aM)
> bMF = as.data.frame(bM)
> digest(aMF)
[1] "cac56b06078733b6fb520442e5482684"
> digest(bMF)
[1] "cac56b06078733b6fb520442e5482684"
所以,
b
长得像个坏小子,有着丰富多彩的过去。 b
来自一个更大的数据框,比如 B
。我只取了 B
中出现的 a
的列,并检查它们是否相等。嗯,他们是平等的,但有不同的摘要。我转换了列名(从“InformativeColumnName1”到“V1”等),只是为了避免可能出现的任何问题 - 尽管 all.equal
和 identical
倾向于指出列名不同的时间。
因为我在两个不同的程序上工作并且不能同时访问
a
和b
,所以我最容易使用摘要值来检查计算。但是,我从数据框中提取列然后对其应用 digest()
的方式似乎有些奇怪。
答案: 事实证明,令我惊讶的是(沮丧,恐惧,尴尬,随便你说),
identical
对属性非常宽容。我原以为只有all.equal
对属性宽容。
这是在Tommy的建议下发现的
identical(d1, d2, attrib.as.set=FALSE)
。运行 attributes(a)
是一个非常糟糕的主意:大量的行名在 Ctrl-C 可以中断它之前花费了一段时间。这是names(attributes())
的输出:
> names(attributes(a))
[1] "names" "row.names" "class"
> names(attributes(b))
[1] "names" "class" "row.names"
顺序不同!感谢
digest()
对我直言不讳。
更新
为了帮助其他人解决这个问题,似乎只需重新排列属性就足以获得相同的哈希值。由于修改属性顺序对我来说是新的,这可能会破坏某些东西,但它适用于我的情况。请注意,如果对象很大,它会有点耗时;我不知道执行此操作的更快方法。 (我也希望转向使用矩阵或数据表而不是数据框,这可能是避免数据框的另一个动机。)
tmpA0 = attributes(a)
tmpA1 = tmpA0[sort(names(tmpA0))]
a2 = a
attributes(a2) = tmpA1
tmpB0 = attributes(b)
tmpB1 = tmpB0[sort(names(tmpB0))]
b2 = b
attributes(b2) = tmpB1
digest(a2) # e04e624692d82353479efbd713ec03f6
digest(b2) # e04e624692d82353479efbd713ec03f6
identical(b,b2, attrib.as.set = FALSE) # FALSE
identical(b,b2, attrib.as.set = TRUE) # TRUE
identical(a2,b2, attrib.as.set = FALSE) # TRUE
没有实际的 data.frames 当然很难知道,但一个区别可能是属性的 order。
identical
默认忽略它,但设置 attrib.as.set=FALSE
可以改变它:
d1 <- structure(1, foo=1, bar=2)
d2 <- structure(1, bar=2, foo=1)
identical(d1, d2) # TRUE
identical(d1, d2, attrib.as.set=FALSE) # FALSE
我们的 digest 包使用内部 R 函数
serialize()
来获取我们提供给哈希生成函数(md5、sha1、...)的内容。
所以我强烈怀疑可能有不同的属性。在您可以构建不依赖于您的 1e7 x 12 数据集的可重现的东西之前,我们几乎无能为力。
此外,
digest()
函数可以输出中间结果和(从最近的 0.5.1 版本开始)甚至 raw
向量。这可能会有所帮助。
最后,您可以随时离线联系我们(作为包维护者/作者),这恰好是 R land 中推荐的方式,StackOverflow 的受欢迎程度无法承受。
如果您从已有的数据框重新生成一个数据框,属性的顺序会重置,如下所示:
reset.data.frame = data.frame(col1 = prob.data.frame$col1,
col2 = prob.data.frame$col2) # etc.
所以你只需要在每次哈希之前使用这个技巧重置属性,它应该可以工作