Лучшая практика, чтобы избежать конфликта с предоставленными пользователем именами переменных - R - PullRequest
1 голос
/ 16 апреля 2020

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

(Обратите внимание, что обновление среды формулы требуется, чтобы R не смотрел в R_GlobalEnv пользователя для моей переменной y.)

# R Version 3.6.2
my_function <- function(user_data, user_formula){
  y <- as.numeric(user_data[,1] > mean(user_data[,1]))

  my_formula <- update.formula(user_formula, y ~ .)
  environment(my_formula) <- environment()

  my_model <- lm(my_formula, data = user_data, model = TRUE)
  return(my_model)
}

some_data <- data.frame(x1 = c(1,2,3,3))
some_formula <- response ~ x1
my_function(some_data, some_formula)

Выше приведено то, что я хочу запустить, и это работает, пока нет переменных в user_formula или user_data с именем "y". Но когда user_data содержит переменную с тем же именем, модель будет использовать эту переменную вместо моей.

some_data <- data.frame(x1 = c(1,2,3,3), y = c(6,7,5,6))
some_formula <- response ~ x1 + y
my_function(some_data, some_formula)$model
#   y x1
# 1 6  1
# 2 7  2
# 3 5  3
# 4 6  3
# Warning messages:
# 1: In model.matrix.default(mt, mf, contrasts) :
#   the response appeared on the right-hand side and was dropped
# 2: In model.matrix.default(mt, mf, contrasts) :
#   problem with term 2 in model.matrix: no columns are assigned

Я попытался заставить R выполнить поиск в среде функции y, используя get (),

my_function <- function(user_data, user_formula){
  y <- as.numeric(user_data[,1] > mean(user_data[,1]))

  e1 <- environment()
  my_formula <- update.formula(user_formula, get("y", e1) ~ .)
  environment(my_formula) <- environment()

  my_model <- lm(my_formula, data = user_data, model = TRUE)
  return(my_model)
}

some_data <- data.frame(x1 = c(1,2,3,3), y = c(6,7,5,6))
some_formula <- response ~ x1 + y
my_function(some_data, some_formula)$model
#   get("y", e1) x1 y
# 1            0  1 6
# 2            0  2 7
# 3            1  3 5
# 4            1  3 6

Но это также не удается, если в пользовательских данных есть переменная с тем же именем как мое имя внутренней среды,

some_data <- data.frame(x1 = c(1,2,3,3), y = c(6,7,5,6), e1 = c(1,2,3,4))
some_formula <- response ~ x1 + y + e1
my_function(some_data, some_formula)$model
#  Error in get("y", e1) : invalid 'envir' argument 

Как правильно избежать наложения моих внутренних переменных на предоставленные пользователем имена переменных? Я бы предпочел метод для базы R, если это возможно.

1 Ответ

0 голосов
/ 16 апреля 2020

За документы из lm, аргумент data обрабатывает переменные в формуле двумя способами, которые НЕ являются взаимоисключающими:

данные необязательный фрейм данных, список или окружение (или объект, поддающийся as.data.frame для фрейма данных), содержащий переменные в модели. Если не найдено в data, переменные берутся из environment(formula), обычно из среды, из которой вызывается lm.

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

Чтобы избежать конфликта имен, рассмотрите возможность добавления подходящего заполнителя, такого как response или зависимая_вариабельная , и выведите предупреждение tryCatch, если пользователь предоставит фрейм данных с тем же именем столбца. Этот подход также позволяет избежать сброса environment(formula).

my_function <- function(user_data, user_formula){
  tryCatch({
       user_data$response <- as.numeric(user_data[,1] > mean(user_data[,1]))

       my_formula <- update.formula(user_formula, response ~ .)

       my_model <- lm(my_formula, data = user_data, model = TRUE)

       return(my_model)
  }, warning = function(w) message("Please rename column 'response' in your data frame")
   , error = function(e) print(e)
  )
}

Выход

some_data <- data.frame(x1 = c(1,2,3,3))
some_formula <- response ~ x1
my_function(some_data, some_formula)

# Call:
# lm(formula = my_formula, data = user_data, model = TRUE)

# Coefficients:
# (Intercept)           x1  
#     -0.7273       0.5455  

some_data <- data.frame(x1 = c(1,2,3,3), y = c(6,7,5,6), e1 = c(1,2,3,4))
some_formula <- response ~ x1 + y + e1
my_function(some_data, some_formula)

# Call:
# lm(formula = my_formula, data = user_data, model = TRUE)

# Coefficients:
# (Intercept)           x1            y           e1  
#   1.667e+00    3.850e-16   -3.333e-01    3.333e-01  

some_data <- data.frame(x1 = c(1,2,3,3), y = c(6,7,5,6), response=c(1,1,1,1))
some_formula <- response ~ x1 + y + response
my_function(some_data, some_formula)
# Please rename column 'response' in your data frame
...