Квазиквотирование в мурлыканье

В рамках более крупной функции для сохранения значений только во временном ряду роста растений, которые происходят до травмы для каждого человека (plantid), я пишу 2 фрагмента, которые по порядку будут содержать функцию

  1. Управляйте тем, что все переменные, указанные в аргументе, являются векторами символов (как и во второй функции, %in% не распознает названные факторы), и если нет, преобразовать в символ, выдавая предупреждение.

  2. Определите и отметьте строки из указанных выше переменных, которые включают одну из строк из аргумента b.

Я совершенно уверен, что что-то не так с операторами цитаты / квазиквотации или bang-bang (_4 _) / big-bang (!!!) (я впервые пишу функцию с цитатой). Мне постоянно выдаются предупреждения «!!! нельзя использовать на верхнем уровне» или тому подобное, и я не знаю, как решить эту проблему. Мне также нужна помощь в поиске хорошего способа преобразовать переменные, не являющиеся символами.

Это то, что у меня есть

Описание аргумента

  • df: data.frame

  • plantid: уникальный идентификатор для каждого отдельного растения

  • year: год наблюдения

  • injuries: список из (в моем случае) 3 столбцов, которые могут содержать код травмы, например c("PrimaryInjury", "SecondaryInjury", "OtherInjury")

  • forbidden_values: интересующие коды травм, например c("Rust", "Insect", "Snow break")

Функция

id_injured <- function(df, plantid, year, injuries, forbidden_values){
    #parsing unquoted strings.
    plantid <- enquo(plantid)
    year <- enquo(year)
    forbidden_values <- enquos(forbidden_values)
    injuries <- syms(injuries)

    #if all variables in injuries are not characters, stop and warn (attempt to convert to character those variables which are not character)

    if(!all(purrr::pmap_int(select(df, !!!injuries), ~is.character(...))))){
       stop("All injury variables are not characters. Convert factors in injuries to character variables")} else {
          (1) #Control to give output while testing function, replace with conversion and warning?
    }

    #Identify rows with matching injury codes with 1, else 0.
    Dataplantid <- df %>% mutate(is_injured = purrr::pmap_int(select(df, !!!injuries), any(c(...) %in% !!!forbidden values)))

    #End of function
}


Использование по назначению

Я удалил часть (1) функции, поэтому она будет пытаться пометить только 1 или 0.

