如何按顺序计算最后的负值?
示例:
200 120 80 7 -12 -20 15 70 85 -12 -19 -43
应该返回
3
因为最后三个值是负数。
189 321 234 -87 -19 -8 -1 10 12 21 9 -23
应该返回
1
和
145 321 213 187 87 78 -23 -43 12 -35 21
应该返回
0
因为最后一个值不是负数。
我知道我可以制作一些可以在第一个非负值上停止的循环,但我不认为这会在计算上有效。有没有更好更简单的方法呢?
答案 0 :(得分:5)
您可以使用rle
:
z <- rnorm(20)
r <- rle(sign(z))
n <- length(r$values)
ifelse(r$values[n] < 1, r$lengths[n], 0)
答案 1 :(得分:5)
这可能会比rle
更快,因为它会在找到正数后立即停止处理数据。我会强调@HongOoi和我的解决方案都假设您的数据不包含任何NA
,这可能就是您的情况:
first.pos <- match(TRUE, rev(x) >= 0)
if (is.na(first.pos)) length(x) else first.pos - 1L
修改:我有点惊讶,但您也可以将first.pos
计算为which(rev(x) >= 0)[1]
,并且在各种输入长度下似乎更快。
基准:
flodel <- function(x) {
first.pos <- which(rev(x) >= 0)[1]
if (is.na(first.pos)) length(x) else first.pos - 1L
}
hong <- function(z) {
r <- rle(sign(z))
n <- length(r$values)
ifelse(r$values[n] < 1, r$lengths[n], 0)
}
alexis <- function(x) sum(Reduce(`==`, ifelse(rev(sign(x)) < 0, 1, NA),
accumulate = T), na.rm = T)
x <- rnorm(1e1)
microbenchmark(flodel(x), hong(x), alexis(x))
# Unit: microseconds
# expr min lq median uq max neval
# flodel(x) 15.079 17.003 19.8910 22.938 1434.925 100
# hong(x) 60.632 68.652 79.7190 108.430 5778.838 100
# alexis(x) 92.711 100.410 117.4125 151.256 2176.288 100
# simon(x) 47.158 56.782 64.3205 86.616 791.728 100
x <- rnorm(1e4)
# Unit: microseconds
# expr min lq median uq max neval
# flodel(x) 207.877 230.013 261.6110 309.2485 3619.233 100
# hong(x) 893.420 972.497 1047.8840 2135.0650 41202.528 100
# alexis(x) 25922.325 28983.209 31241.9405 34402.9145 75246.148 100
# simon(x) 465.798 518.249 548.7245 646.5670 3048.535 100
再一次编辑。关于处理NA
已经有很多讨论,所以这里是一个非必要优化但强大的方法,IMHO遵循R函数通常如何处理NA
:
foo <- function(x, na.rm = FALSE) {
x.rev <- rev(x)
first.pos <- match(TRUE, x.rev >= 0)
first.neg <- if (is.na(first.pos)) x.rev else head(x.rev, first.pos - 1L)
sum(first.neg < 0, na.rm = na.rm)
}
foo(c())
# [1] 0
foo(1:3)
# [1] 0
foo(c(1, -1, NA, -1, NA, -1))
# [1] NA
foo(c(1, -1, NA, -1, NA, -1), na.rm = TRUE)
# [1] 3
答案 2 :(得分:1)
此外,这似乎有效:
sum(Reduce(`==`, ifelse(rev(sign(x)) < 0, 1, NA), accumulate = T), na.rm = T)
E.g:
a <- c(1:4, -5:-2)
b <- c(1:2, -5:-4, 1:2, -1)
d <- c(1:2, -5:-4, 1:2)
e <- c(1:2, -5:-4, NA, 1:2, NA, 2, -1:-3)
lapply(list(a = a, b = b, d = d, e = e),
function(x) sum(Reduce(`==`, ifelse(rev(sign(x)) < 0, 1, NA), accumulate = T),
na.rm = T))
$a
[1] 4
$b
[1] 1
$d
[1] 0
$e
[1] 3
@ flodel评论后编辑
f <- c(1:2, -5:-4, NA, 1:2, NA, 2, -1, NA, -2, NA, -2)
sum(Reduce(`==`, ifelse(rev(sign(f)) < 0, 1, NA), accumulate = T), na.rm = T)
#[1] 1
答案 3 :(得分:1)
此解决方案忽略NA
,这可能是也可能不是......
simon <- function(x) {
y <- na.omit( rev( sign( x ) ) == -1)
return( sum( head( y , which.min( y ) ) ) )
}
使用以下输入数据
x3 <- c( 200 ,120 ,80, 7 ,-12, NA ,15 ,70, 85, -23 , NA , -12, -19 )
x2 <- c( 200 ,120 ,80, 7 ,-12, NA ,15 ,70, 85, -23 , 10 , -12, -19 )
x1 <- c( 200 ,120 ,80, 7 ,-12, NA ,15 ,70, 85, -23 , 10 , -12, NA )
x0 <- c( 200 ,120 ,80, 7 ,-12, NA ,15 ,70, 85, -23 , 10 , -12, 10 )
simon(x3)
#[1] 3
simon(x2)
#[1] 2
simon(x1)
#[1] 1
simon(x0)
#[1] 0