R tidyeval передает список, содержащий несколько векторов символов, в функции dplyr - PullRequest
1 голос
/ 26 мая 2020

В этой задаче я хочу сопоставить функцию с несколькими входами данных, чтобы создать выходные данные, показывающие частоту ответов элемента.

Сначала я создаю два набора данных, используя пакет psych:

suppressMessages(library(here))
suppressMessages(library(tidyverse))
suppressMessages(library(psych))

set.seed(123)

data_input_sim <-
  as_tibble(sim.poly.ideal(nvar = 50, n = 1000, cat = 4, )[["items"]]) %>%
  mutate_all(
    ~ case_when(
      .x == 0 ~ "never",
      .x == 1 ~ "occasionally",
      .x == 2 ~ "frequently",
      .x == 3 ~ "always"
    )
  ) %>%
  rename_all( ~ str_c("i", str_pad(
    as.character(1:50), 2, side = "left", pad = "0"
  ))) %>%
  mutate(
    ID = 100001:101000,
    age = sample(c(5:12), 1000, replace = TRUE),
    age_range = case_when(
      age <=8 ~ "5 to 8 yo",
      T ~ "9 to 12 yo"
    ),
    gender = sample(
      c("female", "male"),
      1000,
      replace = TRUE,
      prob = c(0.53, 0.47)
    ),
    educ = sample(
      c("no_HS", "HS_grad", "some_college", "BA_plus"),
      1000,
      replace = TRUE,
      prob = c(0.119, 0.263, 0.306, 0.311)
    ),
    ethnic = sample(
      c("hispanic", "asian", "black", "white", "other"),
      1000,
      replace = TRUE,
      prob = c(0.239, 0.048, 0.136, 0.521, .056)
    ),
    region = sample(
      c("northeast", "south", "midwest", "west"),
      1000,
      replace = TRUE,
      prob = c(0.166, 0.383, 0.212, 0.238)
    ),
    clin_status = sample(
      c("typ", "clin"),
      1000,
      replace = TRUE,
      prob = c(0.8, 0.2)
    )
  ) %>%
  select(ID:clin_status, i01:i50)

data_input_bfi <- bfi %>%
  drop_na() %>%
  sample_n(1000) %>%
  mutate(
    ID = 200001:201000,
    age_range = case_when(
      age <= 18 ~ "18 yo or younger",
      between(age, 19, 24) ~ "19 to 24 yo",
      between(age, 25, 39) ~ "25 to 39 yo",
      T ~ "40 yo or older"
    ),
    gender = case_when(gender == 1 ~ "male",
                       gender == 2 ~ "female"),
    educ = case_when(
      education == 1 ~ "no_HS",
      education == 2 ~ "HS_grad",
      education == 3 ~ "some_college",
      T ~ "BA_plus"
    ),
    ethnic = sample(
      c("hispanic", "asian", "black", "white", "other"),
      1000,
      replace = TRUE,
      prob = c(0.239, 0.048, 0.136, 0.521, .056)
    ),
    region = sample(
      c("northeast", "south", "midwest", "west"),
      1000,
      replace = TRUE,
      prob = c(0.166, 0.383, 0.212, 0.238)
    ),
    clin_status = sample(
      c("typ", "clin"),
      1000,
      replace = TRUE,
      prob = c(0.8, 0.2)
    )
  ) %>%
  mutate_at(
    vars(A1:O5),
    ~
      case_when(
        .x == 1 ~ "very_inaccurate",
        .x == 2 ~ "moderately_inaccurate",
        .x == 3 ~ "slightly_inaccurate",
        .x == 4 ~ "slightly_accurate",
        .x == 5 ~ "moderately_accurate",
        .x == 6 ~ "very_accurate",
      )
  ) %>% 
  select(ID, age:clin_status, A1:O5)

Затем я извлекаю и упорядочиваю элементы, уникальные для каждого набора данных: суффикс его имени, имена его столбцов элементов и имена его категорий элементов:

data_name_suffix <- c("sim", "bfi")

sim_item_cols <- str_c("i", str_pad(as.character(1:50), 2, side = "left", pad = "0"))
bfi_item_cols <- cross(list(c("A", "C", "E", "N", "O"), seq(1:5))) %>%
  map_chr(str_c, collapse = "") %>% 
  sort()

