【问题标题】:What is a more efficient way to tackle long "else if" statements in R?在 R 中处理长“else if”语句的更有效方法是什么?
【发布时间】:2019-07-02 20:05:49
【问题描述】:

我正在寻找有关如何提高我的 r 脚本中长 if/else 循环的效率的建议。我还希望尽可能地适应未来,因为它的范围可能会随着时间而改变。

背景

使用其他语言的遗留代码,我正在自动化一个大型项目的报告流程。在这个项目中,我有多个“中心”来创建基于数据库子集的报告。现在,我使用如下所示的长示例循环来完成此操作:

df$ReportName <- 0

df$new_centername[is.na(df$new_centername)] <- 0

for (i in 1:nrow(df)){
  if (df$new_centername[i] == 1){
    df$ReportName[i] <- "Center A"
  } else if (df$new_centername[i] == 2){
    df$ReportName[i] <- "Center B"
  } else if (df$new_centername[i] == 3){
    df$ReportName[i] <- "Center C"
  } else if (df$new_centername[i] == 4){
    df$ReportName[i] <- "Center D"
  } else if (df$new_centername[i] == 5){
    df$ReportName[i] <- "Center E"
  } else if (df$new_centername[i] == 6){
    df$ReportName[i] <- "Center F"
  } 
  ...

df是通用数据框,new_centername指定该行数据属于哪个Center,全部用数字编码。

随着时间的推移,我预计会添加更多的中心。我假设有一种方法可以将这些中心存储在一个列表中,然后创建一个遍历列表的循环。这样,在添加新中心时,我只需将它们的名称添加到列表中。

编辑:

  • “中心__”在这里只是一个占位符,实际中心名称差异很大。
  • 数字 ID 也不遵循直线模式。

建议的解决方案:

SampleList <- list("Center A", "Center B", "Center C", ...)

for (i in 1:nrow(df)){
  for (j in 1:length(SampleList)){
    if (df$new_centername[i] == 1){
      df$ReportName[i] <- SampleList[j]
    }
  }
}

非常感谢帮助我围绕最佳逻辑和适当的语法进行优化! 谢谢

【问题讨论】:

  • 你可以使用ifelse,它是矢量化的,不需要循环
  • @Sotos 感谢您的反馈!我过去使用过矢量化 ifelse,但由于我有超过 60 个唯一标识符,因此这种格式同样会让人感到困惑。这就是为什么我正在寻找一个可以跨列表映射的更通用的解决方案。

标签: r loops vectorization


【解决方案1】:

根据显示的代码,我们可以直接假设'new_centername'中的值是从1开始的序列,它可以用作填充其他值的索引。下面,我们使用paste0 创建一个字符串向量,这样'Center A' 将替换或获取'new_centername' 为1、'Center B'、2 等的位置......只是为了展示这个概念,仅在“Center F”之前创建。

df$new_centername <- paste0("Center ", LETTERS[1:6])[df$new_centername]

正如 OP 提到的替换值中没有模式并且索引也不同,那么有效的方法(在 @JasonAizkalns 的 cmets 中也建议)将是创建一个 key/val 数据集,然后与原始数据连接

keyval <- data.frame(key = c(5, 12, 13, 25), 
                     val = c('ASD', 'BDF', 'ANF', 'SDT'), stringsAsFactors = FALSE)
library(data.table)
setDT(df)[keyval, new_name := val, on = .(new_center_name = key)]
df

【讨论】:

  • 不错的方法,也许是为了“面向未来”,创建一个大列表/向量/数据库,然后左连接?还是调整 LETTERS[i] 与传入的长度一致?
  • @JasonAizkalns 谢谢。只要向量长度中的值等于唯一索引的长度,它就可以工作。我假设 OP 已经有一个与原始索引匹配的 'Center' 值长度向量
  • 嗨@akrun,这是一个很好的顺序名称和数字解决方案。对于我的问题,“中心__”只是实际中心名称的占位符,以保护他们的身份。实际名称差异很大,没有模式,数字有一些视觉差异。这就是为什么我希望我可以映射到SampleList。我会更新帖子以反映这一点。
  • 或者,稍微简单一点:paste0("Center ", LETTERS[df$new_centername])
  • @rsh52 您可以创建一个名称向量(这只是基于模式的演示),如果数字也不同,那么我建议创建一个 key/val data.frame并做一个left_join,但在创建key/val数据集时仍然很痛苦,因为没有模式。最后,它会比编写嵌套的ifelse 更好更高效(顺便说一句,ifelse 有一个限制,- 可能在 54 左右)
猜你喜欢
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多