查找R中向量元素的第一个变化的索引

时间:2014-01-03 03:52:21

标签: r

我有一个向量v,我想找到R中向量元素的第一个变化的索引。我该怎么做?例如

    v = c(1, 1, 1, 1, 1, 1, 1, 1.5, 1.5, 2, 2, 2, 2, 2)

5 个答案:

答案 0 :(得分:17)

rle是一个好主意,但如果您只想要变更点的索引,您可以这样做:

c(1,1+which(diff(v)!=0))
## 1 8 10

答案 1 :(得分:6)

您正在寻找rle

rle(v)
## Run Length Encoding
##   lengths: int [1:3] 7 2 5
##   values : num [1:3] 1 1.5 2

这表示值在7 + 1,7 + 2 + 1(和7 + 2 + 5 + 1)的位置变化是元素“一端到底”的索引。

答案 2 :(得分:6)

内部data.table包(意思是未导出尚未)使用函数uniqlist(在dev 1.8.11中)或duplist(在当前1.8中) .10 @CRAN)完全符合您的要求。它应该很快。这是一个基准:

require(data.table)
set.seed(45)
# prepare a huge vector (sorted)
x <- sort(as.numeric(sample(1e5, 1e7, TRUE)))

require(microbenchmark)
ben <- function(v) c(1,1+which(diff(v)!=0))
matthew <- function(v) rle(v)
matteo <- function(v) firstDiff(v)
exegetic <- function(v) first.changes(v)
# if you use 1.8.10, replace uniqlist with duplist
dt <- function(v) data.table:::uniqlist(list(v))

microbenchmark( ans1 <- ben(x), matthew(x), matteo(x), 
                exegetic(x), ans2 <- dt(x), times=10)

# Unit: milliseconds
#            expr       min         lq     median         uq        max neval
#  ans1 <- ben(x)  1181.808  1229.8197  1313.2646  1357.5026  1553.9296    10
#      matthew(x)  1456.918  1496.0300  1581.0062  1660.4067  2148.1691    10
#       matteo(x) 28609.890 29305.1117 30698.5843 32706.3147 34290.9864    10
#     exegetic(x)  1365.243  1546.0652  1576.8699  1659.5488  1886.6058    10
#   ans2 <- dt(x)   113.712   114.7794   143.9938   180.3743   221.8386    10

identical(as.integer(ans1), ans2) # [1] TRUE

我对Rcpp并不熟悉,但似乎解决方案可能会有所改进。

编辑:请参阅Matteo针对Rcpp计时的更新答案。

答案 3 :(得分:3)

如果您需要快速操作,可以使用Rcpp包从R调用C ++:

library(Rcpp)
library(data.table)
library(microbenchmark)

# Rcpp solution
cppFunction('
NumericVector firstDiff(NumericVector & vett)
{
  const int N = vett.size();

  std::list<double> changes;
  changes.push_back(1.0);

  NumericVector::iterator iterH = vett.begin() + 1;
  NumericVector::iterator iterB = vett.begin();

  int count = 2;
  for(iterH = vett.begin() + 1; iterH != vett.end(); iterH++, iterB++)
  {
    if(*iterH != *iterB) changes.push_back(count);
    count++;
  }

  return wrap(changes);
 }
')

# Data table
dt <- function(input) data.table:::uniqlist(list(input))

# Comparison
set.seed(543)
x <- sort(as.numeric(sample(1e5, 1e7, TRUE)))
microbenchmark(ans1 <- firstDiff(x), which(diff(x) != 0)[1], rle(x),
               ans2 <- dt(x), times = 10) 

Unit: milliseconds
                   expr       min        lq    median        uq       max neval
    ans1 <- firstDiff(x)  50.10679  50.12327  50.14164  50.16343  50.28475    10
  which(diff(x) != 0)[1] 545.66478 547.58388 556.15550 561.78275 617.40281    10
                  rle(x) 664.53262 687.04316 709.84949 714.91528 721.37204    10
                   dt(x)  60.60317  82.30181  82.70207  86.13330  94.07739    10

identical(as.integer(ans1), ans2)
#[1] TRUE

Rcpp比data.table略快,并且比本例中的其他替代方案快得多。

答案 4 :(得分:3)

> v <- c(1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 5, 6, 6, 6, 6)

first.changes <- function(d) {
  p <- cumsum(rle(d)$lengths) + 1
  p[-length(p)]
}

> first.changes(v)
[1]  4  6 11 12 14

或者,使用您的数据

> v = c(1, 1, 1, 1, 1, 1, 1, 1.5, 1.5, 2, 2, 2, 2, 2)

> first.changes(v)
[1]  8 10