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

ros*_*ose 12 r

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

v = c(1, 1, 1, 1, 1, 1, 1, 1.5, 1.5, 2, 2, 2, 2, 2)
Run Code Online (Sandbox Code Playgroud)

Ben*_*ker 18

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

c(1,1+which(diff(v)!=0))
## 1 8 10
Run Code Online (Sandbox Code Playgroud)


Mat*_*erg 7

您正在寻找rle:

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

这表示值在位置7 + 1,7 + 2 + 1(和7 + 2 + 5 + 1)的变化将是元素"一个接一个"的索引)


Aru*_*run 6

data.table封装内部(指没有出口尚未)使用的功能uniqlist(在开发中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
Run Code Online (Sandbox Code Playgroud)

我对Rcpp并不熟悉,但似乎解决方案可以改进很多.

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


Mat*_*olo 5

如果您需要快速操作,可以使用 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
Run Code Online (Sandbox Code Playgroud)

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

  • 我非常确定这是由于“list(input)”期间的复制所致。如果你这样做:`k = list(input)`,然后时间`uniqlist(k)`我猜它会是一样的。从 R v3.1 开始 `list(.)` 将不会进行复制。所以,这是一个非常受欢迎的变化! (2认同)