【问题标题】:Best way to name objects programmatically using R?使用 R 以编程方式命名对象的最佳方法?
【发布时间】:2013-07-20 02:22:05
【问题描述】:

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

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 最终只包含对象名称“模型”的字符串。


环顾四周,我发现了THIS 问题,这似乎是相关的,但我正试图弄清楚如何将其应用于我的情况。

我可以创建一个列表,其中包含data_resp(我测量的响应)中每个列名的名称,然后使用lapply 来使用train(),但我仍然有点卡在如何动态引用保存结果模型的新对象名称。

【问题讨论】:

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

标签: r loops variable-assignment


【解决方案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"]].

【讨论】:

    【解决方案2】:

    当你保存模型时,保存另一个名为'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)
    

    您现在可以安全地 rm 或重新使用 '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?)你应该使用一个列表来代替。

    B

    【讨论】:

      【解决方案3】:

      我认为有关使用循环执行此操作的其他答案很棒。我以此为契机,最终尝试更好地理解lapply,因为许多关于如何做到这一点的StackOverflow问题最终建议使用列表和lapply而不是循环。

      我真的很喜欢将train() 的所有结果组合到一个列表中的想法(@vaettchen 在他的循环中做了),并且在考虑如何使用列表执行此操作时,这就是我想出的。首先,我需要列表形式的data.frame,每列一个条目。由于我并不真正使用列表,所以我四处寻找直到尝试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) }
      

      现在我只需要使用lapplydata_resp 的每个元素应用train() 调用。我不知道如何创建一个空的占位符列表,因此感谢@vaettchen(我尝试list_name &lt;- 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 你能说得更具体一点吗?自从我使用它已经有一段时间了,但我肯定 确实 使用过它......所以是的,它的工作原理如图所示。您是否尝试创建trainControl 对象列表,然后遍历它们以查看对结果模型的影响?或者只是让lapply() 遍历模型/数据并引入trainControl 对象作为train() 参数的一部分?
      【解决方案4】:

      有点老问题,但仍然没有公认的答案。
      据我了解,您需要以编程方式重命名变量并保存它,以便在重新加载时保留新名称。
      试试这个:

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-06
        • 2013-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-25
        • 2011-05-18
        相关资源
        最近更新 更多