我有一个非空 Dataframe df1
df1 <- read.table(header = TRUE, text = "
V1 V2
1 5
2 6
3 7
4 8
")
以及两个空 Dataframe df2.a
和df2.b
,即,
df2.a <- data.frame(V1 = integer(0), V2 = integer(0), V3 = integer(0), V4 = integer(0))
df2.b <- structure(list(V1 = NULL, V2 = NULL, V3 = NULL, V4 = NULL), row.names = c(NA, 0L), class = "data.frame")
其中df2.a
和df2.b
看起来几乎没有区别(唯一的区别是使用dput(df2.a)
和dput(df2.b)
时显示的)
> df2.a
[1] V1 V2 V3 V4
<0 rows> (or 0-length row.names)
> df2.b
[1] V1 V2 V3 V4
<0 rows> (or 0-length row.names)
但是,当我尝试将df1
与df2.a
或df2.b
合并时,出现了一些奇怪的情况
> merge(df1,df2.a,all = TRUE)
V1 V2 V3 V4
1 1 5 NA NA
2 2 6 NA NA
3 3 7 NA NA
4 4 8 NA NA
> merge(df1,df2.b,all = TRUE)
V1 V2 V4
1 1 5 NA
2 2 6 NA
3 3 7 NA
4 4 8 NA
正如您所看到的,当将df1
与df2.b
合并时,V3
被丢弃,而所需的输出应该类似于merge(df1,df2.a,all = TRUE)
的输出。
有人能解释一下吗?如果有解决问题的变通方法,在使用merge
超过df1
和df2.b
时表示赞赏。
3条答案
按热度按时间ds97pgxw1#
这是一个复杂的问题。错误步骤发生在
base::merge
的这一行:当您将
df2.b
作为y
参数传递给merge
时,这一行实际上会产生一个无效的 Dataframe ,正如您在浏览器中看到的那样:如果我们跟踪整个逻辑,我们可以看到我们可以通过调用在调试器外部重现错误:
然而,对于
db2.a
,我们没有得到这个问题:这是为什么呢?即使
df2.a
和df2.b
在打印 Dataframe 时看起来相同,但它们并不相同。一个空的数字向量与NULL
不太一样。主要的区别(也是导致这里问题的原因)是索引一个空的数字向量会给你一个非零长度的NA
值,而NULL会给你一个NULL
值。我想这是预期的行为。问题是R完全允许
NULL
作为dataframe列。我很惊讶这种事不经常发生。pu3pd22g2#
我跟踪了这个问题的原因,发现这个错误出现在
merge.data.frame
的以下部分:要显示问题,请尝试以下代码:
因此,此问题是由
[.data.frame
引起的。[.data.frame
的一段源代码是:这里
x
是要返回的结果 Dataframe 。它现在只有V3和V4列。xx
是输入data.frame的副本(在我们的例子中是df2.b)。这个for循环首先将NULL
分配给x
的第1列。因此,在该步骤中删除V3
。接下来,for循环将NULL
分配给x
的第2列。然而,由于V3消失了,所以没有列2。因此,x不会受到影响。这就是为什么我们得到了意想不到的结果。如果我们将
df1
和df2.b
设置为data.table
,合并它们将抛出错误。似乎data.table::merge
对这种情况的处理更为严格。错误消息将帮助我们避免获得意外结果。wz3gfoph3#
我会尽我所能提供一个完整的答案...
(When我发布了答案,我注意到我加入派对太晚了:D我会留下答案,因为我希望它会提供另一个有趣的观点)
调试合并
让我们先看看
merge
函数。具体来说,这里调用的方法是merge.data.frame
(base
包的导出函数)。如果你调试
merge.data.frame(df1,df2.b,all = TRUE)
,你会在第124行看到它被调用:y
与df2.b
相同。由于
m$yi
等于integer(0)
,all.x
等于TRUE
,all.y
等于FALSE
,因此可以简化为:它的输出是:
所以这就是
merge
没有告诉我们的幕后“问题”。让我们深入调查一下。
首先,实际的输出不是这样的,这只是欺骗我们眼睛的默认
print.data.frame
方法。的输出
是
NULL不会被重复,这是有意义的,因为你不能用两个NULL来做一个向量
正如警告所说,data.frame已损坏,打印可能有故障(确实如此!).
这是因为data.frame是以一种巧妙的方式创建的,使用的是
structure()
而不是data.frame()
或as.data.frame()
,这不会导致您使用该结构。这是一个关于你如何只看到一列的故事。
问题是为什么
因此,我们需要看看函数
[.data.frame
。DEBUG [.data.frame]
让我们先观察一些行为。
最后三个看起来很出乎意料。最后一个是我们的案例。和我们之前看到的一样。
如果尝试调试:
你会在第109行找到这段代码:
可读性更强:
此时,变量如下:
如果你用这些变量运行for循环,你会得到x是:
看来我们找到消失的柱子的来源了。
现在,问题究竟出在哪里?
当
j == 1
,x[[j]] <- ...
等于x$V1 <- NULL
时,在R中允许从列表中删除元素V1。因此x变成了一个只有一个元素的列表,this:当
j == 2
,x[[j]]
不再存在,因为在第一次循环中,第一个项目被删除,现在只有一个可用。因此,R试图分配一个新的第二项,但由于您不能将NULL分配为项[如下所示:x[[2]] <- NULL
],则x不会改变。因此,您只有一列。
总结
merge
有奇怪行为的原因是因为您以不正确的方式创建了 Dataframe 。merge
并没有告诉你 Dataframe 实际上已经损坏,它甚至在不应该的时候做了一些事情。最终,
[
及其处理子集的方式定义了其中一列的最终丢失。DPLYR
说实话,就用
dplyr::full_join(df1, df2.b)
。它没有给出任何理所当然的东西,实际上会导致您从一开始就预期的错误: