使用R编程,以编程方式命名对象的最佳方法是什么?

4

我正在对一个数据集运行各种建模算法。通过将我的输入变量逐个与响应建模,我获得了最佳结果,例如:

model <- train(y ~ x1 + x2 + ... + xn, ...)

在我训练好我的模型后,我希望不必每次重新运行它们,因此我一直试图将它们保存为.rda文件。这里是一个随机森林模型的示例循环(如果有更好的方法,请随意建议!):

# data_resp contains my measured responses, one per column
# data_pred contains my predictors, one per column

for (i in 1:ncol(data_resp)) {

  model <- train(data_pred_scale[!is.na(data_resp[, i]), ],
                 data_resp[!is.na(data_resp[, i]), i],
                 method = "rf",
                 tuneGrid = data.frame(.mtry = c(3:6)),
                 nodesize = 3,
                 ntrees = 500)

  save(model, file = paste("./models/model_rf_", names(data_resp)[i], ".rda", sep = ""))

当我加载这个模型时,它会被称为“model”。 目前我还没有找到一个好的方法来保存相应名称的模型,以便之后可以引用。我发现可以通过以下方式将对象赋值给字符串:
assign(paste("./models/model_rf_", names(data_resp)[i], ".rda", sep = ""), train(...))

但是当我保存对象时,我仍然需要考虑如何引用它:

save(???, file = ...)

我不知道如何用自定义名称调用对象。
最后,即使是加载也出现了问题。我尝试使用assign("model_name", load("./model.rda")),但结果对象名为string,只是保存了对象名称"model"的字符串。
在搜索中,我发现这个问题似乎相关,但我正在努力弄清楚如何将其应用到我的情况。
我可以创建一个列表,其中包含data_resp(我的测量响应)中每个列名的名称,然后使用lapply来使用train(),但我仍然有点困惑如何动态地引用新对象名称以保留结果模型。

1
你关于save()的具体问题可以通过简单阅读文档并注意第二个参数来得到答案。 - joran
1
尝试在这种情况下使用saveRDSreadRDS,它会简化很多事情。 - dickoa
4个回答

3
当您保存模型时,请保存另一个名为“name”的对象,该对象是您想要命名的字符字符串:
> d=data.frame(x=1:10,y=rnorm(10))
> model=lm(y~x,data=d)
> name="m1"
> save(model,name,file="save1.rda")
> d=data.frame(x=1:10,y=rnorm(10))
> model=lm(y~x,data=d)
> name="m2"
> save(model,name,file="save2.rda")

现在每个文件都知道它想要的结果对象被称为什么。当你加载时该如何得到它呢?将其加载到一个新环境中,并进行赋值:
> e=new.env()
> load("save1.rda",env=e)
> assign(e$name,e$model)
> summary(m1)

Call:
lm(formula = y ~ x, data = d)

现在您可以安全地删除或重新使用“e”对象。当然,您可以将其包装在一个函数中:

> blargh=function(f){e=new.env();load(f,env=e);assign(e$name,e$model,.GlobalEnv)}
> blargh("save2.rda")
> m2

Call:
lm(formula = y ~ x, data = d)

请注意,这样做是极不好的——首先,您应该将所有模型存储在一个文件中,作为一个带名称的列表。其次,此函数具有副作用,如果您已经有一个名为m2的对象,则会被覆盖。
像这样使用assign几乎总是一个信号(dyswidt?),表明您应该使用列表。

1

这个答案涉及到一定的猜测,但我认为这可能有所帮助:

# get a vector with the column names in data_resp
modNames <- colnames( data_resp )

# create empty list
models <- as.list( NULL )

# iterate through your columns and assign the result as list members
for( n in modNames )
{
  models[[n]] <- train(data_pred_scale[!is.na(data_resp[, n]), ],  ### this may need modification, can't test without data
                 data_resp[!is.na(data_resp[, n]), n],
                 method = "rf",
                 tuneGrid = data.frame(.mtry = c(3:6)),
                 nodesize = 3,
                 ntrees = 500)
}

# save the whole bunch
save( models, file = "models.rda" )

现在,您只需使用load("models.rda")就可以检索到一个对象,该对象是包含所有模型的列表,并且可以使用列表表示法进行访问,例如models[[1]]或使用列名,例如models[["first"]]。请注意,保留HTML标签。

1
我认为其他有关使用循环完成此操作的答案非常好。我将其作为一个机会,尝试更好地理解lapply,因为许多关于如何执行此操作的StackOverflow问题最终都建议使用列表和lapply而不是循环。
我真的很喜欢将train()的所有结果合并到列表中(@vaettchen在他的循环中实现了这一点),并且在考虑如何使用列表完成此操作时,我想出了以下方法。首先,我需要将我的数据框转换为列表形式,每列一个条目。由于我不太使用列表,所以我四处寻找,直到只是尝试as.list(df),它就像魔法般地奏效了。
接下来,我想要对测量响应变量列表中的每个元素应用我的train函数,因此我定义了以下函数:
# predictors are stored in data_pred
# responses are in data_resp (one per column)
# rows in data_pred/data_resp (perhaps obviously) match, one per observation

train_func <- function(y) { train(x = data_pred, y = y,
   method = "rf", tuneGrid = data.frame(.mtry = 3:6),
   ntrees = 500) }

现在我只需要使用lapplytrain()调用应用于data_resp的每个元素。我不知道如何创建一个空的占位符列表,所以感谢@vaettchen(我一直在尝试使用list_name <- list()但没有成功):
models <- lapply(as.list(data_resp), train_func)

令人惊奇的是,我发现models中的元素自动命名为data_resp中的列名,这太棒了。我将与shiny包一起使用,因此用户可以轻松地从下拉菜单中选择响应变量(可以存储响应变量名称),并执行以下操作:

predict(models[["resp_name"]], new_data)

我认为这比基于循环的方法要好得多,一切都恰到好处地落实了。我意识到问题明确要求以编程方式命名变量,如果这促使其他人以那种方式回答而不是给出更广泛的答案,那我道歉。使用lapply的简易性表明,当存在一个(至少在我看来)更好的解决方案时,我试图强行采用特定的解决方案。

奖励: 我没有意识到列表可以是多维的,但在尝试后,它们似乎可以!这甚至更好,因为我正在使用许多算法,我可以将所有内容存储在一个大的列表对象中。

 func_rf <- function(y) { train(x = data_pred, y = y,
     method = "rf", tuneGrid = data.frame(.mtry = 3),
     ntrees = 100) }

 # svmRadial method requires formula syntax to work with factors,
 # so the train function has to be a bit different
 # add `scale = F` since I had to preProcess the numeric vars ahead of time
 # and cbind to the factors. Without it, caret will try to scale the data
 # for you, which fails for factors

 func_svm <- function(y) { train(y ~ ., cbind(data_pred, y),
     method = "svmRadial", tuneGrid = data.frame(.C = 1, .sigma = .2),
     scale = F) }

 model_list <- list(NULL)
 model_list$rf <- lapply(as.list(data_resp), func_rf)
 model_list$svm <- lapply(as.list(data_resp), func_svm)

现在我可以使用列表语法引用所需的模型响应变量!
 predict(model_list[["svm"]][["response_variable"]], new_data)

非常满意这个,希望它能使代码更加高效,更快,并且我真的喜欢我最终得到的“元对象”,而不是一堆文件,每个文件对应一个模型/响应变量组合,后面我必须逐个加载。

上面的函数在使用lapply时是否有效?我正在尝试使用trainControl做同样的事情,但是当运行lapply时,参数似乎无法传递。 - Prophet60091
@Prophet60091 你能具体一点吗?我已经有一段时间没有使用它了,但我肯定曾经使用过它...所以是的,它像展示的那样工作。你是想创建一个trainControl对象列表,然后迭代它们以查看对结果模型的影响吗?还是只是让lapply()迭代模型/数据,并将trainControl对象作为train()参数的一部分带入? - Hendy

0
这是一个有点老的问题,但仍然没有被接受的答案。
据我所知,您需要以编程方式重命名变量并保存它,以便在重新加载时保留新名称。
尝试这个:
saveWithName = function(var.name, obj){
  # var.name is a string with the name of the variable you want to assign
  # obj is any kind of R object (data.frame, list, etc.) you want to rename and save
  assign(var.name, obj)
  save(list=var.name, file=sprintf("model_%s.RData", var.name))
}

saveWithName("lab1", c(1,2))
saveWithName("lab2", c(3,4))
load("model_lab1.RData")
load("model_lab2.RData")

print(lab1)
#>[1] 1 2
print(lab2)
#[1] 3 4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接