Warm tip: This article is reproduced from serverfault.com, please click

environment variables-在R函数中创建可重用对象的推荐方法

(environment variables - Recommended way of creating reusable objects within an R function)

发布于 2020-12-01 18:16:27

假设我们有以下数据:

# simulate data to fit
set.seed(21)
y = rnorm(100)
x = .5*y + rnorm(100, 0, sqrt(.75))

我们还假设用户已拟合模型:

# user fits a lm
mod = lm(y~x)

现在假设我有一个R包,旨在对对象执行一些操作mod为了简化起见,假设我们有两个函数,一个函数绘制数据,另一个函数计算系数。但是,作为中介,假设我们要对数据执行一些操作(在此示例中,添加十)。

例子:

# function that adds ten to all scores
add_ten = function(model) {
  data = model$model
  data = data + 10
  return(data)
}

# functions I defined that do something to the "add_ten" dataset
plot_ten = function(model) {
  new_data = data.frame(add_ten(model))
  x = all.vars(formula(model))[2]
  y = all.vars(formula(model))[1]
  ggplot2::ggplot(new_data, aes_string(x=x, y=y)) + geom_point() + geom_smooth()
}

coefs_ten = function(model) {
  new_data = data.frame(add_ten(model))
  coef(lm(formula(model), new_data))
}

(显然,这样做是很愚蠢的。实际上,我要执行的操作是多重插补,这在计算上是很费力的)。

请注意,在上面的示例中,我必须add_ten两次调用该函数,一次是plot_ten,一次是coefs_ten。这是低效的。

那么,现在我的问题是,在函数内创建可重用对象的最佳方法是什么?

我当然可以创建一个放置在用户全局环境中的对象

add_ten = function(model) {
  # check for add_ten_data in the global environment
  if (exists("add_ten_data", where = .GlobalEnv)) return(get("add_ten_data", envir = .GlobalEnv))
  data = model$model
  data = data + 10
  # assign add_ten_data to the global environment
  assign('add_ten_data', data, envir = .GlobalEnv)
  return(data)
}

我很高兴这样做,但是担心将某些东西放到用户环境中的“礼节”。如果用户在其环境中碰巧有一个名为“ add_ten_data”的对象,则还存在一个潜在的问题。

那么,实现此目标的最佳方法是什么?

提前致谢!

Questioner
dfife
Viewed
0
Allan Cameron 2020-12-02 02:45:02

你当然应该避免将对象写入全局环境。如果你发现必须在多个不同功能的顶部重复执行相同的计算量大的任务,则意味着你执行该计算量大的任务为时已晚。

例如,你可以创建一个S3类,其中包含必要的分量以生成“便宜”图和系数的“便宜”提取。它甚至具有通用调度的优点:

add_ten <- function(model) model$model + 10

lm_tens <- function(formula, data)
{
  model <- if(missing(data)) lm(formula) else lm(formula, data = data)
  
  structure(list(data = data.frame(add_ten(model)), model = model),
            class = "tens")
}

plot.tens <- function(tens) {
  x = all.vars(formula(tens$data))[2]
  y = all.vars(formula(tens$data))[1]
  ggplot2::ggplot(tens$data, ggplot2::aes(x = x, y = y)) + 
    ggplot2::geom_point() + 
    ggplot2::geom_smooth()
}

coef.tens = function(tens) {
  coef(lm(formula(tens$model), data = tens$data))
}

所以现在我们只需要做:

set.seed(21)
y = rnorm(100)
x = .5*y + rnorm(100, 0, sqrt(.75))

mod <- lm_tens(y ~ x)
coef(mod)
#> (Intercept)           x 
#>   4.3269914   0.5775404
plot(mod)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

请注意,我们add_ten在这里只需要调用一次即可。