Как я могу динамически построить строку и передать ее функции mutate () dplyr в 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 я не могу заставить его так работать. Однако это именно то, что мне нужно.

Кто-нибудь может помочь?


person MatSchu    schedule 22.01.2020    source источник
comment
Вы пробовали убрать кавычки из строки?   -  person Bruno    schedule 22.01.2020
comment
Я пробовал несколько функций цитирования и отмены цитирования, но не смог заставить их работать. Однако мои знания по этой теме ограничены.   -  person MatSchu    schedule 28.01.2020


Ответы (3)


Создание и оценка строки

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()
person Simon.S.A.    schedule 22.01.2020
comment
Петля сработала. Я мог бы даже сбросить в базу данных команды, не относящиеся к dplyr, которые являются собственными для этой базы данных. Это именно то, что мне нужно, спасибо. Я еще немного поиграю с eval() и parse() и связанными функциями и посмотрю, смогу ли я найти решение. - person MatSchu; 28.01.2020

Это работает в вашей базе данных?

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

Создано 22 января 2020 г. пакетом REPEX (v0.3.0)

person Bruno    schedule 22.01.2020
comment
Из stackoverflow.com/questions/59768872/, похоже, что отдельная команда не имеет dbplyr перевода. - person Simon.S.A.; 22.01.2020
comment
Предложенный код мне не подходит. Я получаю сообщение об ошибке: Error in separate(., new_col, into = string %>% str_c(1:n), sep = "_") : could not find function "separate" - person MatSchu; 28.01.2020

Недавно я прочитал больше по этой теме и обнаружил, что следующий код работает довольно хорошо, заставляя 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() с помощью !!!. Кроме того, я прочитал, что следует избегать синтаксического анализа и оценки строк, поэтому я переключился на списки.

person MatSchu    schedule 17.02.2020