代码之家  ›  专栏  ›  技术社区  ›  bheavner

将purrr::map2()与dbplyr一起使用

  •  2
  • bheavner  · 技术社区  · 7 年前

    我试图从一个表(“Positions”)中选择行,其中特定列(“position”)的值在另一个(“my\u ranges”)表中定义的范围内,然后从“my\u ranges”表中添加分组标记。

    我可以用tibbles和一些 purrr::map2 调用,但同样的方法不适用于dbplyr数据库TIBLES。这是预期的行为吗?如果是,我是否应该采取不同的方法来使用dbplyr完成此类任务?

    以下是我的示例:

    library("tidyverse")
    set.seed(42)
    
    my_ranges <-
      tibble(
        group_id = c("a", "b", "c", "d"),
        start = c(1, 7, 2, 25),
        end = c(5, 23, 7, 29)
        )
    
    positions <-
      tibble(
        position = as.integer(runif(n = 100, min = 0, max = 30)),
        annotation = stringi::stri_rand_strings(n = 100, length = 10)
      )
    
    # note: this works as I expect and returns a tibble with 106 obs of 3 variables:
    result <- map2(.x = my_ranges$start, .y = my_ranges$end,
                 .f = function(x, y) {between(positions$position, x, y)}) %>%
      map2(.y = my_ranges$group_id,
                  .f = function(x, y){
                    positions %>%
                      filter(x) %>%
                      mutate(group_id = y)}
    ) %>% bind_rows()
    
    # next, make an in-memory db for testing:
    con <- DBI::dbConnect(RSQLite::SQLite(), path = ":memory:")
    
    # copy data to db
    copy_to(con, my_ranges, "my_ranges", temporary = FALSE)
    copy_to(con, positions, "positions", temporary = FALSE)
    
    # get db-backed tibbles:
    my_ranges_db <- tbl(con, "my_ranges")
    positions_db <- tbl(con, "positions")
    
    # note: this does not work as I expect, and instead returns a tibble with 0 obsevations of 0 variables:
    # database range-based query:
    db_result <- map2(.x = my_ranges_db$start, .y = my_ranges_db$end,
                      .f = function(x, y) {
                        between(positions_db$position, x, y)
                        }) %>%
      map2(.y = my_ranges_db$group_id,
           .f = function(x, y){
             positions_db %>%
               filter(x) %>%
               mutate(group_id = y)}
      ) %>% bind_rows()
    
    2 回复  |  直到 7 年前
        1
  •  5
  •   edgararuiz    6 年前

    只要每次迭代都创建一个维度相同的表,那么就可能有一种巧妙的方法将整个操作推送到数据库中。我们的想法是两者兼而有之 map() reduce() 从…起 purrr 。每个 tbl_sql() 操作是懒惰的,所以我们可以迭代它们而不用担心发送一堆查询,然后我们可以使用 union() 这将基本上使用 UNION 子句。下面是一个示例:

    library(dbplyr, warn.conflicts = FALSE)
    library(dplyr, warn.conflicts = FALSE)
    library(purrr, warn.conflicts = FALSE)
    library(DBI, warn.conflicts = FALSE)
    library(rlang, warn.conflicts = FALSE)
    
    con <- DBI::dbConnect(RSQLite::SQLite(), path = ":dbname:")
    
    db_mtcars <- copy_to(con, mtcars)
    
    cyls <- c(4, 6, 8)
    
    all <- cyls %>%
      map(~{
        db_mtcars %>%
          filter(cyl == .x) %>%
          summarise(mpg = mean(mpg, na.rm = TRUE)
          )
      }) %>%
      reduce(function(x, y) union(x, y)) 
    
    all
    #> # Source:   lazy query [?? x 1]
    #> # Database: sqlite 3.22.0 []
    #>     mpg
    #>   <dbl>
    #> 1  15.1
    #> 2  19.7
    #> 3  26.7
    
    show_query(all)
    #> <SQL>
    #> SELECT AVG(`mpg`) AS `mpg`
    #> FROM (SELECT *
    #> FROM (SELECT *
    #> FROM `mtcars`)
    #> WHERE (`cyl` = 4.0))
    #> UNION
    #> SELECT AVG(`mpg`) AS `mpg`
    #> FROM (SELECT *
    #> FROM (SELECT *
    #> FROM `mtcars`)
    #> WHERE (`cyl` = 6.0))
    #> UNION
    #> SELECT AVG(`mpg`) AS `mpg`
    #> FROM (SELECT *
    #> FROM (SELECT *
    #> FROM `mtcars`)
    #> WHERE (`cyl` = 8.0))
    
    dbDisconnect(con)
    
        2
  •  3
  •   moodymudskipper    7 年前

    dbplyr 翻译 R 进入 SQL 。中不存在列表 SQL map 创建列表。因此不可能翻译 地图 进入 SQL

    主要地 dplyr 功能和一些 base 功能已翻译,他们正在处理 tidyr 正如我所理解的那样。使用时 dbplyr公司 试着有一个 SQL 在你的方法中加入逻辑,否则它将很容易被打破。