在 R dbplyr 中跨列(按行)操作
Posted
技术标签:
【中文标题】在 R dbplyr 中跨列(按行)操作【英文标题】:Operating across columns (rowwise) in R dbplyr 【发布时间】:2021-02-05 21:32:29 【问题描述】:我开了一个issue for this。但如果这是操作员错误而不是包的问题,我会在这里发布编码建议。
dbplyr
的最新 (v2.1) 更新应该增加对 across
和相关功能的支持。但是,当我尝试使用它时,仍然会出现错误。
我应该使用不同的语法吗?有解决办法吗?
library(dplyr, warn.conflicts = FALSE)
library(bigrquery)
set.seed(02042021)
Sys.setenv(BIGQUERY_TEST_PROJECT = "elite-magpie-257717")
bq_deauth()
bq_auth(email="ariel.balter@gmail.com")
conn = DBI::dbConnect(
bigrquery::bigquery(),
project = bq_test_project(),
dataset = "test_dataset"
)
my_table = data.frame(
A = replicate(10, paste(sample(letters[1:3], 3, replace=T), collapse="")),
B = replicate(10, paste(sample(letters[1:3], 3, replace=T), collapse="")),
C = replicate(10, paste(sample(letters[1:3], 3, replace=T), collapse="")),
D = runif(10)
)
my_table
#> A B C D
#> 1 bcb cbb bbb 0.3620390
#> 2 aac aac bba 0.5505868
#> 3 aca abb bcb 0.4028455
#> 4 bca baa bbb 0.3247477
#> 5 bcc cac ccc 0.6861223
#> 6 cac bba baa 0.6970764
#> 7 bcb bbc acc 0.6873332
#> 8 bca acb acb 0.5391651
#> 9 cba ccc abc 0.9442450
#> 10 cca cbc bcc 0.6319561
my_table %>%
mutate(
has_ab = if_any(everything(), ~grepl("ab", .))
)
#> A B C D has_ab
#> 1 bcb cbb bbb 0.3620390 FALSE
#> 2 aac aac bba 0.5505868 FALSE
#> 3 aca abb bcb 0.4028455 TRUE
#> 4 bca baa bbb 0.3247477 FALSE
#> 5 bcc cac ccc 0.6861223 FALSE
#> 6 cac bba baa 0.6970764 FALSE
#> 7 bcb bbc acc 0.6873332 FALSE
#> 8 bca acb acb 0.5391651 FALSE
#> 9 cba ccc abc 0.9442450 TRUE
#> 10 cca cbc bcc 0.6319561 FALSE
my_table %>%
mutate(
has_ab = if_any(where(is.numeric), ~grepl("ab", .))
)
#> A B C D has_ab
#> 1 bcb cbb bbb 0.3620390 FALSE
#> 2 aac aac bba 0.5505868 FALSE
#> 3 aca abb bcb 0.4028455 FALSE
#> 4 bca baa bbb 0.3247477 FALSE
#> 5 bcc cac ccc 0.6861223 FALSE
#> 6 cac bba baa 0.6970764 FALSE
#> 7 bcb bbc acc 0.6873332 FALSE
#> 8 bca acb acb 0.5391651 FALSE
#> 9 cba ccc abc 0.9442450 FALSE
#> 10 cca cbc bcc 0.6319561 FALSE
dbRemoveTable(
conn=conn,
name="test_dataset.mytable",
value=my_table,
overwrite=T
)
dbWriteTable(
conn=conn,
name="test_dataset.mytable",
value=my_table,
overwrite=T
)
my_table_bq = tbl(conn, "mytable")
my_table_bq %>%
mutate(
has_ab = if_any(everything(), ~grepl("ab", .))
)
#> Error in UseMethod("escape"): no applicable method for 'escape' applied to an object of class "formula"
my_table_bq %>%
mutate(
has_ab = if_any(where(is.numeric), ~grepl("ab", .))
)
#> Error in UseMethod("escape"): no applicable method for 'escape' applied to an object of class "function"
由reprex package (v1.0.0) 于 2021-02-05 创建
【问题讨论】:
我试过没有公式,只是赋值TRUE
/ FALSE
- 它说Function not found: if_any at [1:28] [invalidQuery]
另外,我认为 Bigquery 的更多问题是不支持这些功能 - 所以最好在 R 中操作它并放回 BigQuery - 在数据库中执行这些操作有什么好处?
@SinhNguyen -- 据我所知,这本身并不是一个大查询问题。 Hadley 仍在努力改进 dbplyr 中对across
的支持,我认为这会更进一步。在数据库中工作的原因是文件太大而无法在本地处理(10 GB)。事实上,这就是dbplyr
的魅力!
顺便说一句,我只是使用本地 RSQLite 数据库而不是 BigQuery 运行相同的代码,以确保这绝对不是 BQ 问题。
如果你在 BQ 中编写查询 - 什么相当于 R 中的操作?
【参考方案1】:
我认为目前 dbplyr(2.1.0 版)不可能做到这一点。这是我的测试用例:
(1) 工作案例
# shared setup
library(dplyr)
library(dbplyr)
data(iris)
df = tbl_lazy(iris, con = simulate_mssql()) %>%
select(Sepal.Length)
df %>%
mutate(new = Sepal.Length + 1) %>%
show_query()
返回预期的 SQL:
<SQL>
SELECT `Sepal.Length`, `Sepal.Length` + 1.0 AS `new`
FROM `df`
(2) 介绍一个简单的一切
df %>%
mutate(new = if_any(everything(), TRUE)) %>%
show_query()
返回无效的 sql,因为不存在 if_any
或 everything
的翻译:
<SQL>
SELECT `Sepal.Length`, if_any(everything(), 1) AS `new`
FROM `df`
(3) 简单的 is.numeric
df %>%
mutate(new = if_any(where(is.numeric), TRUE)) %>%
show_query()
错误,因为is.numeric
作为函数传递
UseMethod("escape") 中的错误: 没有适用于“函数”类的对象的“转义”方法
(4) 将 TRUE 包装在一个返回 TRUE 的函数中
df %>%
mutate(new = if_any(everything(), ~TRUE)) %>%
show_query()
由于不存在隐式函数~...
的翻译而出错:
UseMethod("escape") 中的错误: 没有适用于“公式”类对象的“转义”方法
【讨论】:
dbplyr
v2.1 一直是 released(现在可以通过 CRAN 和 conda 获得)。这旨在解决issues with operations across columns。但是我仍然有问题。也许我超出了这些修复的范围。不确定,并且会喜欢解决方法。
重新运行 dbplyr 2.1 仅输出更改为 TRUE
在示例 (2) 中变为 1
。【参考方案2】:
这是一个基于this answer 的关于动态case_when
的潜在解决方法。
list_of_columns = colnames(df)
text_to_match = "ab"
cases = paste0("`", list_of_columns, "` %LIKE% '%", text_to_match, "%' ~ 1")
cases = c(cases, "1 == 1 ~ 0")
output = df %>%
mutate(new_col = case_when(
!!!rlang::parse_exprs(cases)
))
有关此技术的另一个示例,或者作为可重用函数的一部分,请查看我的 dbplyr_helpers repo 的 collapse_indicator_columns
函数。
【讨论】:
以上是关于在 R dbplyr 中跨列(按行)操作的主要内容,如果未能解决你的问题,请参考以下文章