寻找局部最大值和最小值

Posted

技术标签:

【中文标题】寻找局部最大值和最小值【英文标题】:Finding local maxima and minima 【发布时间】:2021-09-12 01:20:11 【问题描述】:

我正在寻找一种计算有效的方法来查找 R 中大量数字的局部最大值/最小值。 希望没有for 循环......

例如,如果我有一个像1 2 3 2 1 1 2 1 这样的数据文件,我希望函数返回 3 和 7,它们是局部最大值的位置。

【问题讨论】:

【参考方案1】:

diff(diff(x))(或diff(x,differences=2):感谢@ZheyuanLi)本质上计算二阶导数的离散模拟,因此在局部最大值处应该是负数。下面的+1 处理了diff 的结果比输入向量短这一事实。

编辑:添加了@Tommy 对 delta-x 不是 1 的情况的更正...

tt <- c(1,2,3,2,1, 1, 2, 1)
which(diff(sign(diff(tt)))==-2)+1

我上面的建议 (http://statweb.stanford.edu/~tibs/PPC/Rdist/) 适用于数据噪声较大的情况。

【讨论】:

你比我少了几秒钟 - 并且有更好的解决方案 :) 但如果值不总是改变一个,它应该是 which(diff(sign(diff(x)))==-2)+1 正如 Tommy 所指出的,当输入列表按递增顺序排列时,Ben 的解决方案也不起作用。例如tt 链接...ppc.peaks.html不起作用,请改用statweb.stanford.edu/~tibs/PPC/Rdist。【参考方案2】:

@Ben 的解决方案非常棒。但它不处理以下情况:

# all these return numeric(0):
x <- c(1,2,9,9,2,1,1,5,5,1) # duplicated points at maxima 
which(diff(sign(diff(x)))==-2)+1 
x <- c(2,2,9,9,2,1,1,5,5,1) # duplicated points at start
which(diff(sign(diff(x)))==-2)+1 
x <- c(3,2,9,9,2,1,1,5,5,1) # start is maxima
which(diff(sign(diff(x)))==-2)+1

这是一个更健壮(但更慢、更丑)的版本:

localMaxima <- function(x) 
  # Use -Inf instead if x is numeric (non-integer)
  y <- diff(c(-.Machine$integer.max, x)) > 0L
  rle(y)$lengths
  y <- cumsum(rle(y)$lengths)
  y <- y[seq.int(1L, length(y), 2L)]
  if (x[[1]] == x[[2]]) 
    y <- y[-1]
  
  y


x <- c(1,2,9,9,2,1,1,5,5,1)
localMaxima(x) # 3, 8
x <- c(2,2,9,9,2,1,1,5,5,1)
localMaxima(x) # 3, 8
x <- c(3,2,9,9,2,1,1,5,5,1)
localMaxima(x) # 1, 3, 8

【讨论】:

感谢我尝试了这段代码,它可以工作!如何在不更改输入的情况下为局部最小值修改它? 嗨,Tommy,我想在一个包中使用您的 localMinima 功能,请您联系我以便我能够正确地确认您吗? @VahidMir 基本上这个函数是一种(聪明的!)方法来获取向量的一阶导数从正到负切换的位置。因此,局部最小值将在它从负数变为正数的地方给出:只需将第一行替换为y &lt;- diff(c(.Machine$integer.max, x)) &lt; 0L(这样可以保留检测初始最小值的可能性) 很好,但是 localMaxima() 会在拐点 localMaxima(c(1, 2, 2, 3, 2, 1)) 上触发错误,返回 2 4 而不仅仅是 4 为什么 rle(y)$lengths 被调用了两次?我的意思是我了解y &lt;- cumsum(rle(y)$lengths),但不了解前面的独立rle(y)$lengths【参考方案3】:

使用动物园库函数rollapply:

x <- c(1, 2, 3, 2, 1, 1, 2, 1)
library(zoo)
 xz <- as.zoo(x)
 rollapply(xz, 3, function(x) which.min(x)==2)
