缩短嵌套的ifelse语句

3
如果给定以下数据表,并且我们想要依次将 x1 与 x2 到 x5 进行比较,则可以使用以下方法:
set.seed(1)
library(data.table)
TDT <- data.table(x1 = round(rnorm(100,0.75,0.3),2),
                  x2 = round(rnorm(100,0.75,0.3),2),
                  x3 = round(rnorm(100,0.75,0.3),2),
                  x4 = round(rnorm(100,0.75,0.3),2),
                  x5 = round(rnorm(100,0.75,0.3),2))

TDT[,compare := ifelse(x1 < x2,1,ifelse(x1 < x3,2,ifelse(x1 < x4,3,ifelse(x1 < x5,4,5))))]

因此,如果x1 < x2,则compare == 1,以此类推。

现在在我的例子中,我有很多列需要与x1进行比较。是否有更简洁的方法来写这个,即不使用嵌套的ifelse?


2
max.col(TDT$x1 < cbind(TDT[, -1, with=F], Inf), "first") - user20650
1
@user20650 那是一个聪明的选项。我喜欢它。 - akrun
需要重写data.tableish,干杯Akrun! - user20650
1
@user20650 我认为可以改成TDT[, compare := max.col(x1 < data.table(.SD, Inf), "first"), .SDcols = x2:x5]。我尝试使用c(.SD, Inf),但不知何故它返回错误。 - akrun
1
不错的东西 @Akrun,看起来更像一点了。 - user20650
显示剩余3条评论
2个回答

6

这样做可以省去一些打字,而且易于阅读。

TDT[, compare := dplyr::case_when(
      x1 < x2 ~ 1,
      x1 < x3 ~ 2,
      x1 < x4 ~ 3,
      x1 < x5 ~ 4,
      TRUE ~ 5)]

如果您有许多列,不想逐个指定它们的名称,则可以使用以下方式:
apply(TDT, 1, function (x) which(x[1] < x[2:5])[1]) 

在这里,x[2:5] 应该被相关的列替换。


我以前从未使用过 := 运算符。你能简要解释一下它在这里的作用吗?根据在线文档,我还不太清楚... - patrick
我也不知道。可能是data.table的某种疯狂操作,我想它会创建一个新列。 - user3603486
很酷,谢谢!我在这方面多读了一些内容::=操作符通过引用原地更新data.table列,在替换或重新分配数据集的部分时避免任何浅层或深层复制。您可以像这样使用它:DT [,c(“colA”,“colB”, ...):= list(valA,valB,...)] - patrick
1
@patrick 请查看入门指南页面。 - MichaelChirico

5
我们可以使用data.table中的Mapmax.col来实现这一点。
TDT[, compare := {d1 <- as.data.table(Map(function(x) x1 < x, .SD))
       max.col(d1, "first") *(c(5, 1)[((Reduce(`+`, d1)!=0)+1)])}, .SDcols = x2:x5]

#OP's code
v1 <- TDT[, ifelse(x1 < x2,1,ifelse(x1 < x3,2,ifelse(x1 < x4,3,ifelse(x1 < x5,4,5))))]
identical(v1, TDT$compare)
#[1] TRUE

@user3032689 我修改了答案。 - akrun
3
我认为比起使用Map()/max.col()/Reduce, 更干净的解决方法是使用一个简单且灵活的循环--例如像这样的代码 compare = rep_len(length(TDT), nrow(TDT)); for(i in length(TDT):2) compare[TDT[[i]] > TDT[[1]]] = i - 1L 看起来是有效的。 - alexis_laz

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