在 R dbplyr 中跨列按行操作

问题描述

我打开了一个 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,C = replicate(10,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),.))
  )
#>      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,overwrite=T
)

my_table_bq = tbl(conn,"mytable")


my_table_bq %>%
  mutate(
    has_ab = if_any(everything(),.))
  )
#> 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),.))
  )
#> Error in UseMethod("escape"): no applicable method for 'escape' applied to an object of class "function"

reprex package (v1.0.0) 于 2021 年 2 月 5 日创建

解决方法

我认为目前使用 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") 中的错误: 没有适用于“公式”类对象的“转义”方法

,

这里有一个基于 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 repocollapse_indicator_columns 函数。