如何从排序的 Ruby 数组中删除所有元素,这些元素与其最近的邻居比限制更近?

How to remove all elements from sorted Ruby array which are closer to its closest neighbour than a limit?

我的 Ruby 程序中有一个排序的实数数组。我想删除所有非常 "similar" 的元素:它们的差异小于给定的限制。所以最后我只想保留那些与其他元素很好区分的元素,不同的元素:原始数组中没有其他元素比限制更接近它们。

目前我正在试验这两种方法:

limit=0.5
vvs=vv.sort.reverse.each_cons(2).map{|a,b| (a-b).abs<limit ? nil : a}.compact

vvs=vv.each_cons(3).map{|a,b,c| (a-b).abs<limit && (b-c).abs<limit  ? nil : b}.compact

我的程序需要使用这种方法来同步字幕,并且这些值可能包含一些噪音。由于这个事实,我只想分析那些即使存在一些附加噪声也可以区分的不同元素。

我的原始真实数据来自"Catch 22"https://pastebin.com/mRiS02mb

没有检查真实数据,但可能类似于(从 0 开始,但可以更改为 -Float::INFINITY):

data = [1, 1.05, 1.5, 1.5, 1.9, 2, 2.1, 3, 3.6, 4, 4.1]

delta = 0.5
data.each_with_object([]) { |e, o| o << e if e >= (o.last || 0) + delta }

#=> [1, 1.5, 2, 3, 3.6, 4.1]

鉴于此示例数据:

data = [
  1.07, 1.14, 1.14, 1.24, 1.55, 1.56, 1.82, 1.83, 2.04, 2.16, 2.23,
  2.37, 2.38, 2.39, 2.41, 2.46, 2.54, 2.58, 2.93, 2.94, 2.98, 3.06,
  3.12, 3.18, 3.62, 3.65, 3.69, 3.87, 4.0, 4.25, 4.36, 4.36, 4.38,
  4.63, 4.78, 4.8, 4.83, 4.86, 5.13, 5.37
]

您可以按四舍五入的值对数字进行分组:

limit = 0.5
grouped_data = data.group_by { |f| (f / limit).round * limit }
#=> {
#     1.0 => [1.07, 1.14, 1.14, 1.24],
#     1.5 => [1.55, 1.56],
#     2.0 => [1.82, 1.83, 2.04, 2.16, 2.23],
#     2.5 => [2.37, 2.38, 2.39, 2.41, 2.46, 2.54, 2.58],
#     3.0 => [2.93, 2.94, 2.98, 3.06, 3.12, 3.18],
#     3.5 => [3.62, 3.65, 3.69],
#     4.0 => [3.87, 4.0],
#     4.5 => [4.25, 4.36, 4.36, 4.38, 4.63],
#     5.0 => [4.78, 4.8, 4.83, 4.86, 5.13],
#     5.5 => [5.37]
#   }

0.75 到 1.25 的值在 1.0 槽中,1.25 到 1.75 的值在 1.5 槽中,依此类推。

现在从组中选择一个值,例如第一个:

grouped_data.map { |k, vs| vs.first }
#=> [1.07, 1.55, 1.82, 2.37, 2.93, 3.62, 3.87, 4.25, 4.78, 5.37]

或者中间那个:

grouped_data.map { |k, vs| vs[vs.size/2] }
#=> [1.14, 1.56, 2.04, 2.41, 3.06, 3.65, 4.0, 4.36, 4.83, 5.37]

或最接近其各自插槽值的值:

grouped_data.map { |k, vs| vs.min_by { |v| (k - v).abs } }
#=> [1.07, 1.55, 2.04, 2.46, 2.98, 3.62, 4.0, 4.38, 5.13, 5.37]

请注意,如果相邻插槽的值恰好靠近边界,则它们仍可能在限制内,例如

[1.24, 1.26].group_by { |f| (f / limit).round * limit }
#=> { 1.0 => [1.24], 1.5 => [1.26] }

问题好像有点歧义。我按照我在对该问题的评论中所述进行解释。

data = [ 3.42,  5.49,  6.12,  6.48,  7.11,  8.79,  9.36,
         9.54, 10.86, 10.95, 11.07, 13.08, 14.41, 14.92] 
limit = 0.5

([-Float::INFINITY].concat(data) << Float::INFINITY).each_cons(3).
  select { |a,b,c| b-a >= 0.5 && c-b >= 0.5 }.
  map { |_,b,_| b }
  #=> [3.42, 5.49, 7.11, 8.79, 14.41, 14.92]