#    2     3     4     5     6     7 
#FALSE FALSE FALSE  TRUE FALSE FALSE 
 rollapply(xz, 3, function(x) which.max(x)==2)
#    2     3     4     5     6     7 
#FALSE  TRUE FALSE FALSE FALSE  TRUE 

然后使用“coredata”为那些“which.max”是表示局部最大值的“中心值”的值提取索引。您显然可以使用 which.min 而不是 which.max 对局部最小值执行相同的操作。

 rxz <- rollapply(xz, 3, function(x) which.max(x)==2)
 index(rxz)[coredata(rxz)]
#[1] 3 7

我假设您不需要起始值或结束值,但如果您这样做,您可以在处理之前填充向量的末端,就像染色体上的端粒一样。

(我注意到 ppc 包(“Peak Probability Contrasts”)用于进行质谱分析,只是因为在阅读上面@BenBolker 的评论之前我不知道它的可用性,我认为添加这几个词会增加对质谱感兴趣的人会在搜索中看到这个。)

【讨论】:

这比其他的有一个非常显着的优势。通过将间隔增加到大于 3 的值,我们可以忽略一个点恰好比它的两个最近邻点略高的情况,即使附近还有其他点更大。这对于具有小的随机变化的测量数据很有用。 感谢您的评论,感谢 dbaupp,感谢@GGrothendieck 和 Achim Zeileis 将zoo 写得如此干净,以至于我能够干净地应用它。 这是一个绝妙的解决方案,但请注意:明确定义 align 参数是个好主意。 zoo:::rollapply.zoo 默认使用align = "center",但xts:::rollapply.xts 使用align = "right" @dleal,你在数组xz 上滚动一个宽度为 3 的窗口。这个窗口的内容是返回最大值索引的函数的参数x。如果这个索引指向窗口的中心,那么你就停留在局部最大值上!在这种特殊情况中,窗口宽度为 3,因此中间元素的索引为 2。基本上,您正在为宽度等于 2*m–1 的窗口寻找条件 which.max(x) == m 虽然有趣的是,@42- 的建议在重复值多于宽度(例如 3)的情况下会失败。换句话说,鞍点会被误认为是一个极端。一个简单的例子是:x &lt;- c(3, 2, 2, 2, 2, 1, 3),然后是rx &lt;- rollapply(as.zoo(x), 3, function(x) which.min(x)==2),然后index(rx)[coredata(rx)] 错误地给出[1] 2 6(应该是[1] 6)。【参考方案4】:

提供了一些不错的解决方案,但这取决于您的需要。

只需 diff(tt) 返回差异。

您想检测何时从递增值变为递减值。 @Ben 提供了一种方法:

 diff(sign(diff(tt)))==-2

这里的问题是,这只会检测立即从严格递增到严格递减的变化。

轻微的变化将允许在峰值处重复值(返回 TRUE 以表示最后一次出现的峰值):

 diff(diff(x)>=0)<0

然后,如果您想在开头或结尾检测最大值,您只需正确填充前后

以下是包含在函数中的所有内容(包括寻找山谷):

 which.peaks <- function(x,partial=TRUE,decreasing=FALSE)
     if (decreasing)
         if (partial)
             which(diff(c(FALSE,diff(x)>0,TRUE))>0)
         else 
             which(diff(diff(x)>0)>0)+1
         
     else 
         if (partial)
             which(diff(c(TRUE,diff(x)>=0,FALSE))<0)
         else 
             which(diff(diff(x)>=0)<0)+1
         
     
 

【讨论】:

粮农组织未来的访客,我在这里尝试了一些建议的解决方案,这对我来说效果最好。【参考方案5】:

我在其他地方发布了这个,但我认为这是一种有趣的方式。我不确定它的计算效率如何,但它是一种非常简洁的解决问题的方法。

vals=rbinom(1000,20,0.5)

