Исправление неожиданной замены аргумента в функции, созданной в коде

#r #function

#r #функция

Вопрос:

Прежде всего, это мой первый вопрос по stackoverflow. Я попытался написать свой вопрос (ниже) с некоторым здравым смыслом (описательное название для других, чтобы другие могли легко искать и находить его, воспроизводимый код, …), но если есть какие-либо рекомендации или исправления для будущих вопросов, которые я бы попросил, чтобы всем вам было удобнее отвечать, пожалуйста, укажитеэто мне в ответах.

Вопрос :

Мне удалось написать некоторый код, дающий мне именно то, что я хочу. Это не должно быть причиной для того, чтобы задавать вопрос на этой доске, однако мне приходится копировать и вставлять один и тот же фрагмент много десятков раз с небольшими и очень предсказуемыми изменениями.

Поэтому я решил написать свою собственную функцию, чтобы сэкономить время. Эта функция не удалась, и мне кажется, что это просто расширение предыдущих методов построения собственной функции в R-скрипте.

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

По сути, мои вопросы сводятся к :

  1. Почему аргумент «Переменная» не изменяется аргументом, который я указал, когда я запускаю функцию?
  2. Как я должен изменить свой скрипт, чтобы исправить это?

Код :

(Мне не разрешено передавать какую-либо часть моей базы данных, поэтому я использую набор данных gapminder, который содержит переменные с аналогичными характеристиками).

 #Package

library(tidyverse)
library(questionr)
library(haven)
library(knitr)
library(tidyr)
library(qwraps2)
library(matrixStats)
library(reldist)
library(gapminder)

#Reproductible data

db <- gapminder

#Function

Liste <- function(Variable)
{
  Liste <-
  list(list("Minimum" = ~eval(parse(text = paste("min(",Variable," na.rm = TRUE"))),
            "Moyenne" = ~mean(Variable, na.rm = TRUE),
            "Mediane" = ~median(Variable, na.rm = TRUE),
            "Maximum" = ~max(Variable, na.rm = TRUE),
            "Caracteristiques position" = ~ifelse(mean(Variable, na.rm = TRUE)>median(Variable, na.rm = TRUE),"Moyenne > Mediane",ifelse(mean(Variable, na.rm = TRUE)<median(Variable, na.rm = TRUE),"Mediane > Moyenne","Moyenne = Mediane")),
            "Valeur manquante" = ~sum(is.na(Variable)),
            "D1" = ~quantile(Variable, 0.1, na.rm = TRUE),
            "Q25" = ~quantile(Variable, 0.25, na.rm = TRUE),
            "Q75" = ~quantile(Variable, 0.75, na.rm = TRUE),
            "D9" = ~quantile(Variable, 0.9, na.rm = TRUE)
  ))
}


Liste_Test <- Liste(Variable = lifeExp)
 

Подведение итогов проблемы

Если вы откроете объект «Liste_Test» вместо замены «Переменной» на «lifeExp», ничего не изменится. Это мешает правильной работе остальной части функции.

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

 "Minimum" = ~eval(parse(text = paste("min(",Variable," na.rm = TRUE")))
 

Это первая попытка использовать so close questions на плате StackOverflow. Результат точно такой же.

Я также нашел некоторый ответ, рекомендующий использовать функцию «do.call». Однако я не могу понять, как это должно работать и, следовательно, какую модификацию я должен сделать. Чтобы дать вам представление об ожидаемом результате, вы можете добавить этот фрагмент (не в виде функции) в предыдущий сценарий.

Я надеюсь, что я предоставил полное, хотя и не слишком длинное объяснение моей проблемы и вопросов.

Комментарии:

1. Это сильный первый вопрос, но я бы хотел, чтобы ваш пример был более минимальным — я не думаю, что вы используете почти любой пакет, для которого у вас есть library() вызовы. У меня нет questionr , reldist , или qwraps2 установлен. Но я не думаю, что вы используете какие-либо пакеты, кроме gapminder как для выборки данных и purrr (загруженных tidyverse ) для синтаксиса ~ функции. Может быть, вы могли бы удалить остальные library вызовы?

