Select 使用 dtplyr 连接冲突列后

Select after a join with conflicting columns with dtplyr

如果我 运行 下面这个简单的例子,我会得到预期的输出:

library(dplyr)
library(dtplyr)
library(data.table)

dt1 <- lazy_dt(data.table(a = 1:5, b = 6:10))
dt2 <- lazy_dt(data.table(a = letters[1:5], b = 6:10))

dt1 %>%
  left_join(
    dt2,
    by = "b"
  ) %>%
  as.data.table()
>     b a.x a.y
> 1:  6   1   a
> 2:  7   2   b
> 3:  8   3   c
> 4:  9   4   d
> 5: 10   5   e

请注意,使用添加 .x.y 后缀的标准 dplyr 格式正确管理冲突列 a

但是,如果我现在尝试删除其中一列:

dt1 %>%
  left_join(
    dt2,
    by = "b"
  ) %>%
  select(
    -a.y
  ) %>%
  as.data.table()
> Error in is_character(x) : object 'a.y' not found

有趣的是,如果我尝试 select a 列之一 (select(a.x)),我会得到同样的错误,但是...如果我改为尝试 select(a)(selecting 一个不应该再存在的列),我得到以下输出:

dt1 %>%
  left_join(
    dt2,
    by = "b"
  ) %>%
  select(
    a
  ) %>%
  as.data.table()
>    a.b
> 1:   1
> 2:   2
> 3:   3
> 4:   4
> 5:   5

其中 selected 列显然是 dt1$a,但由于某种原因,给定的列名称是 a.b。 (如果我尝试 select(a.b),我会得到相同的 object not found 错误)。

同时,如果我尝试删除 a,两个 a 列都会被删除:

dt1 %>%
  left_join(
    dt2,
    by = "b"
  ) %>%
  select(
    -a
  ) %>%
  as.data.table()
>     b
> 1:  6
> 2:  7
> 3:  8
> 4:  9
> 5: 10

那么,如何在表具有冲突(而不是连接依据)列的情况下将 select 用于连接?

编辑:

如某些答案中所述,我显然可以在 select 之前执行惰性求值,这很有效。但是,它会发出警告(因为我想将我的对象保留为 data.table,而不是 data.frame),所以它似乎不是预期的方法:

dt1 %>%
  left_join(
    dt2,
    by = "b"
  ) %>%
  as.data.table() %>%
  select(
    -a.x
  )
>     b a.y
> 1:  6   a
> 2:  7   b
> 3:  8   c
> 4:  9   d
> 5: 10   e
> Warning message:
> You are using a dplyr method on a raw data.table, which will call the data 
> frame implementation, and is likely to be inefficient.
> * 
> * To suppress this message, either generate a data.table translation with
> * `lazy_dt()` or convert to a data frame or tibble with
> * `as.data.frame()`/`as_tibble()`.

显然,left_join 不适用于 data.table,但适用于 data.frame(我以前不知道)。

因此,一种解决方案是:

library(dplyr)
library(dtplyr)
library(data.table)

dt1 <- lazy_dt(data.table(a = 1:5, b = 6:10))
dt2 <- lazy_dt(data.table(a = letters[1:5], b = 6:10))

as.data.frame(dt1) %>%
  left_join(as.data.frame(dt2), by = "b") %>%
  select(-a.y) %>%
  as.data.table()

关键是 dtplyr 使用 惰性求值 。更多这里 https://dtplyr.tidyverse.org/,但关键部分是:

Compared to the previous release, this version of dtplyr is a complete rewrite that focusses only on lazy evaluation triggered by use of lazy_dt(). This means that no computation is performed until you explicitly request it with as.data.table(), as.data.frame() or as_tibble().

在您的示例中,这意味着在 select 之前尚未评估联接,它们正在翻译并等待评估。翻译机制允许 dtplyr 更有效地将多个动词组合成一个动作。 (更多关于翻译的信息:https://dtplyr.tidyverse.org/articles/translation.html

有几种方法可以解决这个问题。最简单的是使用 as.data.frame().

向上移动评估
dt1 %>%
  left_join(
    dt2,
    by = "b"
  ) %>%
  as.data.frame() %>%
  select(-a.y)

另一种方法是获取 data.table 对象,然后使用 data.table 语法对列进行子集化。

这是 dtplyr (1.0.0) 当前版本中的一个 bug,但现已在开发版本中修复。