检查向量是否按升序/降序排列

21
我想知道向量中的数字是升序/不变还是降序。所以对于vector1vector2,它应该是TRUE,而对于vector3,它应该是FALSE。简单地说,如果向量中有翻转,则应返回FALSE
vector1 = c(2, 2, 2, 2, 2, 2)
vector2 = c(2, 2, 3, 3, 3, 3)
vector3 = c(2, 2, 1, 2, 2, 2)

有没有一种不用编写循环就能快速完成此操作的方法?

2个回答

35

有一个名为is.unsorted的基本R函数非常适合这种情况:

!is.unsorted(vector1)
# [1] TRUE
!is.unsorted(vector2)
# [1] TRUE
!is.unsorted(vector3)
# [1] FALSE

这个函数速度非常快,因为它几乎直接调用了编译好的 C 代码。

我的最初想法是使用 sortidentical,类似于 identical(sort(vector1), vector1),但这样做相当慢;不过,我认为这种方法可以扩展到更灵活的情况。

如果速度真的很重要,我们可以跳过一些 is.unsorted 的开销,并直接调用内部函数:

.Internal(is.unsorted(vector1, FALSE))

(FALSE会将FALSE传递给strictly参数)。这在小向量上可以提供约4倍的加速。

为了感受最终选项有多快,这里有一个基准测试:

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   

因此,直接调用内部函数(警告:您需要确保您的向量完全干净--没有NA等)可以使您的速度比基本的R函数快大约2倍,而这个函数又比使用diff 快了约30倍,而这个函数又比我的最初选择快了约2倍。


真的很酷的函数(+1)!我有点担心这不是根据问题中的句子“简单地说,如果向量中存在反转,则应返回FALSE”所要求的,我将其解释为方向的反转(例如增加然后减少,或减少然后增加)。如果OP只是检查向量是否非递减,那么我同意这是一个很好的内置函数,应该比我的更有效率(特别是如果向量在相对早期就开始减少)。 - josliber
1
@josilber 我同意他的例子似乎没有涵盖他可能考虑的所有情况。无论如何,调整很简单:不要使用 is.unsorted(x),而是使用 is.unsorted(-x)。同时检查两者的方法是 !is.unsorted(x)|!is.unsorted(-x) - MichaelChirico
是的,那些应该处理另外两种情况。请注意,任何涉及 is.unsorted(-x) 的操作都会比 is.unsorted(x) 慢(在我的基准测试中慢5倍至10倍),因为它需要迭代向量并否定所有元素。 - josliber
@josilber 是的,基本上消除了内部基础差距,以添加单调递减情况。尽管如此仍然非常快。 - MichaelChirico

27

您可以使用diff来计算元素之间的差异,并使用all检查它们是否全为非负数:

你可以使用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

如何判断趋势是否下降? - Abdul Basit Khan
2
@AbdulBasitKhan 上述代码检查所有向量是否为非递减的,你可以将 >= 0 替换为 <= 0 来检查它们是否为非递增的。 - josliber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接