2. Я бы также посоветовал вам свести проблему к минимуму — list("Minimum" = ~eval(parse(text = paste("min(",Variable," na.rm = TRUE"))),"Moyenne" = ~mean(Variable, na.rm = TRUE) достаточно? Или вам действительно нужно еще 9 статистических данных? Но это всего лишь отзыв о том, как вы задали вопрос, что в целом неплохо для первого вопроса.

3. Спасибо за отзыв о том, как я написал вопрос. Я подумал о количестве пакетов, которые я вставил в код, но именно в первом проекте большинство (возможно, не все) имело смысл. Я забыл обновить список, когда удалил отклонение этого вопроса с помощью взвешенных функций. Что касается длины кода, я уменьшу его в следующий раз, поскольку переход от списка из двух статистических данных к большему списку — это просто расширение того, что можно сделать, если найдено решение.

Ответ №1:

Когда вы передаете имена столбцов в качестве аргумента функции, вам нужно кодировать вещи иначе, чем вы обычно делаете. (Читайте о нестандартной оценке)

Вы передаете Liste(Variable = lifeExp) , но R не знает, что lifeExp это такое (введите это в консоли и посмотрите, что вы получите).

Чтобы все было просто, передайте значения вместо имени столбца здесь.

 Liste <- function(Variable)
{
    list("Minimum" = min(Variable, na.rm = TRUE),
         "Moyenne" = mean(Variable, na.rm = TRUE),
         "Mediane" = median(Variable, na.rm = TRUE),
         "Maximum" = max(Variable, na.rm = TRUE),
         "Caracteristiques position" = ifelse(mean(Variable, na.rm = TRUE) > 
                median(Variable, na.rm = TRUE),"Moyenne > Mediane",
                ifelse(mean(Variable, na.rm = TRUE) < median(Variable, na.rm = TRUE),
          "Mediane > Moyenne","Moyenne = Mediane")),
          "Valeur manquante" = sum(is.na(Variable)),
          "D1" = quantile(Variable, 0.1, na.rm = TRUE),
          "Q25" = quantile(Variable, 0.25, na.rm = TRUE),
          "Q75" = quantile(Variable, 0.75, na.rm = TRUE),
          "D9" = quantile(Variable, 0.9, na.rm = TRUE))
    
}
 

и затем вы можете вызвать эту функцию как :

 Liste(db$lifeExp)
 

Комментарии:

1. Спасибо за этот ответ. Это ответ, наиболее близкий к исходному способу написания моего кода, поэтому я основывался на нем (я опубликую, как я использовал ответы для получения результата, который я искал). Глядя на то, как вы это переписали, я думаю, что я откажусь от использования summary_table функции из qwarps2 пакета. Кстати, я не вставлял весь исходный пример сценария. Это объясняет, почему я не назвал data.frame источника в функции, а только переменную в ней.

Ответ №2:

Используя rlang ‘s ensym , мы можем заключить имя в кавычки, т.е. Вы можете передать имя так, как вы делали без кавычек, и правильно обработать его. более простой альтернативой было бы использовать deparse(substitute(Variable)) . проблема, с которой вы столкнулись в своем коде, заключается в том, что вы передали lifeExp вместо "lifeExp" и если вы передали имя переменной правильно, как бы вы справились с этим на другой стороне, не имея доступа к data.frame ?

 library(tidyverse)
library(rlang)
sommaire <- function(df, Variable){
  Variable <- ensym(Variable)
  df %>%
     summarise(Minimum = min(!!Variable, na.rm=T), Moyenne = mean(!!Variable, na.rm=T), Mediane= median(!!Variable, na.rm=T), 
        Maximum = max(!!Variable, na.rm=T), `Caracteristiques position` = sprintf("Moyenne %s Mediane", case_when(Moyenne > Mediane ~ ">", Moyenne==Mediane ~ "=", T ~ "<")),
        `Valeur manquante` = sum(is.na(!!Variable)), data=list(set_names(quantile(!!Variable, c(.1, .25, .75, .9), na.rm=T), c("D1", "Q25", "Q75", "D9")))) %>%
         unnest_wider(data)
}

sommaire(db, lifeExp)
# A tibble: 1 x 10
  Minimum Moyenne Mediane Maximum `Caracteristiques position` `Valeur manquante`    D1   Q25   Q75    D9
    <dbl>   <dbl>   <dbl>   <dbl> <chr>                                    <int> <dbl> <dbl> <dbl> <dbl>
1    23.6    59.5    60.7    82.6 Moyenne < Mediane                            0  41.5  48.2  70.8  75.1
 

Вы можете применить это ко всем числовым переменным, используя purrr::map :

 map(set_names(colnames(db)[sapply(db, is.numeric)]), sommaire, df=db)  %>% bind_rows(.id="Variable")
# A tibble: 4 x 11
  Variable  Minimum    Moyenne   Mediane      Maximum `Caracteristiques position` `Valeur manquante`       D1       Q25        Q75         D9
  <chr>       <dbl>      <dbl>     <dbl>        <dbl> <chr>                                    <int>    <dbl>     <dbl>      <dbl>      <dbl>
1 year       1952       1980.     1980.        2007   Moyenne = Mediane                            0   1957      1966.      1993.      2002  
2 lifeExp      23.6       59.5      60.7         82.6 Moyenne < Mediane                            0     41.5      48.2       70.8       75.1
3 pop       60011   29601212.  7023596.  1318683096   Moyenne > Mediane                            0 946367.  2793664   19585222.  54801370. 
4 gdpPercap   241.      7215.     3532.      113523.  Moyenne > Mediane                            0    688.     1202.      9325.     19449. 
 

Комментарии:

1. Спасибо за этот ответ. Это близко к тому, что я искал, особенно второй сценарий для того, что я имею в виду в качестве первого шага (имея первый обзор набора переменных в моей базе данных). Причина, по которой я не называю data.frame источника переменной, заключается в том, что я изначально использую функцию summary_table из пакета qwarps2 . Я просто замечаю, что я не вставил весь код, отсюда отсутствие примера того, что я хочу иметь в конце, и правильной иллюстрации того, почему я хотел назвать только переменную, не называя data.frame происхождения.

Ответ №3:

Спасибо всем за ответы.

Я также приношу извинения за то, что не вставил весь пример сценария (моя ошибка, плохая перепроверка перед нажатием кнопки отправки). Часть причины, по которой я решил написать функцию таким образом, исходит от меня, используя summary_table из qwarps2 пакета. Предоставленный вами ответ позволяет мне отказаться от использования функции и пакета, которые делают все это более сложным.

Вот как я использовал ваш ответ для решения своей проблемы.

СТРАТЕГИИ

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

Поэтому я действовал в два этапа :

  1. Изменение моей исходной функции, чтобы сделать ее функциональной ;
  2. Создайте функцию, чтобы создать ее в правильном формате (возможно, с чрезмерной конвульсией).

Исправлена исходная функция

 Liste <- function(Variable)
{
  list("Minimum" = min(Variable, na.rm = TRUE),
       "Moyenne" = mean(Variable, na.rm = TRUE),
       "Mediane" = median(Variable, na.rm = TRUE),
       "Maximum" = max(Variable, na.rm = TRUE),
       "Position" = ifelse(mean(Variable, na.rm = TRUE) > 
                                              median(Variable, na.rm = TRUE),"Moyenne > Mediane",
                                            ifelse(mean(Variable, na.rm = TRUE) < median(Variable, na.rm = TRUE),
                                                   "Mediane > Moyenne","Moyenne = Mediane")),
       "Presente" = sum(!is.na(Variable))
       "Manquante" = sum(is.na(Variable)),
       "D1" = quantile(Variable, 0.1, na.rm = TRUE),
       "Q25" = quantile(Variable, 0.25, na.rm = TRUE),
       "Q75" = quantile(Variable, 0.75, na.rm = TRUE),
       "D9" = quantile(Variable, 0.9, na.rm = TRUE))
  
}
 

Дополнительная функция (вертикальный формат)

 Sommaire <- function(Variable)
{
  Tableau <- as.data.frame(Liste(Variable))
  Tableau <- data.frame(t(Tableau[-1]))
  return(Tableau)
}
 

Еще раз спасибо всем за помощь