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,但现已在开发版本中修复。
如果我 运行 下面这个简单的例子,我会得到预期的输出:
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,但现已在开发版本中修复。