sim_item_cats <- c("never", "occasionally","frequently", "always")
bfi_item_cats <- c("very_inaccurate", "moderately_inaccurate", "slightly_inaccurate",
                  "slightly_accurate", "moderately_accurate", "very_accurate")

data_name_suffix - это двухэлементный вектор символов; Затем я создаю двухэлементные списки (используя quos()) для хранения столбца элемента и имен категорий:

item_cols <- quos(sim_item_cols, bfi_item_cols)
item_cats <- quos(sim_item_cats, bfi_item_cats)

Теперь я пытаюсь сопоставить функцию создания вывода по трем входам, используя purrr::pmap() :

pmap_df(
  list(data_name_suffix,
       item_cols,
       item_cats),
  ~
    eval(as.name(str_c("data_input_", data_name_suffix))) %>%
    select(!!!item_cols) %>%
    gather(var, value) %>%
    group_by(var, value) %>%
    count(var, value) %>%
    ungroup() %>%
    spread(value, n) %>%
    arrange(match(var, !!!item_cols)) %>%
    select(var, !!!item_cats) %>% 
    assign(str_c("freq_item_val_", data_name_suffix), ., envir = .GlobalEnv)
)

И он возвращает эту ошибку:

Error: Unknown columns `A1`, `A2`, `A3`, `A4`, `A5` and ... 

Это наводит на мысль, что R видит список item_cols как один длинный вектор символов, а не два отдельных символа векторы для перебора.

И здесь мы достигли предела моего понимания и опыта работы с tidyeval методами. Я подозреваю, что я делаю что-то не так с quos() и !!!.

Заранее благодарю за любую помощь, и я надеюсь, что тот, кто это читает, будет в безопасности и здоров в это сюрреалистическое время.

1 Ответ

0 голосов
/ 26 мая 2020

Здесь мы могли бы использовать mget для получения значений объектов

library(stringr)
library(purrr)
library(dplyr)
library(tidyr)
list(mget(str_c('data_input_', data_name_suffix)),
     item_cols,
   item_cats) %>%
     pmap(~ ..1 %>% 
            select(!!! ..2) %>% 
            pivot_longer(everything(), names_to = 'var', values_to = 'value') %>% 
            count(var, value) %>% 
            pivot_wider(names_from = value, values_from = n) %>% 
            arrange(match(var, !!!..2)) %>%
            select(var, !!! ..3) )
#$data_input_sim
# A tibble: 50 x 5
#   var   never occasionally frequently always
#   <chr> <int>        <int>      <int>  <int>
# 1 i01     465          366        141     28
# 2 i02     489          336        147     28
# 3 i03     457          367        146     30
# 4 i04     433          385        162     20
# 5 i05     418          362        171     49
# 6 i06     420          369        169     42
# 7 i07     405          367        182     46
# 8 i08     361          401        194     44
# 9 i09     346          391        211     52
#10 i10     334          425        203     38
# … with 40 more rows

#$data_input_bfi
# A tibble: 25 x 7
#   var   very_inaccurate moderately_inaccurate slightly_inaccurate slightly_accurate moderately_accurate very_accurate
#   <chr>           <int>                 <int>               <int>             <int>               #<int>         <int>
# 1 A1                334                   278                 151               130                  75            32
# 2 A2                 18                    49                  48               197                 365           323
# 3 A3                 32                    51                  72               210                 353           282
# 4 A4                 48                    69                  60               159                 243           421
# 5 A5                 26                    66                  89               207                 340           272
# 6 C1                 17                    48                  82               213                 383           257
# 7 C2                 26                    85                  98               212                 361           218
# 8 C3                 35                    80                 102               272                 322           189
# 9 C4                296                   270                 166               163                  83            22
#10 C5                197                   212                 118               207                 167            99
# … with 15 more rows

ПРИМЕЧАНИЕ. assign Создание нескольких объектов не рекомендуется. Вместо этого сохраните вывод в list и внесите изменения в каждый из элементов list (при необходимости), перебирая его с помощью map

...