【问题标题】:What is the fastest way to calculate a rolling average of an array with Ruby?用 Ruby 计算数组滚动平均值的最快方法是什么?
【发布时间】:2015-04-07 20:31:21
【问题描述】:

在 ruby​​ 中计算数组 x 秒滚动平均值的最快方法是什么?

我有两个骑自行车的数据数组。该时间是在行驶过程中读取相应速度值的时间。您会注意到读数并不是每秒都读取的。出于这个原因,我不相信我可以将滚动数组加一。

speed = [0, 15, 17, 19, 18, 22, 24, 28, 22, 17, 16, 14, 15, 15, 15, 0, 15, 19, 21, 25, 26, 24, 24] 
time = [0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, 20, 21, 22, 23, 25, 26, 27] 

我已经尝试过类似以下的方法(计算 5 秒的滚动平均值并将其放入一个数组中),但对于大型数组和多个间隔来说它非常慢(需要 8 分钟来计算 1 小时自行车骑行的所有间隔, 1..3600):

duration = time.max

interval_average = []
time_hash = Hash[time.map.with_index.to_a] 

roll_start = 0
roll_stop = 5

for i in 1..(duration+1) do
    start = time_hash[roll_start]
    stop = time_hash[roll_stop]

    rolling_array = speed[start..stop]

    avg_value = mean(rolling_array)

    interval_average.push(avg_value)

    roll_start += 1
    roll_stop += 1
end

在我自己的代码中,我忽略了异常并改为推送 0,因为我真的只是对最终找到 x 秒平均值的最大值感兴趣。

【问题讨论】:

  • speed[start..stop] 将分配一个子数组,这可能会导致一些实质性的 GC 抖动。您的目标可能应该是尽可能消除分配;重用中间数组将产生巨大的好处。
  • @ChrisHeald 我怀疑分配是这里的罪魁祸首。 arr = 10_000_000.times.to_a; Benchmark.measure { 1_000_000.times { ar[100..-2] } }.real #=> 0.17680915212258697
  • 首先分析您的代码以查看时间的去向(例如 ruby​​-prof)

标签: ruby arrays average


【解决方案1】:

我不确定它的性能,但这里有另一种方法,您可以测试它来找到某个固定时间长度内的最大滚动平均值。

speed = [0, 15, 17, 19, 18, 22, 24, 28, 22, 17, 16, 14, 15, 15, 15, 0, 15, 19, 21, 25, 26, 24, 24] 
time = [0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, 20, 21, 22, 23, 25, 26, 27] 

interval_length = 5 # seconds

speed.zip(time)                                                     # 1
     .each_cons(interval_length)                                    # 2
     .select { |i| i.last.last - i.first.last == interval_length}   # 3
     .map { |i| i.map(&:first).reduce(:+) / interval_length.to_f }  # 4
     .max                                                           # 5

将其分解为具有中间结果的组件:

1) 将每个速度读数与所用时间配对。

# => [[0, 0], [15, 1], [17, 2], [19, 3], [18, 5], [22, 6], [24, 7], [28, 8], [22, 10], [17, 11], [16, 12], [14, 13], [15, 15], [15, 16], [15, 17], [0, 18], [15, 20], [19, 21], [21, 22], [25, 23], [26, 25], [24, 26], [24, 27]]

2) 将上述分割成连续运行的interval_length,在本例中为 5。这将为您提供一个 Enumerator 对象,但使用 to_a 我们可以看到中间结果如下所示:

# => [[15, 1], [17, 2], [19, 3], [18, 5], [22, 6]], [[17, 2], [19, 3], [18, 5], [22, 6], [24, 7]], [[19, 3], [18, 5], [22, 6], [24, 7], [28, 8]], [[18, 5], [22, 6], [24, 7], [28, 8], [22, 10]], [[22, 6], [24, 7], [28, 8], [22, 10], [17, 11]], [[24, 7], [28, 8], [22, 10], [17, 11], [16, 12]], [[28, 8], [22, 10], [17, 11], [16, 12], [14, 13]], [[22, 10], [17, 11], [16, 12], [14, 13], [15, 15]], [[17, 11], [16, 12], [14, 13], [15, 15], [15, 16]], [[16, 12], [14, 13], [15, 15], [15, 16], [15, 17]], [[14, 13], [15, 15], [15, 16], [15, 17], [0, 18]], [[15, 15], [15, 16], [15, 17], [0, 18], [15, 20]], [[15, 16], [15, 17], [0, 18], [15, 20], [19, 21]], [[15, 17], [0, 18], [15, 20], [19, 21], [21, 22]], [[0, 18], [15, 20], [19, 21], [21, 22], [25, 23]], [[15, 20], [19, 21], [21, 22], [25, 23], [26, 25]], [[19, 21], [21, 22], [25, 23], [26, 25], [24, 26]], [[21, 22], [25, 23], [26, 25], [24, 26], [24, 27]

3) 由于您没有每秒的信息,因此每块速度值中的一些可能会超过时间间隔,而不是真正的interval_length 秒长。因此,让我们将计算仅限于这些。 5秒内,恰好不需要丢弃任何数据,中间结果与第2步相同。

4) 现在我们可以取滚动平均值:

 # => [13.8, 18.2, 20.0, 22.2, 22.8, 22.6, 21.4, 19.4, 16.8, 15.4, 15.0, 11.8, 12.0, 12.8, 14.0, 16.0, 21.2, 23.0, 24.0]

5) 及其最大值:

# => 24.0

同样,我不确定这在一个非常大的数据集上会如何,但它可能值得一试。

【讨论】:

  • 这很有意义 - 它适用于最大为 8 的 interval_lengths,但在 9 及以上时会中断。我最初认为这可能是由于缺少 9 秒点,但它使它超过了缺少的 4 秒点。知道为什么吗?它看起来确实快得多。
  • 有趣的是,对于这个以 9 分割的数据集,每个实际的区间长度都是 10(0 到 10、1 到 11、2 到 12 等),所以当我们选择时,我们得到一个空数组。让我看看能不能稍微调整一下。
  • 我越看这个,我就越觉得它可能行不通。例如 - 从第 1 秒开始的 10 秒平均速度需要查看从 1 秒到 11 秒的速度点(10 秒,但在这种情况下只有 9 个数据点)。我正试图摆脱寻找这些点的索引,因为这似乎是减慢速度的原因,但这可能是不可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-27
  • 2019-05-31
  • 1970-01-01
  • 2018-04-05
  • 2018-07-28
  • 2020-09-27
  • 2021-06-04
相关资源
最近更新 更多