text=paste0(substr(format(diff(vals),scientific=TRUE),1,1),collapse="")

sort(na.omit(c(gregexpr('[ ]-',text)[[1]]+1,ifelse(grepl('^-',text),1,NA),
 ifelse(grepl('[^-]$',text),length(vals),NA))))

【讨论】:

简洁但经过混淆通常没有那么有用,除非它是一个需要极高效率的大公司,或者你需要尽可能不清楚的竞赛:) 愿意解释代码或主要要点吗?只是把这个奇怪的(而且不可读 - 为什么不添加一些间距?)代码扔给我们不会导致人们尝试使用它 这比其他方法效率低,但仍然有效。我认为这将属于需要不清楚的竞争。一般的想法是您将代码中的差异转换为文本并获取它的第一个字符,如果它是正数则为空格,如果为负数则为-。如果您看到 - - 模式(或任一端点处的空格),您已找到最大值。我在 Linux 上试过这个,我使用 substr(...,2,2) 而不是 substr(...,1,1),因为文本有一个前导空格。正则表达式对于这个问题并不理想,但它是一个有趣的解决方案。【参考方案6】:

这是最小值的解决方案

@Ben 的解决方案

x <- c(1,2,3,2,1,2,1)
which(diff(sign(diff(x)))==+2)+1 # 5

请注意汤米帖子中的案例!

@Tommy 的解决方案:

localMinima <- function(x) 
  # Use -Inf instead if x is numeric (non-integer)
  y <- diff(c(.Machine$integer.max, x)) > 0L
  rle(y)$lengths
  y <- cumsum(rle(y)$lengths)
  y <- y[seq.int(1L, length(y), 2L)]
  if (x[[1]] == x[[2]]) 
    y <- y[-1]
  
  y


x <- c(1,2,9,9,2,1,1,5,5,1)
localMinima(x) # 1, 7, 10
x <- c(2,2,9,9,2,1,1,5,5,1)
localMinima(x) # 7, 10
x <- c(3,2,9,9,2,1,1,5,5,1)
localMinima(x) # 2, 7, 10

请注意:localMaximalocalMinima 都不能在开始时处理重复的最大值/最小值!

【讨论】:

不确定您的答案真正带来了什么,因为其他答案包括相同的算法。 是的,但它给出了最小值的解决方案,如最初要求的那样。另外,还没有提到开始时重复最大值/最小值的情况。 好吧......即使答案基本相同,我也无法反驳。所以我不会投反对票,但也不会投赞成票。您应该尝试回答尚未回答的问题(即使这个问题没有正式回答,前 2 个答案有 18 票和 20 票这一事实使其相同)。 顺便说一句,我可能需要帮助找到一种方法来适应具有更大间隔的最大值/最小值的函数,因此'delta x > 1'。有人有想法吗? @Sebastian 如果还没有看到,请查看我的答案以获得更大的间隔。【参考方案7】:

在以前的解决方案中让位置工作时遇到了一些麻烦,我想出了一种直接获取最小值和最大值的方法。下面的代码将执行此操作并绘制它,将最小值标记为绿色,最大值标记为红色。与which.max() 函数不同,这会将最小值/最大值的所有索引拉出数据帧。零值被添加到第一个 diff() 函数中,以说明在您使用该函数时出现的结果长度缺失的缩短。将它插入到最里面的diff() 函数调用中可以避免在逻辑表达式之外添加偏移量。没关系,但我觉得这是一种更清洁的方式。

# create example data called stockData
stockData = data.frame(x = 1:30, y=rnorm(30,7))

# get the location of the minima/maxima. note the added zero offsets  
# the location to get the correct indices
min_indexes = which(diff(  sign(diff( c(0,stockData$y)))) == 2)
max_indexes = which(diff(  sign(diff( c(0,stockData$y)))) == -2)

# get the actual values where the minima/maxima are located
min_locs = stockData[min_indexes,]
max_locs = stockData[max_indexes,]

