【问题标题】:How can I dynamically create new variables/columns on databases in R using dplyr?如何使用 dplyr 在 R 中的数据库上动态创建新变量/列?
【发布时间】:2019-12-23 04:39:43
【问题描述】:

我是 Stackoverflow 的新手,对 R 也很陌生。非常感谢您的帮助。

我正在使用dplyrmutate() 函数根据一个初始列创建一组新列。对于要创建的先验已知数量的列,一切正常。

但是,在我的应用程序中,要创建的新列的数量是未知的(或者在运行代码之前确定为输入参数)。

为了说明,请考虑以下最小的工作示例:

library(RSQLite)
library(dplyr)
library(dbplyr)
library(DBI)

con <- DBI::dbConnect(RSQLite::SQLite(), path = ":memory:")

copy_to(con, mtcars, "mtcars", temporary = FALSE)

db <- tbl(con, "mtcars") %>%
    select(carb) %>%
    distinct(carb) %>%
    arrange(carb) %>%
    mutate(carb1 = carb + 1) %>%
    mutate(carb2 = carb + 2) %>%
    mutate(carb3 = carb + 3) %>%
    show_query() %>%
    collect()

在本例中,我创建了三个新变量。但是,我希望程序能够处理动态数量的变量(例如,五个或十个新变量)。我也想做collect()之前的所有计算,因为我想尽可能晚地将数据复制到内存中。

我的实际应用程序的一些背景:我想使用DB2's function ADD_MONTHS()。所以我需要dplyr/dbplyr 将该函数直接刷新到 SQL 命令中。因此,我需要一个实际上不使用数据帧逻辑的解决方案 - 我需要该解决方案位于 dplyr

从不同的角度来看:在 SAS 中,我会使用宏处理器来动态构建 proc sql 语句。 R中是否有等价物?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    我们可以使用map

    library(dplyr)
    library(purrr)
    library(stringr)
    map_dfc(1:3, ~ df %>%
                      transmute(!! str_c('x', .x) := x + .x)) %>%
        bind_cols(df, .)
    #  x x1 x2 x3
    #1 1  2  3  4
    #2 2  3  4  5
    #3 3  4  5  6
    

    对于数据库,在添加列之前执行collect

    dat <- tbl(con, "mtcars") %>%
            select(carb) %>%
            distinct(carb) %>%
            arrange(carb) %>%
            collect()
    map_dfc(dat$carb, ~ dat %>%
                          transmute(!! str_c('carb', .x) := carb + .x)) %>%
        bind_cols(dat, .)
    # A tibble: 6 x 7
    #   carb carb1 carb2 carb3 carb4 carb6 carb8
    #  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #1     1     2     3     4     5     7     9
    #2     2     3     4     5     6     8    10
    #3     3     4     5     6     7     9    11
    #4     4     5     6     7     8    10    12
    #5     6     7     8     9    10    12    14
    #6     8     9    10    11    12    14    16
    

    如果我们想在collecting 之前这样做,另一种选择是在mutate 中传递一个表达式

    tbl(con, "mtcars") %>%
       select(carb) %>%
       distinct(carb) %>%
       arrange(carb) %>%
       mutate(!!! rlang::parse_exprs(str_c('carb', 1:3, sep="+", collapse=";"))) %>%
       rename_at(-1, ~ str_c('carb', 1:3)) %>%
       show_query() %>%
       collect()
    #<SQL>
    #SELECT `carb`, `carb` + 1.0 AS `carb1`, `carb` + 2.0 AS `carb2`, `carb` + 3.0 AS #`carb3`
    #FROM (SELECT *
    #FROM (SELECT DISTINCT *
    #FROM (SELECT `carb`
    #FROM `mtcars`))
    #ORDER BY `carb`)
    # A tibble: 6 x 4
    #   carb carb1 carb2 carb3
    #  <dbl> <dbl> <dbl> <dbl>
    #1     1     2     3     4
    #2     2     3     4     5
    #3     3     4     5     6
    #4     4     5     6     7
    #5     6     7     8     9
    #6     8     9    10    11
    

    【讨论】:

    • 您好 akrun,感谢您的回答。如果添加了 library(stringr),则您的示例有效。不幸的是,我的例子过于简单或选择不当。我没有使用数据框,而是使用数据库并且无法构造数据框,因为我想对数据库进行计算。根据我对purrrmap_dfc 的有限理解,它只适用于数据帧。我构建了一个新的最小工作示例,可以更好地捕捉我的问题,并相应地编辑了我最初提交的问题。
    • 有没有办法在collect()之前做到这一点?在我的现实生活中,我无法将数据复制到内存中,因为它非常大,我希望将其尽可能长时间地保存在数据库中。
    • 这是我一直在寻找的解决方案,非常感谢。
    【解决方案2】:

    我们可以使用来自purrrmap2_dfc 传递值来添加数据并将数据添加到原始df

    library(dplyr)
    library(purrr)
    
    bind_cols(df, map2_dfc(1:3, df ,`+`))
    
    #  x V1 V2 V3
    #1 1  2  3  4
    #2 2  3  4  5
    #3 3  4  5  6
    

    【讨论】:

    • 您好 Ronak,谢谢您的回答。代码工作正常。不幸的是,我的例子过于简单或选择不当。我没有使用数据框,而是使用数据库并且无法构造数据框,因为我想对数据库进行计算。根据我对purrrmap2_dfc 的有限理解,它仅适用于数据帧。我构建了一个新的最小工作示例,可以更好地捕捉我的问题,并相应地编辑了我最初提交的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-11-18
    相关资源
    最近更新 更多