将一个非空的 data.frame 与一个空的合并时的奇怪行为

Tho*_*ing 13 merge r dataframe

我有一个非空数据框 df1

df1 <- structure(list(V1 = 1:4, V2 = 5:8), class = "data.frame", row.names = c(NA, 
-4L))

> df1
  V1 V2
1  1  5
2  2  6
3  3  7
4  4  8
Run Code Online (Sandbox Code Playgroud)

和两个空的数据帧df2.adf2.b,即

df2.a <- structure(list(V1 = integer(0), V2 = integer(0), V3 = integer(0), V4 = integer(0)), row.names = integer(0), class = "data.frame")


df2.b <- structure(list(V1 = NULL, V2 = NULL, V3 = NULL, V4 = NULL), row.names = c(NA, 0L), class = "data.frame")
Run Code Online (Sandbox Code Playgroud)

where df2.aanddf2.b看起来几乎没有区别(唯一的区别是使用dput(df2.a)and时显示的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)
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试df1df2.aor合并时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
Run Code Online (Sandbox Code Playgroud)

如您所见,V3df1与合并时被丢弃df2.b,而所需的应该是类似于merge(df1,df2.a,all = TRUE).

有人可以解释一下吗?感谢您在使用mergeoverdf1df2.b.

All*_*ron 13

这是一个复杂的问题。错误步骤发生在以下行base::merge

y <- y[c(m$yi, if (all.x) rep.int(1L, nxx), if (all.y) m$y.alone), 
            -by.y, drop = FALSE]
Run Code Online (Sandbox Code Playgroud)

当您将参数df2.b作为y参数传递给 时merge,这一行实际上会生成一个无效的数据框,正如您在浏览器中所看到的:

Browse[2]> y
#>        V4
#> NA   NULL
#> NA.1 <NA>
#> NA.2 <NA>
#> NA.3 <NA>
#> Warning message:
#> In format.data.frame(if (omit) x[seq_len(n0), , drop = FALSE] else x,  :
#>  corrupt data frame: columns will be truncated or padded with NAs
Run Code Online (Sandbox Code Playgroud)

如果我们通过跟踪逻辑,我们可以看到我们可以通过调用在调试器之外重现错误:

Browse[2]> y
#>        V4
#> NA   NULL
#> NA.1 <NA>
#> NA.2 <NA>
#> NA.3 <NA>
#> Warning message:
#> In format.data.frame(if (omit) x[seq_len(n0), , drop = FALSE] else x,  :
#>  corrupt data frame: columns will be truncated or padded with NAs
Run Code Online (Sandbox Code Playgroud)

然而,我们没有遇到这个问题db2.a

df2.b[c(1, 1, 1, 1), -c(1:2), drop = FALSE]
#>        V4
#> NA   NULL
#> NA.1 <NA>
#> NA.2 <NA>
#> NA.3 <NA>
#> Warning message:
#> In format.data.frame(if (omit) x[seq_len(n0), , drop = FALSE] else x,  :
#>  corrupt data frame: columns will be truncated or padded with NAs
Run Code Online (Sandbox Code Playgroud)

那么这是为什么呢?尽管打印数据框时df2.adf2.b看起来相同,但它们并不相同。空的数字向量与NULL. 主要区别(导致问题的那个)是索引一个空的数字向量给你一个非零长度的NA值,而 NULL 给你一个单一的NULL值。

df2.a[c(1, 1, 1, 1), -c(1:2), drop = FALSE]
#>      V3 V4
#> NA   NA NA
#> NA.1 NA NA
#> NA.2 NA NA
#> NA.3 NA NA
Run Code Online (Sandbox Code Playgroud)

所以我想这是预期的行为。问题是 R 完全允许NULL作为数据框列。我很惊讶这种事情不会经常发生。

  • R 并不真正允许 `NULL` 作为数据框列,您不能使用惯用的构造函数 `data.frame()` 甚至 `x["col"] &lt;- list(NULL )`。我相信我们可以因此认为这个对象已损坏。但是 print 方法(甚至 as.data.frame 方法)在此类对象上不会失败。我们可能会认为这是一个缺点(在这种情况下我想我会的!)但是验证对象需要资源,因此如果它是一个错误,那么这可能是有争议的。 (2认同)

mt1*_*022 5

我跟踪了这​​个问题的原因,发现这个错误出现在以下部分merge.data.frame

y <- y[c(m$yi, if (all.x) rep.int(1L, nxx), if (all.y) m$y.alone), 
            -by.y, drop = FALSE]
Run Code Online (Sandbox Code Playgroud)

要显示问题,请尝试以下代码:

df2.b[rep(1, 4), -(1:2), drop = FALSE]
#        V4
# NA   NULL
# NA.1 <NA>
# NA.2 <NA>
# NA.3 <NA>
# Warning message:
# In format.data.frame(if (omit) x[seq_len(n0), , drop = FALSE] else x,  :
#   corrupt data frame: columns will be truncated or padded with NAs

df2.a[rep(1, 4), -(1:2), drop = FALSE]
#    V3 V4
# 1: NA NA
# 2: NA NA
# 3: NA NA
# 4: NA NA
Run Code Online (Sandbox Code Playgroud)

因此,此问题是由[.data.frame. 源代码的一部分[.data.frame是:

for (j in seq_along(x)) {
        xj <- xx[[sxx[j]]]
        x[[j]] <- if (length(dim(xj)) != 2L){
            xj[i]
        }else{ xj[i, , drop = FALSE]}
    }
Run Code Online (Sandbox Code Playgroud)

在这里,x是要返回的结果 data.frame。它现在只有 V3 和 V4 列。xx是输入 data.frame 的副本(在我们的例子中是 df2.b)。此 for 循环将首先分配NULL给 的第1 列x。因此,V3在这一步被删除。接下来,for 循环分配NULL给 的第 2 列x。但是,随着 V3 消失,第 2 列没有了。因此,x 不会受到影响。这就是为什么我们会得到意想不到的结果。

如果我们将df1和设置df2.bdata.table,则合并它们将引发错误。似乎data.table::merge对这种情况处理得更严格。错误消息将帮助我们避免获得意外结果。