Вот несколько примеров векторов для воспроизведения:
a <- c(14,26,38,64,96,127,152,152,152,152,152,152)
b <- c(4,7,9,13,13,13,13,13,13,13,13,13,13,13)
c <- c(62,297,297,297,297,297,297,297,297,297,297,297)
Очевидно, что в какой-то момент определенное значение повторяется до конца. Мне нужно получить именно тот индекс, в котором эти значения появляются впервые.
Таким образом, в этом случае результат будет 7,4,2
, поскольку в a
152
начинается с 7-й позиции, в b
13
начинается с 4-й позиции, а в c
297
начинается со 2-й позиции.
Надеюсь, это ясно.
Кто-нибудь подскажет, как получить это автоматически?
Обновлено: данные всегда увеличиваются, и как только они начинают повторяться, они продолжаются до конца. В этом виде анализа всегда будет повторение, по крайней мере, двух последних значений.
Что вы возвращаете, когда нет повторения? Пример: x <- 1:10
, какой результат?
Извините за поздние ответы. Да, данные всегда увеличиваются, и как только они начинают повторяться, это продолжается до конца. В этом виде анализа всегда будет повторение, по крайней мере, двух последних значений.
Судя по вашим разъяснениям, вы можете использовать which.max()
- например. sapply(list(a, b, c), which.max)
дает 7 4 2
.
@lotus опубликуйте ответ, пожалуйста, это самый простой ответ.
Вы можете использовать rle(), чтобы взять кодировку длины серии каждого значения, кроме последнего, и суммировать их длины:
get_index <- \(x) sum(head(rle(x)$lengths, -1)) + 1
sapply(list(a, b, c), get_index)
# [1] 7 4 2
Если ваши векторы очень длинные и последнее значение повторяется только ближе к концу, вам не нужно проверять длину каждого прогона, поэтому приведенное выше будет неэффективно. Лучше начать с конца вектора и работать в обратном направлении, пока не найдете другое значение:
Rcpp::cppFunction('
int get_index2(NumericVector x) {
int n = x.size();
double last_value = x[n - 1];
for (int i = n - 2; i >= 0; --i) {
if (x[i] != last_value) {
return i + 2; // +1 as it is next element; +1 for 1-indexing
}
}
return 1; // all elements are the same
}
')
sapply(list(a,b,c), get_index2)
# [1] 7 4 2
data.table
решениеУчитывая ваше обновление вопроса, другой способ подойти к этому:
sapply(list(a,b,c), data.table::uniqueN)
# [1] 7 4 2
Концептуально это не отличается от хорошего ответа от zx8754, и с векторами такого размера вряд ли будет существенно отличаться по скорости и даже может быть медленнее. Однако для очень больших векторов это происходит на быстрее.
спасибо большое, именно то, что я искал!
Поскольку данные всегда увеличиваются и, как только они начинают повторяться, они продолжаются до конца, вы можете просто сделать:
min(which(diff(a)==0))
#[1] 7
sapply(list(a, b, c), \(x) min(which(diff(x)==0)))
[1] 7 4 2
Если последнее условие смягчено, вы можете перевернуть вектор и использовать diff
, чтобы найти первое вхождение ненулевого числа.
length(a) - min(which(diff(rev(a))!=0)) + 1
# [1] 7
x <- c(1,2,2,3,4,5,5,5,5,5,5)
length(x) - min(which(diff(rev(x))!=0)) + 1
#[1] 6
Это тоже работает, спасибо!
Если вы знаете, что последнее значение является повторяющимся значением, вы можете использовать его и match()
, который находит индекс первого значения совпадения:
first <- \(x) match(x[length(x)], x)
sapply(list(a, b, c), first)
# 7 4 2
Если вы ищете первое последовательное значение, вы можете использовать diff()
и which()
:
first_conseq <- \(x) which(diff(x) == 0)[1]
sapply(list(a, b, c), first_conseq)
# 7 4 2
По умолчанию diff()
возвращает разницу между последовательными значениями. Если два значения одинаковы, то их разница будет равна 0. which()
вернет индекс всех значений TRUE
в логическом векторе, поэтому мы используем [1]
для первого случая.
Спасибо @LMc за дополнительные предложения.
Еще одно базовое решение R:
f <- \(x) (length(x) - which.max(rev(x) != x[length(x)]) + 1L)%%length(x) + 1L
Я сравню его с другими вариантами, а также проведу некоторые сравнительные тесты. Подбрасывание в паре крайних случаев:
a <- c(14,26,38,64,96,127,152,152,152,152,152,152)
b <- c(4,7,9,13,13,13,13,13,13,13,13,13,13,13)
c <- c(62,297,297,297,297,297,297,297,297,297,297,297)
d <- numeric(12)
e <- 1:14
Тестирование предлагаемых ответов, включая крайние случаи:
get_index <- \(x) sum(head(rle(x)$lengths, -1)) + 1L
Edward <- \(a) length(a) - min(which(diff(rev(a))!=0)) + 1L
first_conseq <- \(x) which(diff(x) == 0)[1]
sapply(list(a, b, c, d, e), f)
#> [1] 7 4 2 1 14
sapply(list(a, b, c, d, e), get_index)
#> [1] 7 4 2 1 14
sapply(list(a, b, c, d, e), Edward)
#> Warning in min(which(diff(rev(a)) != 0)): no non-missing arguments to min;
#> returning Inf
#> [1] 7 4 2 -Inf 14
sapply(list(a, b, c, d, e), first_conseq)
#> [1] 7 4 2 1 NA
И функция Rcpp SamR (слегка измененная для скорости):
Rcpp::cppFunction('
int get_index2(const NumericVector& x) {
const int n = x.size();
const double last_value = x[n - 1];
for (int i = n - 2; i >= 0; --i) {
if (x[i] != last_value) {
return i + 2; // +1 as it is next element; +1 for 1-indexing
}
}
return 1; // all elements are the same
}
')
sapply(list(a, b, c, d, e), get_index2)
#> [1] 7 4 2 1 14
Только функции f
и get_index
хорошо себя ведут в крайних случаях.
Бенчмаркинг с большим набором данных:
n <- sample(1e5, 1e3, 1)
x <- lapply(n, \(n) c(sample(1e4, n, 1), 0L, sample(1e5 - n, 1))[-1:-2])
identical(n, vapply(x, f, 0L))
#> [1] TRUE
bench::mark(
f = vapply(x, f, 0L),
get_index = vapply(x, get_index, 0L)
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 f 306.6ms 316.29ms 3.16 580.07MB 12.6
#> 2 get_index 2.46s 2.46s 0.406 4.91GB 13.8
#> 3 get_index2 62.4ms 67.14ms 14.6 404.34MB 42.0
Как пояснил ОП, если данные всегда увеличиваются и начинают дублироваться с последним значением, нам просто нужно проверить уникальную длину:
lengths(lapply(list(a, b, c), unique))
# [1] 7 4 2
Спасибо @zx8754, это самое простое и простое решение!
Еще одно решение на базе R. Применение duplicated
дает логический массив с первым значением TRUE
по целевому индексу плюс 1, which
извлекает индекс. Я добавил «крайние» случаи, рассмотренные @jblood94 выше. Хотя эти случаи не включены в вопрос OP, похоже, что функция повторов не должна возвращать NA.
a <- c(14, 26, 38, 64, 96, 127, 152, 152, 152, 152, 152, 152)
b <- c(4, 7, 9, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13)
c <- c(62, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297, 297)
d <- 12
e <- 1:14
pull_index <- \(x) which(duplicated(x))[1] - 1
sapply(list(a, b, c, d, e), pull_index)
#
# [1] 7 4 2 NA NA
Можешь попробовать
f <- \(x) {
length(x) - which.min(replace(rev(duplicated(x, fromLast = TRUE)), 1, TRUE)) + 2
}
такой, что
> lapply(list(a, b, c), f)
[[1]]
[1] 7
[[2]]
[1] 4
[[3]]
[1] 2
Чтобы уточнить, ваши данные всегда увеличиваются? Кроме того, как только он начинает повторяться, он повторяется до последнего элемента вектора?