如何高效地解决a[b]可能返回不同长度向量的问题?

3
> a <- factor(1:3)
> b <- c(0,1,2)
> ifelse(b == 0, 5, a[b])
[1] 5 2 1

我希望看到的结果是5,1,2 原因是a[b]实际上返回1,2,其长度与a不同,因此由于循环而导致ifelse的结果被破坏。
我可以使用if...else编写ifelse,但效率非常低。 如何处理它是常见的方式?
4个回答

3
这个方案可以得出正确答案:
a <- factor(1:3)
b <- c(0,1,2)
a = as.numeric(a)
zero_logical = b != 0         # Precompute because if a contains a zero, this will also be replaced by 5
b[zero_logical] <- a[b[zero_logical]]
b[!zero_logical] <- 5
b
[1] 5 1 2

这个解决方法使用了两个赋值语句,一个用于 b != 0 ,另一个用于 b == 0 ,因此与显式循环相比,这应该非常快。

2

我还没有和Paul的答案进行效率比较,但是考虑到这个(可能)特殊情况:

a <- factor(1:3)
b <- c(0,1,2)
a2 <- as.factor(c("5", a))
a2[b + 1]
# [1] 5 1 2
# Levels: 1 2 3 5

如果你要查找的不是b == 0,那么这种方法很可能行不通。


如果“b”从不为零,那么原始代码是否就能正常工作呢? - Carl Witthoft
@CarlWitthoft,我现在无法测试,但我猜测在b中测试负数也会带来问题。但是这个答案也会失败。? - BenBarnes
是的,考虑到[如何处理负索引,这很可能发生 :-) - Carl Witthoft

2
b[b == 0] <- NA
ifelse(is.na(b), 5, a[b])

0

之前的两个答案都使用了a[_conditions_]而不是_conditions_(a[]),这对你的情况似乎更好;这个答案也有同样的概念 :)

除了其他提到的方法,一种方法是使用类似于a[ifelse(, , b)]而不是ifelse(, , a[b])。我能想到的唯一解决方案是在a的末尾添加一个5,这样就可以使用a[ifelse(b == 0, which(a == 5), b)];当然,在上述调用之前,a必须被重新分配。

为了避免在你工作的环境中更改a并避免分配额外的变量(即a的备份),你可以将所有内容包装在local中;否则,local只是看起来很酷的答案,可以避免:

a <- factor(1:3)
b <- c(0,1,2)

res <- local({  # just to keep all environments clean and to not re-assign `a`
a = as.numeric(as.character(a)) 
a[length(a) + 1] <- 5

a[ifelse(b == 0, which(a == 5), b)]
})
res
#[1] 5 1 2
a
#[1] 1 2 3   #not changed `a`
#Levels: 1 2 3

我想不出更优雅的方式来利用a[ifelse(, , )]。此外,我不确定是否有任何限制。

希望我没有错过什么重要的东西。


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