R pipe, mget и среды

Я публикую это в надежде, что кто-нибудь сможет объяснить здесь поведение. И, возможно, это поможет другим сэкономить время, пытаясь выяснить, как исправить подобную ошибку.

Ответ, вероятно, находится где-то здесь, в этой виньетке Хэдли Уикхэм и Лайонел Генри. Тем не менее, кому-то вроде меня потребуются недели обучения, чтобы соединить точки.

Я выполняю несколько запросов из удаленной базы данных, а затем объединяю их в одну таблицу data.table. Я добавляю префикс "part_" к имени каждого отдельного результата запроса и использую ls() и mget() с data.table rbindlist() объединить их.

Это работает:

       results_all <- rbindlist(mget(ls(pattern = "part_", )))

Я изучил этот подход, вероятно, из списка data.tables в памяти и комбинирования по строкам (rbind), и это полезно знать, как это сделать наверняка.

Для удобства чтения я часто предпочитаю использовать канал magrittr (или связывание с data.table), особенно с такими проектами, потому что я использую dplyr для запроса базы данных. Но этот код приводит к ошибке:

       results_all <- ls(pattern = "part_", ) %>% 
 mget() %>%
 rbindlist()

Ошибка гласит Error: value for ‘part_a’ not found где part_a - это первое имя объекта в векторе символов, возвращаемое ls().

Выполняя поиск этого сообщения об ошибке, я наткнулся на обсуждение этой проблемы data.table на Github. Прочитав это, я попробовал установить "inherits = TRUE" в mget() следующим образом:

       results_all <- ls(pattern = "part_", ) %>% 
 mget(inherits = TRUE) %>%
 rbindlist()

И это работает. Таким образом, ошибка возникает при передаче результата ls() к mget(). И, учитывая, что вложение ls() в mget() работает, я предполагаю, что это как-то связано с конвейером и "охватывающими фреймами окружения".

При написании этого я столкнулся с неожиданным сообщением об ошибке при объединении data.table с помощью rbindlist() с помощью mget(). Из обсуждения я узнал, что это тоже работает.

       results_all <- ls(pattern = "part_", ) %>% 
 mget(envir = .GlobalEnv) %>%
 rbindlist()

Опять же, я надеюсь, что кто-нибудь сможет объяснить, что происходит, людям, которые хотят больше узнать о том, как работают среды в R.

Изменить: добавление воспроизводимого примера

В соответствии с запросом на воспроизводимый ответ, запуск приведенного выше кода с использованием этих трех таблиц data.tables (data.frames или tibbles будут вести себя одинаково) должен сделать это.

       part_a <- data.table(col1 = 1:10, col2 = sample(letters, 10))

part_b <- data.table(col1 = 11:20, col2 = sample(letters, 10))
  
part_c <- data.table(col1 = 21:30, col2 = sample(letters, 10)) 

1 ответ

Решение

В rhs аргумент оператора канала (в вашем примере выражение mget()) никогда не оценивается интерпретатором как вызов функции. Оператор канала - это инфиксная функция, которая выполняет нестандартное вычисление своего второго аргумента (rhs). Функция конвейера составляет и выполняет вызов новой функции, используя выражение RHS как своего рода "шаблон".

Вызывающая среда этого нового вызова функции - это функциональная среда %>%, а не вызывающая среда lhs функция или глобальная среда. .GlobalEnv и вызывающая среда lhs функция оказывается той же средой в вашем примере, и эта среда является родительской для функциональной среды %>%, вот почему inherits = TRUE или настройте среду на .GlobalEnv работает на вас.

Другие вопросы по тегам