# plot the data and mark minima with red and maxima with green
plot(stockData$y, type="l")
points( min_locs, col="red", pch=19, cex=1  )
points( max_locs, col="green", pch=19, cex=1  )

【讨论】:

几乎非常好 - 最后似乎没有最大值> histData$counts [1] 18000 0 0 0 0 0 0 0 0 0 0 0 [217] 0 0 0 0 0 0 0 0 5992 max_indexes = sign(diff( c(0,histData$counts,0)))) 虽然有效,但我不知道它是否会破坏其他任何东西。 @idontgetoutmuch... 该方法本质上使用数据的一阶导数计算,并且不会在被评估的系列的端点处找到相对最大值或最小值。如果这是一个相对的最大值/最小值,它将适用于该系列的倒数第二个值,因为可以在那里近似导数。如果您正在寻找系列中的最大值,则 max() 函数应该可以正常工作。将它与上面的代码结合起来应该可以得到你需要的 max/mins 信息。 我应该对我上面评论的第一句话更清楚......该方法基本上使用数据的一阶导数近似值,并且不会在端点找到相对最大值或最小值正在评估系列,因为无法知道端点是否是相对的最大/分钟。【参考方案8】:

我今天对此进行了尝试。我知道你说希望没有 for 循环,但我坚持使用 apply 函数。有点紧凑和快速,并允许阈值规范,因此您可以大于 1。

功能:

inflect <- function(x, threshold = 1)
  up   <- sapply(1:threshold, function(n) c(x[-(seq(n))], rep(NA, n)))
  down <-  sapply(-1:-threshold, function(n) c(rep(NA,abs(n)), x[-seq(length(x), length(x) - abs(n) + 1)]))
  a    <- cbind(x,up,down)
  list(minima = which(apply(a, 1, min) == a[,1]), maxima = which(apply(a, 1, max) == a[,1]))

要使用阈值进行可视化/播放,您可以运行以下代码:

# Pick a desired threshold # to plot up to
n <- 2
# Generate Data
randomwalk <- 100 + cumsum(rnorm(50, 0.2, 1)) # climbs upwards most of the time
bottoms <- lapply(1:n, function(x) inflect(randomwalk, threshold = x)$minima)
tops <- lapply(1:n, function(x) inflect(randomwalk, threshold = x)$maxima)
# Color functions
cf.1 <- grDevices::colorRampPalette(c("pink","red"))
cf.2 <- grDevices::colorRampPalette(c("cyan","blue"))
plot(randomwalk, type = 'l', main = "Minima & Maxima\nVariable Thresholds")
for(i in 1:n)
  points(bottoms[[i]], randomwalk[bottoms[[i]]], pch = 16, col = cf.1(n)[i], cex = i/1.5)

for(i in 1:n)
  points(tops[[i]], randomwalk[tops[[i]]], pch = 16, col = cf.2(n)[i], cex = i/1.5)

legend("topleft", legend = c("Minima",1:n,"Maxima",1:n), 
       pch = rep(c(NA, rep(16,n)), 2), col = c(1, cf.1(n),1, cf.2(n)), 
       pt.cex =  c(rep(c(1, c(1:n) / 1.5), 2)), cex = .75, ncol = 2)

【讨论】:

我喜欢 的功能。感谢埃文(y) 很高兴听到这个消息! 我的数据有重复的最大值和最小值,例如c(1,2,3,3,2,1,1,2,3) 这会为每个最大值/最小值生成多个命中。尝试threshold 似乎只会改变绘图上的点大小,但不能解决这个问题。有什么建议吗? 您好,当值绑定时,您到底期望发生什么?该功能将它们视为同一点 - 被忽略。在一座平坦的山顶上,走到哪里都是峰顶,即使中间有一道缝隙。您正在使用哪些具有相同值的数据?顺便说一句,如果相邻的高度不相同,点的变化也会不同。在阈值 = 3 处查看向量 c(0,0,0,1,0.7,3,2,3,3,2,1,1,2,3,0.7, 0.5,0,0,0)【参考方案9】:

