Как я могу динамически построить строку и передать ее функции mutate () dplyr в R? - PullRequest
1 голос
/ 22 января 2020

Я задавал подобный вопрос раньше ( Ссылка ). Данный ответ работает отлично. Однако, оказывается, что это не полностью применимо к моему варианту использования.

Пожалуйста, рассмотрите следующий минимальный рабочий пример:

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 я не могу заставить ее работать так. Тем не менее, это именно то, что мне нужно.

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

Ответы [ 3 ]

2 голосов
/ 22 января 2020

Генерация и оценка строки

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 это может плохо воспроизводиться. Если вам удастся заставить что-то подобное работать, было бы неплохо увидеть его в виде ответа.

Использование al oop

Вместо того, чтобы делать это как единственная строка, альтернативой является использование al oop:

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()
1 голос
/ 17 февраля 2020

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

1 голос
/ 22 января 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

Создано в 2020-01-22 с помощью пакета Представитель (v0.3.0)

...