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 так же быстро, как мой первоначальный выбор.