间接地引用变量来避免硬编码,这样写R代码是否低效?

3

假设我有以下数据框:

x <- data.frame(id= c("a", "b", "c", "d", "e")
                , term= c(179, 192, 189, 182, 179)
                , f17= c(1, 2, 3, 4, 5)
                , s18= c(6, 7, 8, 9, 10)
                , f18 = c(11, 12, 13, 14, 15)
                , s19 = c(16, 17, 18, 19, 20))

在这个数据框中,我想创建一个变量,记录给定术语(f17对应于术语179,s18对应于术语182,f18对应于术语189,f19对应于术语192)的相应列中每个id的值。
显然,可以轻松地通过一系列ifelse语句来完成,但是每隔几个月,我都会得到新的数据术语,我不想每次获取更多数据时都手动重新编码。此外,我发现这种有许多嵌套ifelse语句的编码非常难以阅读。
我相对较新于R,但是我是非常有经验的SAS和SAS宏程序员,因此我知道在SAS中我想要做的事情可以使用一些数组和在数据步骤中的do循环轻松完成,这就是我试图在R中重现的内容。我最终做的是如下所示。
注意:我意识到以下内容与一系列嵌套ifelse语句不同,而是按顺序的一系列ifelse语句,这些语句正在覆盖相同的变量,但是这确实为我提供了需要的解决方案,考虑到我数据中的所有情况。
xTerms <- c(179, 182, 189, 192)
xVars <- c("f17", "s18", "f18", "s19")

x$startVal <- NA
for(i in 1:length(xTerms)){
  x$startVal <- ifelse(x$term == xTerms[i], x[[xVars[i]]], x$startVal)
}

我应该补充一下,这是期望的结果:
> x
  id term f17 s18 f18 s19 startVal
1  a  179   1   6  11  16        1
2  b  192   2   7  12  17       17
3  c  189   3   8  13  18       13
4  d  182   4   9  14  19        9
5  e  179   5  10  15  20        5

以上代码的思路是,当我获取新数据时,我只需要更新xTerms和xVars的定义。或者我甚至可以根据x中术语变量的唯一值列表以及x中变量来动态创建它们。
对于这种迭代问题,是否这种方法是在R中解决的最佳方法,我很想听取更有经验的R用户的反馈意见?你能否分享一些资源,介绍如何更好地利用R进行此类操作?

1
我认为更好的选择是使用行列索引 x[cbind(match(xTerms, x$term), match(xVars, names(x)))] - akrun
3个回答

2

You can use match...

xTerms <- c(179, 182, 189, 192)
xVars <- c("f17", "s18", "f18", "s19")

x$startVal <- sapply(1:nrow(x), function(i) x[i, xVars[match(x$term[i], xTerms)]])

x
  id term f17 s18 f18 s19 startVal
1  a  179   1   6  11  16        1
2  b  192   2   7  12  17       17
3  c  189   3   8  13  18       13
4  d  182   4   9  14  19        9
5  e  179   5  10  15  20        5

太棒了!非常感谢!你用match的方式给了我很多思考的东西!我以前遇到过函数和apply家族的问题,但我会继续努力的! - beri

2
如果您将xTermsxVars放在查找表lkp中,您可以使用melt将数据转换为长格式,并加入lkp以获取起始值。然后,您可以重新加入x作为列。"最初的回答"
library(data.table)
setDT(x)

lkp <- data.table(Terms = xTerms, Vars = xVars)

startvals <- melt(x, c('id', 'term'))[lkp, on = .(term == Terms, variable == Vars)]

x[startvals, on = .(id, term), startVal := value]


x  
#    id term f17 s18 f18 s19 startVal
# 1:  a  179   1   6  11  16        1
# 2:  b  192   2   7  12  17       17
# 3:  c  189   3   8  13  18       13
# 4:  d  182   4   9  14  19        9
# 5:  e  179   5  10  15  20        5

谢谢!我一直在思考的一个问题是,我使用的df结构是否真的是一个好的结构,或者我应该将数据放在长格式中,你的回答提醒我更多地思考这个问题。此外,我不熟悉你用于连接数据的语法(我一直在使用合并函数),所以我又学到了一件新事情!谢谢! - beri
是的,那个语法是特定于data.table包的。在这个问题https://dev59.com/1nM_5IYBdhLWcg3wn0lO中有一些很好的解释,说明它如何对应于左/右/内连接。 - IceCreamToucan

1
一个选择是使用 行/列 索引。
x$startVal <- x[3:6][cbind(seq_len(nrow(x)), 
             match(xVars[match(x$term, xTerms)], names(x)[3:6]))]
x
#  id term f17 s18 f18 s19 startVal
#1  a  179   1   6  11  16        1
#2  b  192   2   7  12  17       17
#3  c  189   3   8  13  18       13
#4  d  182   4   9  14  19        9
#5  e  179   5  10  15  20        5

1
谢谢!我一直对使用cbind持谨慎态度,因为它基本上是将两列数据粘在一起,而不是根据一个或多个id变量进行合并,这让我感到很危险。但在这种情况下,我可以看到它非常强大!我需要更多地尝试它。 - beri
@beri cbind 的结果是一个矩阵,与 for 循环相比应该非常快。 - akrun
1
啊,这对于效率来说非常有用!谢谢! - beri

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