R: как проверить, является ли вектор восходящим/нисходящим
vector1 = c(2, 2, 2, 2, 2, 2)
vector2 = c(2, 2, 3, 3, 3, 3)
vector3 = c(2, 2, 1, 2, 2, 2)
Я хочу знать, являются ли числа в векторе восходящими/остаются одинаковыми или нисходящими. Поэтому для vector1
и vector2
это должно быть TRUE
, тогда как для vector3
оно должно быть FALSE
. Проще говоря, он должен возвращать FALSE
, если в векторе есть реверсия. Есть ли быстрый способ сделать это без написания цикла?
Ответы
Ответ 1
Вы можете diff
вычислить различия между элементами и all
, чтобы проверить, все ли они неотрицательны:
all(diff(vector1) >= 0)
# [1] TRUE
all(diff(vector2) >= 0)
# [1] TRUE
all(diff(vector3) >= 0)
# [1] FALSE
Вышеприведенный код проверяет, не все ли все векторы, и вы можете заменить >= 0
на <= 0
, чтобы проверить, не увеличиваются ли они. Если вместо этого ваша цель - идентифицировать векторы, которые либо не уменьшаются, либо не увеличиваются (например, они не имеют возрастающего и уменьшающегося шага в одном и том же векторе), есть простая модификация:
!all(c(-1, 1) %in% sign(diff(vector1)))
# [1] TRUE
!all(c(-1, 1) %in% sign(diff(vector2)))
# [1] TRUE
!all(c(-1, 1) %in% sign(diff(vector3)))
# [1] FALSE
Ответ 2
Существует базовая функция R
, называемая is.unsorted
, которая идеально подходит для этой ситуации:
!is.unsorted(vector1)
# [1] TRUE
!is.unsorted(vector2)
# [1] TRUE
!is.unsorted(vector3)
# [1] FALSE
Эта функция выполняется очень быстро, так как она обращается почти непосредственно к скомпилированному C
коду.
Моя первоначальная мысль заключалась в использовании sort
и identical
, a la identical(sort(vector1), vector1)
, но это довольно медленно; что, я думаю, этот подход может быть расширен до более гибких ситуаций.
Если скорость действительно важна, мы можем пропустить некоторые из служебных данных is.unsorted
и вызвать внутреннюю функцию напрямую:
.Internal(is.unsorted(vector1, FALSE))
(FALSE
проходит FALSE
к аргументу strictly
). Это предложило ускорение ~ 4x на маленьком векторе.
Чтобы понять, насколько быстро конечная опция, вот эталон:
library(microbenchmark)
set.seed(10101)
srtd <- sort(sample(1e6, rep = TRUE)) # a sorted test case
unsr <- sample(1e6, rep = TRUE) #an unsorted test case
microbenchmark(times = 1000L,
josilber = {all(diff(srtd) >= 0)
all(diff(unsr) >= 0)},
mikec = {identical(sort(srtd), srtd)
identical(sort(unsr), unsr)},
baser = {!is.unsorted(srtd)
!is.unsorted(unsr)},
intern = {!.Internal(is.unsorted(srtd, FALSE))
!.Internal(is.unsorted(unsr, FALSE))})
Результаты на моей машине:
# Unit: microseconds
# expr min lq mean median uq max neval cld
# josilber 30349.108 30737.6440 34550.6599 34113.5970 34964.171 155283.320 1000 c
# mikec 93167.836 94183.8865 97119.4493 94852.7530 97528.859 229692.328 1000 d
# baser 1089.670 1168.7400 1322.9341 1296.7375 1347.946 6301.866 1000 b
# intern 514.816 532.4405 576.2867 560.5955 566.236 2456.237 1000 a
Таким образом, вызов внутренней функции напрямую (предостережение: вы должны быть уверены, что ваш вектор абсолютно чистый - no NA
s и т.д.) дает вам ~ 2x скорость по сравнению с базовой функцией R
, которая в свою очередь ~ 30 раз быстрее, чем при использовании diff
, что в свою очередь ~ 2x так же быстро, как мой первоначальный выбор.