使用 R 中的 `arrow` 包动态构造查询

问题描述 投票:0回答:1

我正在使用

arrow
包与 R 中的大型 (30GB) parquet 文件数据集进行交互。当我有一个明确的查询时,这非常有用,但当我尝试动态构建查询时,我不确定最佳实践因为我拥有的最佳解决方案需要使用
eval(parse(text=))
,所以我正在寻找更好的解决方案。

作为示例,我有下面的简单数据集:

library(arrow)
artab <- arrow_table(int=1:1000, value=runif(1000))

我想检索值在 0.4 到 0.6 之间的所有行,这使用

dplyr
语法实现起来很简单:

library(dplyr)
library(data.table) # for %between% operator syntax
artab %>%
  filter(value%between%c(0.4, 0.6)) %>%
  collect()

当我有多个“窗口”时,我想使用

|
OR 运算符提取数据:

artab %>%
  filter(value%between%c(0.4, 0.6) | value%between%c(0.8, 1.0)) %>%
  collect()

但是,如果我有一长串窗口并且想要以编程方式生成此查询,我能想到的唯一选择是将整个管道链构建为字符向量,然后将其传递给

eval(parse(text=))
:

window_req_string <- data.frame(
  lower_bound=c(0.1, 0.4, 0.8), 
  upper_bound=c(0.2, 0.6, 1.0)
  ) %>%
  summarise(req_cmd=paste0("value%between%c(", lower_bound, ", ", upper_bound, 
                           ")", collapse="|")) %>%
  pull(req_cmd)
full_arrow_req <- paste0(
  'artab %>% filter(', window_req_string, ') %>% dplyr::collect()'
)
arrow_output <- eval(parse(text=full_arrow_req))

有更好的方法吗? 用户定义函数在这里似乎不是一个好的选择,因为该函数已经存在(

filter
),我只需要一个矢量化版本。我特别想要一种更好的方法来做到这一点,因为我传递的管道链也可能会变得很长(过滤附加列并运行计算)。

我想知道摆弄

R6
对象本身来强制插入查询(例如,编写减去此特定过滤步骤的查询,然后编辑 R6 对象以将其“添加”到步骤列表中)是否是一个好主意,但我我对 R6 不太熟悉,这看起来很老套。

除了将其写为字符串并使用

arrow
之外,有没有办法在 R 中动态构造
eval(paste())
查询?

r dplyr parquet apache-arrow
1个回答
0
投票

摆弄 R6 对象感觉不是正确的路径,因为大多数这些查询内部都是基于通过 tidy eval 获取和评估表达式。我可能会做这样的事情:

library(arrow)
library(dplyr)
library(purrr)
library(rlang)

tbl <- tibble::tibble(x = 1:10)
ranges <- list(c(1, 3), c(5,6), c(9, 10))

calls <- map(ranges, ~call2("between", as.name("x"), .x[[1]], .x[[2]]) )
filter_string <- paste(calls, collapse = "|")

# works in dplyr
tbl |>
  filter(!! rlang::parse_expr(filter_string))
#> # A tibble: 7 × 1
#>       x
#>   <int>
#> 1     1
#> 2     2
#> 3     3
#> 4     5
#> 5     6
#> 6     9
#> 7    10
  
# works in arrow too!
output <- arrow_table(tbl) |>
  filter(!! rlang::parse_expr(filter_string))

output
#> Table (query)
#> x: int32
#> 
#> * Filter: ((((x >= 1) and (x <= 3)) or ((x >= 5) and (x <= 6))) or ((x >= 9) and (x <= 10)))
#> See $.data for the source Arrow object

collect(output)
#> # A tibble: 7 × 1
#>       x
#>   <int>
#> 1     1
#> 2     2
#> 3     3
#> 4     5
#> 5     6
#> 6     9
#> 7    10

诚然,我们仍然处于构建和评估字符串的领域,但它可能更干净一点?

© www.soinside.com 2019 - 2024. All rights reserved.