如何动态构建字符串并将其传递给 R 中 dplyr 的 mutate() 函数?

How can I dynamically build a string and pass it to dplyr's mutate() function in R?

我之前问过类似的问题()。给定的答案工作正常。然而,事实证明,它并不完全适用于我的用例。

请考虑以下最小工作示例:

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

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(Q1=1, Q2=2, Q3=3, Q4=4) %>% 
    collect()

我对动态构建字符串 Q1=1, Q2=2, Q3=3, Q4=4 感兴趣,它可以是 Q1=1, Q2=2, ..., Qn = n.

我的一个想法是构建这样的字符串:

n_par <- 4
str_c('Q', 1:n_par, ' = ', 1:n_par, collapse = ', ')

这样 n_par 可以是任何正数。但是,由于 dplyr 的非标准评估,我不能让它那样工作。然而,这正是我需要的。

有人可以帮忙吗?

这在你的数据库中有效吗?

library(tidyverse)

q_n <- function(n) {
  str_c('Q', 1:n, ' = ', 1:n, collapse = ', ')
}

create_n_string <- function(data,n = 5,string = "Q"){
  data %>% 
    mutate(new_col = str_flatten(1:n,collapse = "_")) %>% 
    separate(new_col,into = string %>% str_c(1:n),sep = "_")
} 

mtcars %>% 
select(carb) %>%
  distinct(carb) %>%
  arrange(carb) %>%
  create_n_string()
#>   carb Q1 Q2 Q3 Q4 Q5
#> 1    1  1  2  3  4  5
#> 2    2  1  2  3  4  5
#> 3    3  1  2  3  4  5
#> 4    4  1  2  3  4  5
#> 5    6  1  2  3  4  5
#> 6    8  1  2  3  4  5

reprex package (v0.3.0)

于 2020 年 1 月 22 日创建

生成和评估字符串

Q1 = 1, Q2 = 2, Q3 = 3, Q4 = 4"Q1 = 1, Q2 = 2, Q3 = 3, Q4 = 4" 不是字符串的方式不同。有一些 R 函数会接受一个字符串对象并将其计算为代码。例如:

> eval(parse(text="print('hello world')"))

#> [1] "hello world"

不过,这在dbplyr翻译里面可能不太好玩。如果您设法使这种方法起作用,那么最好将其作为答案发布。

使用循环

与其将其作为单个字符串执行,还不如使用循环:

db <- tbl(con, "mtcars") %>%
    select(carb) %>%
    distinct(carb) %>%
    arrange(carb)

for(i in 1:n){
    var = paste0("Q",i)
    db <- db %>%
        mutate(!!sym(var) := i)
}

db <- collect(db)

需要 !!sym() 来告诉 dplyr 您希望将文本参数视为变量。没有它,惰性求值会给你带来奇怪的结果。 := 赋值是必需的,因为需要评估 LHS。

这种方法大致相当于每个变量的一个 mutate 语句(下面的示例),但 dbplyr 翻译可能看起来不如在单个 mutate 语句中完成所有操作那么优雅。

db <- tbl(con, "mtcars") %>%
    select(carb) %>%
    distinct(carb) %>%
    arrange(carb) %>%
    mutate(Q1 = 1) %>%
    mutate(Q2 = 2) %>%
    ...
    mutate(Qn = n) %>%
    collect()

我最近阅读了有关该主题的更多信息,我发现以下代码工作得很好,使 dbplyr 编写了更清晰的 SQL 代码。

# Libraries

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

# Example database

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

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

# Parameter for number of variables to be created

n <- 4 

# Variable list

var <- list() 
for(i in 1:n){
    j <- paste0("Q", i) 
    var[[j]] <- i
} 

# Query/computation

db <- tbl(con, "mtcars") %>% 
    select(carb) %>% 
    distinct(carb) %>% 
    arrange(carb) %>% 
    mutate(!!! var) %>% 
    show_query() %>% 
    collect()

诀窍是用专有名称构建一个列表,然后使用 !!! 将其放入 mutate() 函数中。此外,我读到应该避免解析和评估字符串,所以我切换到列表。