Dataplantid <- id_injured(df=df, plantid=plantid, year=year, injuries=c("PrimaryInjury","SecondaryInjury","OtherInjury"),forbidden_values=c("Rust","Insect","Snow break")

Результат

Ошибка: невозможно использовать !!! на верхнем уровне.

> last_trace()
<error/rlang_error>
Can't use `!!!` at top level.
Backtrace:
     █
  1. └─global::so_injured(...)
  2.   └─`%>%`(...)
  3.     ├─base::withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
  4.     └─base::eval(quote(`_fseq`(`_lhs`)), env, env)
  5.       └─base::eval(quote(`_fseq`(`_lhs`)), env, env)
  6.         └─`_fseq`(`_lhs`)
  7.           └─magrittr::freduce(value, `_function_list`)
  8.             ├─base::withVisible(function_list[[k]](value))
  9.             └─function_list[[k]](value)
 10.               ├─dplyr::mutate(...)
 11.               └─dplyr:::mutate.data.frame(...)
 12.                 ├─base::as.data.frame(mutate(tbl_df(.data), ...))
 13.                 ├─dplyr::mutate(tbl_df(.data), ...)
 14.                 └─dplyr:::mutate.tbl_df(tbl_df(.data), ...)
 15.                   └─rlang::enquos(..., .named = TRUE)
 16.                     └─rlang:::endots(...)
 17.                       └─rlang:::map(...)
 18.                         └─base::lapply(.x, .f, ...)
 19.                           └─rlang:::FUN(X[[i]], ...)
 20.                             └─rlang::splice(...)

Связанные данные

plantid <- rep(c(1,2,3,4,5), times=c(3,3,3,3,3))
year <- rep(1:3, length.out=length(plantid))
set.seed(42)
PrimaryInjury <- sample(c(NA,NA,NA,"Rust","Insect", "Snow break"), 15, replace=TRUE)
SecondaryInjury <- rep(NA, length.out=length(plantid)) #Filled with NA for example
OtherInjury <- rep(NA, length.out=length(plantid)) #Filled NA for example
df <- data.frame(plantid,year,PrimaryInjury,SecondaryInjury,OtherInjury)
#Right now, PrimaryInjury is a factor, SecondaryInjury and OtherInjury are logical.

Ожидаемый результат

Dataplantid <- df
Dataplantid$is_injured <- c(0,1,0,0,0,1,0,0,0,1,0,1,1,1,0)

person Silviculturalist    schedule 24.03.2020    source источник
comment
Прежде всего, ваша функция пропускает возвращаемый аргумент. Новый фрейм данных Dataplantid не будет сохранен в глобальной среде. Ваша цель состоит в том, чтобы функция возвращала фрейм данных, который отмечает, было ли определенное растение повреждено? Тогда вам нужен только оператор mutate() с ifelse(). Кроме того, проблема не воспроизводится, поскольку вы не показали, как вы использовали эту функцию.   -  person MKR    schedule 24.03.2020
comment
@MKR, спасибо за ответ! Я обновлю вопрос, чтобы отразить, как используется эта функция. Возвращенный Dataplantid будет возвращен, но не сохранен, поскольку это последний объект в функциональной среде, так что я могу назначить вывод имени по своему выбору, да? В более крупной функции Dataplantid будет обрабатываться для группировки по плантидам и упорядочения по годам, а затем отмечены все наблюдения, предшествующие первой травме, с помощью 0, иначе 1, после чего он будет присоединен к исходному фрейму данных, чтобы я мог присоединиться исходный фрейм данных с новой переменной до первой травмы.   -  person Silviculturalist    schedule 24.03.2020
comment
@MKR Каждая функция в R возвращает значение (не «аргумент»!), Включая это, хотя ненужное присвоение внутри функции, безусловно, вводит в заблуждение. В любом случае эта часть кода работает.   -  person Konrad Rudolph    schedule 24.03.2020
comment
@KonradRudolph не могли бы вы поделиться тем, что у вас есть? Я не могу заставить его работать.   -  person Silviculturalist    schedule 24.03.2020
comment
@KonradRudolph мой результат Error in c(...) %in% list(~c("Rust", "Insect", "Snow break")) : '...' used in an incorrect context   -  person Silviculturalist    schedule 24.03.2020
comment
@Silviculturalist Мне эта ошибка кажется разумной. Каковы ваши намерения с этим кодом? Как вы думаете, что означает c(...)? Тем не менее, это не то сообщение об ошибке, которое я получаю после исправления очевидных синтаксических ошибок в опубликованном вами коде.   -  person Konrad Rudolph    schedule 24.03.2020
comment
@KonradRudolph, насколько я понимаю, многоточие ... принимает произвольное количество переменных, а c () предоставляет векторную оболочку (?). Мне помог этот код, и я не понимаю, как ... используется для замены различных переменных, выбранных в pmap_int (выберите (., !!! травмы). Для меня purrr::pmap_int(select(df, !!!injuries), any(c(...) %in% !!!forbidden values)) читается как с каждой из переменных в травмах Список векторов, совпадает ли какое-либо из них в списке запрещенных_значений? Мое намерение с кодом состоит в том, чтобы он отмечал любые положительные совпадения в строке, установленной как 1, иначе 0.   -  person Silviculturalist    schedule 24.03.2020


Ответы (1)


Вот несколько проблем в порядке от наименее к наиболее проблемным:

  1. Для логических результатов используйте map_lgl вместо map_int.
  2. В частности, используйте map_lgl вместо pmap_int, если вы на самом деле не собираетесь отображать несколько аргументов параллельно, что здесь не так.
  3. Не назначайте результат функции переменной внутри функции. На самом деле это не вредит, но в этом нет необходимости и вводит в заблуждение.
  4. Не заключайте в кавычки, а затем интерполируйте значения forbidden_values. Вы хотите использовать здесь вектор символов, а не R-имена.
  5. Вам не хватало ~ в вызове purrr для вычисления is_injured.
  6. Логика определения поврежденных ценностей работает не совсем так; может быть способ использования pmap_lgl здесь, но я думаю, что проще - хотя, возможно, и более многословно - преобразовать ваши данные в длинный формат и работать с этим.

В совокупности получаем:

id_injured <- function(df, plantid, year, injuries, forbidden_values) {
    plantid <- enquo(plantid)
    year <- enquo(year)
    injuries <- syms(injuries)

    df_injuries <- select(df, !!! injuries)

    if (! all(purrr::map_lgl(df_injuries, is.character))) {
        stop("All injury variables are not characters. Convert factors in injuries to character variables")
    }

    is_injured <- df_injuries %>%
        mutate(.RowID = row_number()) %>%
        tidyr::gather(Key, Value, -.RowID) %>%
        group_by(.RowID) %>%
        summarize(is_injured = any(Value %in% forbidden_values)) %>%
        pull(is_injured)

    df %>% mutate(is_injured = is_injured)
}
person Konrad Rudolph    schedule 24.03.2020
comment
Спасибо @Konrad, это работает. Мне также удалось решить это как: ´selectlist ‹- function (df, selectlist, запрещено_values) {enquos (selectlist) df%›% mutate (is_injured = purrr :: pmap_int (select (., !! selectlist), ~ any (c (...)% в% !! Запрещенные_значения)))} список выбора (ожидаемый, список выбора = c (Первичная травма, Вторичная травма), disabled_values ​​= c (Ржавчина, Насекомое, Снежный перерыв)) ´ - person Silviculturalist; 24.03.2020