【问题标题】:Count the number of negative values at the end of a vector计算向量末尾的负值个数
【发布时间】:2013-10-26 14:02:48
【问题描述】:

如何按顺序计算最后一个负值?

例子:

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

因为最后一个值不是负数。

我知道我可以做一些循环,在第一个非负值处停止,但我认为这在计算上不会有效。有没有更好更简单的方法呢?

【问题讨论】:

  • 假设您的数据不包含NAs 是否安全?

标签: r


【解决方案1】:

你可以使用rle:

z <- rnorm(20)
r <- rle(sign(z))
n <- length(r$values)
ifelse(r$values[n] < 1, r$lengths[n], 0)

【讨论】:

  • +1 我稍有不同,依赖which.max...run &lt;- rle( rev( sign( x ) == -1 ) ) ; run$lengths[ which.max( run$values == TRUE ) ],这当然是错误的。
【解决方案2】:

这可能会比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) &gt;= 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

再修改一次。关于处理NAs 的讨论很多,所以这里有一个非必要优化但稳健的方法,恕我直言,它遵循 R 函数通常如何处理NAs:

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

【讨论】:

  • 嗯,如果数据确实包含 NA,则将其视为负数。想要吗?
  • @SimonO101:我有一个关于NAs 的免责声明。而且我认为即使您的答案也没有给出NAs 的正确结果,请参阅我对亚历克西斯的最后评论。不过 +1。
  • 我在我的示例中明确说明了如何处理 NA。它们被忽略,即不计入总数(如您的示例中),但不要破坏模式。如果遇到 NA,我可能应该发出警告。
  • 好的,也许“正确”的结果有点主观。我们只是说,本着 R 如何处理NA 的精神,默认行为(在可选的na.rm = FALSE 下)应该是我所描述的。您实现的将是非默认na.rm = TRUE 下的行为。我添加了另一个函数来实现这个想法。
【解决方案3】:

另外,这似乎有效:

sum(Reduce(`==`, ifelse(rev(sign(x)) < 0, 1, NA), accumulate = T), na.rm = T)

例如:

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

【讨论】:

  • 在您的示例中抛出 NAs 并不是很有用,除非您将它们放在向量末尾的一系列负数的中间。
  • 现在你添加了一个这样的例子,你不同意答案应该是NA吗?因为根据NA 背后的真实价值(正面或负面),最终答案可能是 1、3 或 5。
  • @flodel:对不起,我不太明白你在说什么。 NA 的实际价值有什么影响?你的意思是打印出来的NA 应该是一种警告?
  • 是的,返回 NA 就像是在说“我不能确定”,这就是您最后一个示例应该返回的内容。与运行 cummax(c(1, 2, NA, 4)) 时的想法相同。
【解决方案4】:

此解决方案忽略 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

【讨论】:

    猜你喜欢
    • 2012-05-26
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 2014-11-12
    • 1970-01-01
    相关资源
    最近更新 更多