У меня есть вектор v
и я хотел бы найти индекс первых изменений в элементах вектора в R. Как я могу это сделать? Например:
v = c(1, 1, 1, 1, 1, 1, 1, 1.5, 1.5, 2, 2, 2, 2, 2)
У меня есть вектор v
и я хотел бы найти индекс первых изменений в элементах вектора в R. Как я могу это сделать? Например:
v = c(1, 1, 1, 1, 1, 1, 1, 1.5, 1.5, 2, 2, 2, 2, 2)
rle
- хорошая идея, но если вы хотите только индексы точек изменения, которые вы можете просто сделать:
c(1,1+which(diff(v)!=0))
## 1 8 10
Вы ищете 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 будет индексом элемента "один от конца" )
Внутренний пакет 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.
Если вам нужна операция быстро, вы можете использовать пакет Rcpp для вызова С++ из R:
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 и намного быстрее, чем другие альтернативы в этом примере.
> 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