在 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_anyeverything 的翻译:

<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 中跨列(按行)操作的主要内容,如果未能解决你的问题,请参考以下文章

R中跨列的条件均值

在 Access CrossTab 查询报告中跨列求和行

如何在 Apache Spark 中跨列创建 RDD 分区?

在 SQL Server 2008 视图中跨列计算特定值

SQL查询单表中跨列唯一值

如何在 PySpark 中跨多个时间间隔使用 .filter() 操作?