【问题标题】:How to identify peaks and troughs in an array of floats?如何识别浮点数组中的峰和谷?
【发布时间】:2019-03-06 03:07:45
【问题描述】:

我有一个数组,

array = [
  0.43, 
  0.64, # => peak
  0.2, 
 -0.05, 
 -0.15, # => trough
  0.2, # => peak
 -0.1, 
 -0.5, # => trough
 -0.3
]

数据中有两个峰值和两个谷值。这些波峰和波谷不一定是数组的 minmax。如何通过程序识别它们?

理想的输出是:

peak_indexes = [1, 5]
trough_indexes = [4, 7]

【问题讨论】:

  • 是什么让 -0.5 成为负峰值?
  • @ElChapo 因为它之前的数据比-0.5更接近0是
  • 那么在这种情况下,每个最终元素都会是一个峰值/谷值吗?如果之前的元素更接近 0,则为峰值,如果之前的元素远离 0,则为 Trough。
  • 这是一个合理的观点。我会说排除最后一个元素,因为我认为它无法确定它是否是真正的峰值
  • 如果峰/谷跨越多个元素,例如[0.1, 0.6, 0.6, 0.2]?

标签: arrays ruby


【解决方案1】:

each_cons(3) 提取检查中间项所需的相邻三个术语,同时排除中间具有array 的第一个或最后一个元素的三元组。

with_index(1) 考虑到中间有array 的第一个元素的三元组被跳过,因此从 1 开始对索引进行编号。

您还没有定义高峰和低谷的含义。如果您的意思是取当地的最大值和最小值,那么以下将起作用。

array.each_cons(3).with_index(1).select{|a, i| a.max == a[1]}.map(&:last)
# => [1, 5]

array.each_cons(3).with_index(1).select{|a, i| a.min == a[1]}.map(&:last)
# => [4, 7]

或者,如果您的意思是 Stefan 在对我的回答的评论中解释的内容,那么以下内容将起作用:

array
.each_cons(3)
.with_index(1)
.select{|(a1, a2, a3), i| a1 < a2 && a2 > a3}
.map(&:last)
# => [1, 5]

array
.each_cons(3)
.with_index(1)
.select{|(a1, a2, a3), i| a1 > a2 && a2 < a3}
.map(&:last)
# => [4, 7]

【讨论】:

  • a.max == a[1] / a.min == a[1] 如果有多个相同的条目,则给出误报,例如[0.1, 0.2, 0.2, 0.3]。相反,您应该检查中间元素是否实际上大于(不大于或等于)其邻居,例如通过|(a, b, c), i| a &lt; b &amp;&amp; b &gt; c }
  • 希望 Ruby 能够像 Python 那样进行链式比较。 a &lt; b &gt; c 看起来很酷。
  • @Stefan 我认为这会使语法变得复杂。该表达式必须被理解为一个单一的结构,而不是两种方法的连续应用。即,a &lt; b&gt; c 并不意味着(a &lt; b) &gt; c。一些数学表达式不适合编程。另一个例子是$a, b \in A$,如果将它导入到 Ruby 中,可能会与多重赋值混淆。
  • (a &lt; b) &gt; c 无论如何都没有多大意义,因为a &lt; b 返回truefalse,两者都没有响应&gt;。不幸的是,1 &lt; 2 == true 的评估方式不同,因此这可能会破坏现有代码。除此之外,我认为它非常适合编程。
【解决方案2】:

首先让我们定义什么是高峰或低谷。

  • 峰值是大于其左右值的值。
  • 波谷是一个比其左右两侧的值更小的值。

这将允许我们定义两个有用的函数:

def is_peak?(left_value, value, right_value)
  return value > left_value && value > right_value
end

def is_trough?(left_value, value, right_value)
  return value < left_value && value < right_value
end

现在我们可以遍历数组中的每个元素,并使用这些函数来询问该元素是波峰还是波谷。

array = [0.43, 0.64, 0.2, -0.05, -0.15, 0.2, -0.1, -0.5]
positive_peak_indexes = []
negative_peak_indexes = []
# Loop through the array
array.each_with_index do |elem, i|
  # Make sure we don't get an out of bounds exception
  next if (i-1 < 0) || (i + 1 >= array.length)

  # Now we add to our appropriate arrays if it's a peak/trough
  positive_peak_indexes << i if is_peak?(array[i-1], elem, array[i+1])
  negative_peak_indexes << i if is_trough?(array[i-1], elem, array[i+1])
end
puts "positive_peak_indexes = #{positive_peak_indexes.to_s}"
puts "negative_peak_indexes = #{negative_peak_indexes.to_s}"

【讨论】:

    【解决方案3】:

    我没有时间测试更多案例,所以可能存在一些错误。

    无论如何,基本的想法是使用它的索引(Enumerable#each_with_index)压缩每个元素,然后在有变化时使用方法Enumerable#chunk_while 对数组进行切片。最后从块中提取极端。

    用一段代码比较好,ary是数据数组。

    首先是块:

    chunks = ary.map.with_index{ |x, i| [x,i] }.chunk_while { |x,y| y.first < x.first }
    

    然后收集极端:

    peaks = chunks.collect { |e| e.first if e.size > 1 }.compact #=> [[0.64, 1], [0.2, 5]]
    trough = chunks.collect { |e| e.last if e.size > 1 }.compact #=> [[-0.15, 4], [-0.5, 7]]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-22
      • 2021-05-21
      • 1970-01-01
      • 2015-07-27
      • 1970-01-01
      • 1970-01-01
      • 2021-07-07
      • 1970-01-01
      相关资源
      最近更新 更多