【问题标题】:Customised output name in global environment using R functions使用 R 函数在全局环境中自定义输出名称
【发布时间】:2021-06-09 12:49:45
【问题描述】:

我是 R 编程新手,对这个问题有点困惑。我有 3 个不同的数据库,我想从中提取 2 组测试和训练数据。我知道如何使用单独的代码行来执行此操作,但这是重复性任务,因此我想使用函数自动执行此操作。

我希望我的输出名称带有数据库名称,然后是测试或训练,然后是数字,我想将它们直接导出到全局环境中。这是我编写的代码(使用 MTCARS)作为不工作的示例数据库。我也给出了我想要的输出

需要说明的是,这只是一个示例。在不同的领域,我想在全局环境中自定义 function(x) 的输出,而无需一遍又一遍地编写代码。

mydata<-mtcars
 
output_2_set_test_train <-function(x){

  library(caTools)
  i=1
  dfname <- deparse(substitute(x))

  while(i<=2){
    sample.split(x[[1]], SplitRatio = .75)->split_tag
    subset(x, split_tag==T)->> paste0(dfname,"_","train","_",i)
    subset(x, split_tag==F)->> paste0(dfname,"_","test","_",i)
    i=i+1
  }
}
 
output_2_set_test_train(mydata)

运行我的函数后,我希望我的全局环境具有以下具有此特定名称的数据框 -

#1 数据框命名 -> mydata_train_1 #2 数据框命名 -> mydata_test_1 #3 数据框命名 -> mydata_train_2 #4 数据框命名 -> mydata_test_2

我尝试对此进行大量搜索,但找不到任何有效的答案。

有人可以帮我更正代码吗?

【问题讨论】:

  • 这基本上是一个非常糟糕的主意,即使它有效。不要将函数内部的东西分配到全局环境中。相反,返回值。其他一些注意事项:在函数中调用 library 也是一个坏主意,因为它会导致另一个全局副作用。写foo==TRUE 是多余的;只需写foofoo==FALSE 也一样:只写 ! foo。并且不要使用快捷方式TF,因为这些是可以被覆盖的变量。最后,您的 while 循环不必要地令人费解——这应该是一个 for 循环,或者更好的是,一个 lapply 调用。
  • 还有一个:从左到右的赋值是单调的,应该用从右到左的赋值代替,以使受让人更明显。我建议您忘记 -&gt;/-&gt;&gt; 运算符甚至存在,并且永远不要使用它。它在 R 中的存在确实是一个错误(这是几乎没有其他语言具有等价物的原因)。
  • 您好@KonradRudolph,感谢您的反馈。正如我提到的,我对 R 和编程完全陌生。感谢您的反馈。它将帮助我学习和提高,但我一直在尝试自学并且对惯用语/单用语代码一无所知。我会阅读。我使用 -> / ->> 的原因是因为我在 MIT 上 EDX 的基础课程,他们建议在 R 中使用 -> 而不是 =。

标签: r dataframe function customization


【解决方案1】:

请参阅 Konrad 关于此类编码以及 R 中的约定的警告。您的代码中几乎没有什么是惯用的。

您需要替换这些行:

subset(x, split_tag==T)->> paste0(dfname,"_","train","_",i)
subset(x, split_tag==F)->> paste0(dfname,"_","test","_",i)

类似:

assign(paste0(dfname,"_","train","_",i), subset(x, split_tag==T), envir = globalenv())
assign(paste0(dfname,"_","test","_",i), subset(x, split_tag==F), envir = globalenv())

【讨论】:

  • 如果你帮助人们编写糟糕的代码,至少请提及写这实际上是一个坏主意。
  • @KonradRudolph,风格问题对我来说很明显,我在示例中没有遇到太多assign,所以我通常不会使用它......但不是程序员,我不要以为我知道为什么这“从根本上”是一个坏主意。这听起来像是“你不应该在 R 中使用 for 循环”。例如,如果您的目标是在全局环境中实际查看您的对象,您的 IDE 可能会为您提供即时信息以供您分析;有什么大不了的?
  • 这不是“风格问题”,而是代码质量问题。它与“你不应该使用循环”属于根本不同的“坏主意”类别,尽管后者不仅仅是一个风格问题。 ——而且,是的,有个有效用途,但是>99% 的 Stack Overflow 请求不属于这一类,而且我没有看到任何迹象表明这个问题属于 1%。本网站回答的目的是帮助。这在很大程度上包括对有问题/错误的代码提供反馈。
  • 对不起,我不想争论。我问是因为我真的很想知道在一般函数内部分配通常有什么问题
  • 我专门使用了assign 而不仅仅是&lt;&lt;-,因为直觉上,故意指定某个位置似乎相对安全,而不是让它匹配它遇到的任何对象......但我不是确定我是否遗漏了其他内容?
【解决方案2】:

R 的一个重要概念是函数应具有尽可能少的副作用。 副作用是除了返回值之外的任何操作。

这有几个明显的例外(例如,将数据写入文件的函数,显然 需要 有副作用)但在调用/全局范围内创建变量几乎这绝不是一个好主意,因为 返回值 是为此的预期机制。

在您的情况下,您通常会返回两个值的列表。连同对代码的其他一些更改,结果如下所示:

output_2_set_test_train <- function (x) {
  is_training <- caTools::sample.split(x[[1L]], SplitRatio = 0.75)
  list(
    train = subset(x, is_training),
    test = subset(x, ! is_training)
  )
}

你会像这样使用函数:

data <- output_2_set_test_train(mtcars)

现在data$train 包含训练集,data$test 包含测试集。

关于实现的几点说明:

  1. 显然,最重要的变化是我们返回函数的值,而不是将其分配到全局环境中。
  2. 我不使用library,因为这是另一个全局副作用:library 将包附加到全局而不是本地(如果您对更好的方法感兴趣,请查看包'box')。
    • 相反,我们使用 caTools::sample.split 来引用函数内部“caTools”包中的这个特定函数
  3. 我删除了无用的 ==T==F 比较。
  4. 我已经完全删除了 while 循环以及与之相关的所有内容。
  5. 我已将 split_tag 重命名为 is_training,这是一个更具描述性的名称。

【讨论】:

  • 感谢您。关于您的代码的最后一个问题。它更干净,但我在其中有一个循环的原因是有 2 组不同的训练和测试数据,并且还避免编写更多代码,因为每次我都必须输入手动代码,即 mydata_test_1 = as.data .frame(data$test) 然后是 mydata_test_2 = as.data.frame(data$test2) 等等。有没有好办法让这些事情自动化?
  • 如果这些问题对您来说听起来很愚蠢,请提前道歉,因为您是超级用户并且比喻为博士,而我仍在尝试学习 A、B、C。我正在学习数据分析以供自己使用(并减少重复性任务)
  • @GauravArora 自动化事物绝对是好的,但是对于 两个 不同的事物,将它们单独写出来而不是使用循环几乎总是更具可读性。而且你不需要使用as.data.frame(data$test),数据已经data.frame——所以你可以直接使用data$test,也不需要将它分配给另一个变量——当然你可以,如果这样可以让事情更清楚的话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-24
  • 1970-01-01
  • 1970-01-01
  • 2020-12-22
  • 1970-01-01
  • 2016-06-10
  • 2016-03-27
相关资源
最近更新 更多