首页 > 解决方案 > 在 R 函数中创建可重用对象的推荐方法

问题描述

假设我们有以下数据:

# 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”的对象,那么还有一个潜在的问题。

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

提前致谢!

标签: renvironment-variables

解决方案


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

例如,您可以创建一个包含必要组件的 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这里调用一次。


推荐阅读