@42- 的回答很好,但我有一个用例我不想使用zoo。使用dplyrlaglead 很容易实现这一点:

library(dplyr)
test = data_frame(x = sample(1:10, 20, replace = TRUE))
mutate(test, local.minima = if_else(lag(x) > x & lead(x) > x, TRUE, FALSE)

rollapply 解决方案一样,您可以分别通过lag/lead 参数ndefault 控制窗口大小和边缘情况。

【讨论】:

【参考方案10】:

pracma 包中,使用

tt <- c(1,2,3,2,1, 1, 2, 1)
tt_peaks <- findpeaks(tt, zero = "0", peakpat = NULL,
       minpeakheight = -Inf, minpeakdistance = 1, threshold = 0, npeaks = 0, sortstr = FALSE)

  [,1] [,2] [,3] [,4]
  [1,]  3    3    1    5
  [2,]  2    7    6    8

返回一个有 4 列的矩阵。 第一列显示局部峰值的绝对值。 第二列是索引 第 3 列和第 4 列是峰的起点和终点(可能有重叠)。

详情请见https://www.rdocumentation.org/packages/pracma/versions/1.9.9/topics/findpeaks。

一个警告:我在一系列非整数中使用了它,峰值是一个索引为时已晚(对于所有峰值),我不知道为什么。所以我不得不从我的索引向量中手动删除“1”(没什么大不了的)。

【讨论】:

【参考方案11】:

聚会迟到了,但这可能会引起其他人的兴趣。您现在可以使用ggpmisc 包中的(内部)函数find_peaks。您可以使用thresholdspanstrict 参数对其进行参数化。由于ggpmisc 包旨在与ggplot2 一起使用,您可以使用stat_peaksstat_valleys 函数直接绘制minimamaxima

set.seed(1)
x <- 1:10
y <- runif(10)
# Maxima
x[ggpmisc:::find_peaks(y)]
[1] 4 7
y[ggpmisc:::find_peaks(y)]
[1] 0.9082078 0.9446753
# Minima
x[ggpmisc:::find_peaks(-y)]
[1] 5
y[ggpmisc:::find_peaks(-y)]
[1] 0.2016819    
# Plot
ggplot(data = data.frame(x, y), aes(x = x, y = y)) + geom_line() + stat_peaks(col = "red") + stat_valleys(col = "green")

【讨论】:

【参考方案12】:

找到局部最大值和最小值以获得不太容易的序列,例如1 0 1 1 2 0 1 1 0 1 1 1 0 1 我会在 (1), 5, 7.5, 11 和 (14) 给出他们的位置,最大值和最小值 2, 6, 9, 13。

#Position                1 1 1 1 1
#      1 2 3 4 5 6 7 8 9 0 1 2 3 4
x <- c(1,0,1,1,2,0,1,1,0,1,1,1,0,1) #Frequency
#      p v     p v  p  v   p   v p  p..Peak, v..Valey

peakPosition <- function(x, inclBorders=TRUE) 
  if(inclBorders) y <- c(min(x), x, min(x))
   else y <- c(x[1], x)
  y <- data.frame(x=sign(diff(y)), i=1:(length(y)-1))
  y <- y[y$x!=0,]
  idx <- diff(y$x)<0
  (y$i[c(idx,F)] + y$i[c(F,idx)] - 1)/2


#Find Peaks
peakPosition(x)
#1.0  5.0  7.5 11.0 14.0

#Find Valeys
peakPosition(-x)
#2  6  9 13

peakPosition(c(1,2,3,2,1,1,2,1)) #3 7

【讨论】:

【参考方案13】:

在我正在处理的情况下,重复经常发生。所以我实现了一个函数,可以找到第一个或最后一个极值(最小值或最大值):

locate_xtrem <- function (x, last = FALSE)

  # use rle to deal with duplicates
  x_rle <- rle(x)

  # force the first value to be identified as an extrema
  first_value <- x_rle$values[1] - x_rle$values[2]

  # differentiate the series, keep only the sign, and use 'rle' function to
  # locate increase or decrease concerning multiple successive values.
  # The result values is a series of (only) -1 and 1.
  #
  # ! NOTE: with this method, last value will be considered as an extrema
  diff_sign_rle <- c(first_value, diff(x_rle$values)) %>% sign() %>% rle()

  # this vector will be used to get the initial positions
  diff_idx <- cumsum(diff_sign_rle$lengths)

  # find min and max
  diff_min <- diff_idx[diff_sign_rle$values < 0]
  diff_max <- diff_idx[diff_sign_rle$values > 0]

  # get the min and max indexes in the original series
  x_idx <- cumsum(x_rle$lengths)
  if (last) 
    min <- x_idx[diff_min]
    max <- x_idx[diff_max]
   else 
    min <- x_idx[diff_min] - x_rle$lengths[diff_min] + 1
    max <- x_idx[diff_max] - x_rle$lengths[diff_max] + 1
  
  # just get number of occurences
  min_nb <- x_rle$lengths[diff_min]
  max_nb <- x_rle$lengths[diff_max]

  # format the result as a tibble
  bind_rows(
    tibble(Idx = min, Values = x[min], NB = min_nb, Status = "min"),
    tibble(Idx = max, Values = x[max], NB = max_nb, Status = "max")) %>%
    arrange(.data$Idx) %>%
    mutate(Last = last) %>%
    mutate_at(vars(.data$Idx, .data$NB), as.integer)

原问题的答案是:

> x <- c(1, 2, 3, 2, 1, 1, 2, 1)
> locate_xtrem(x)
# A tibble: 5 x 5
    Idx Values    NB Status Last 
  <int>  <dbl> <int> <chr>  <lgl>
1     1      1     1 min    FALSE
2     3      3     1 max    FALSE
3     5      1     2 min    FALSE
4     7      2     1 max    FALSE
5     8      1     1 min    FALSE

结果表明第二个最小值等于 1,并且该值从索引 5 开始重复两次。因此,通过将这个时间指示给函数以查找最后一次出现的局部极值,可以获得不同的结果:

> locate_xtrem(x, last = TRUE)
# A tibble: 5 x 5
    Idx Values    NB Status Last 
  <int>  <dbl> <int> <chr>  <lgl>
1     1      1     1 min    TRUE 
2     3      3     1 max    TRUE 
3     6      1     2 min    TRUE 
4     7      2     1 max    TRUE 
5     8      1     1 min    TRUE 

根据目标,可以在局部极值的第一个值和最后一个值之间切换。 last = TRUE 的第二个结果也可以通过列“Idx”和“NB”之间的操作获得...

最后,为了处理数据中的噪声,可以实现一个函数来消除低于给定阈值的波动。代码没有公开,因为它超出了最初的问题。我已经将它包装在一个包中(主要是为了自动化测试过程),并在下面给出一个结果示例:

x_series %>% xtrem::locate_xtrem()

x_series %>% xtrem::locate_xtrem() %>% remove_noise()

【讨论】:

不错!我有一个累积值图,因此有平坦的范围。您的解决方案是这里唯一有效的解决方案。也可以包含绘图代码。 这是一个很棒的功能!能够在重复序列中控制第一个和最后一个非常方便。【参考方案14】:

Timothée Poisot 的这个函数对于嘈杂的系列很方便:

2009 年 5 月 3 日在向量中查找局部极值的算法 Filed under: Algorithm — Tags: Extrema, Time series — Timothée Poisot @ 6:46pm

我花了一些时间寻找一种算法来找到局部极值 一个向量(时间序列)。我使用的解决方案是“步行”通过 向量逐级大于1,为了甚至只保留一个值 当值非常嘈杂时(参见末尾的图片 发布)。

事情是这样的:

findpeaks <- function(vec,bw=1,x.coo=c(1:length(vec)))

    pos.x.max <- NULL
    pos.y.max <- NULL
    pos.x.min <- NULL
    pos.y.min <- NULL   for(i in 1:(length(vec)-1))            if((i+1+bw)>length(vec))
                sup.stop <- length(vec)elsesup.stop <- i+1+bw
                
        if((i-bw)<1)inf.stop <- 1elseinf.stop <- i-bw
        subset.sup <- vec[(i+1):sup.stop]
        subset.inf <- vec[inf.stop:(i-1)]

        is.max   <- sum(subset.inf > vec[i]) == 0
        is.nomin <- sum(subset.sup > vec[i]) == 0

        no.max   <- sum(subset.inf > vec[i]) == length(subset.inf)
        no.nomin <- sum(subset.sup > vec[i]) == length(subset.sup)

        if(is.max & is.nomin)
            pos.x.max <- c(pos.x.max,x.coo[i])
            pos.y.max <- c(pos.y.max,vec[i])
        
        if(no.max & no.nomin)
            pos.x.min <- c(pos.x.min,x.coo[i])
            pos.y.min <- c(pos.y.min,vec[i])
        
    
    return(list(pos.x.max,pos.y.max,pos.x.min,pos.y.min))

Link to original blog post

【讨论】:

我喜欢这个功能。它提供了一种确定信封的好方法。【参考方案15】:

我们在这里看到了许多具有不同功能的不错的功能和想法。几乎所有示例的一个问题是效率。很多时候,我们看到使用像diff()for()-loops 这样的复杂函数,当涉及到大型数据集时,它们会变得很慢。让我介绍一个我每天都在使用的高效功能,功能最少,但速度非常快:

局部最大值函数amax()

目的是检测实值向量中的所有局部最大值。 如果第一个元素x[1] 是全局最大值,则忽略它, 因为没有关于前一个元素的信息。如果有 是一个平台,第一个边缘被检测到。

@param x 数值向量

@return 返回局部最大值的指标。如果x[1] = max,那么 它被忽略了。

amax <- function(x)

  a1 <- c(0,x,0)
  a2 <- c(x,0,0)
  a3 <- c(0,0,x)
  e <- which((a1 >= a2 & a1 > a3)[2:(length(x))])
  if(!is.na(e[1] == 1))
    if(e[1]==1)
      e <- e[-1]
  if(length(e) == 0) e <- NaN
  return (e)


a <- c(1,2,3,2,1,5,5,4)
amax(a) # 3, 6

【讨论】:

【参考方案16】:

@BEN 提出的公式和@TOMMY 提出的案例的增强(快速而简单的方法):

以下递归公式可以处理任何情况:

dx=c(0,sign(diff(x)))
numberofzeros= length(dx) - sum(abs(dx)) -1 # to find the number of zeros 
                                            # in the dx minus the first one 
                                            # which is added intentionally.
#running recursive formula to clear middle zeros 
# iterate for the number of zeros   
for (i in 1:numberofzeros) 
    dx = sign(2*dx + c(0,rev(sign(diff(rev(dx))))))
    

现在,只需稍加改动即可使用@Ben Bolker 提供的公式:

plot(x)
points(which(diff(dx)==2),x[which(diff(dx)==2)],col = 'blue')#Local MIN.
points(which(diff(dx)==-2),x[which(diff(dx)==-2)],col = 'red')#Local MAX.

【讨论】:

以上是关于寻找局部最大值和最小值的主要内容,如果未能解决你的问题,请参考以下文章

一种获取向量的局部最小值和最大值的方法

Python在离散数据上查找局部最大值和最小值[重复]

编程之美寻找数组中的最大值和最小值

Python/Pandas- 在趋势变化时应用标签(识别数据集中的局部最大值和最小值)

在 1D-NumPy 数组中查找奇异值/局部最大值/最小值集(再次)

Pandas:基于局部最小值-最大值的数据之字形分割