2015-04-07 3 views
0

Каков самый быстрый способ вычисления среднего среднего скользящего среднего x в рубине?Каков самый быстрый способ вычисления скользящего среднего массива с Ruby?

У меня есть два массива данных с велосипедной поездки. Время, когда соответствующее значение скорости было прочитано во время поездки. Вы заметите, что показания не принимались каждую секунду. По этой причине я не верю, что могу просто увеличивать скользящий массив на единицу.

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 вместо этого, так как я на самом деле просто заинтересован в поиске Маха х второй усредняет в конец.

+0

'speed [start..stop]' будет выделять суб-массив, что, вероятно, вызывает некоторый существенный трэш GC. Вероятно, ваша цель должна заключаться в том, чтобы устранить распределения, где это возможно; повторное использование промежуточных массивов даст существенные преимущества. –

+0

@ChrisHeald Я сомневаюсь, что ассигнования здесь являются преступниками. 'arr = 10_000_000.times.to_a; Benchmark.measure {1_000_000.times {ar [100 ..- 2]}} .real # => 0.17680915212258697' –

+2

Начните с профилирования кода, чтобы узнать, где идет время (например, ruby-prof) –

ответ

0

Я не уверен в его производительности, но вот еще один подход, который вы можете проверить, чтобы найти максимальные скользящие средние за определенный фиксированный отрезок времени.

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 

Опять же, я не знаю, как это будет плата за проезд на действительно большой набор данных, но это может быть стоит попробовать.

+0

Это делает много sense - он работает для interval_lengths до 8, но затем разбивается на 9 и выше. Первоначально я думал, что это может быть из-за недостающего 9-секундного пункта, но он пропустил пропущенную 4-секундную точку. Любая идея почему? Это кажется намного быстрее. – user4740054

+0

Интересно, что для этого набора данных, разделенного на 9, каждая фактическая длина интервала составляет 10 (от 0 до 10, от 1 до 11, от 2 до 12 и т. Д.), Поэтому при выборе мы получаем пустой массив. Позвольте мне посмотреть, могу ли я немного изменить его. –

+0

Чем больше я смотрю на это, тем больше думаю, что это, вероятно, не сработает. Например - средняя скорость 10 секунд, начинающаяся со второго, должна смотреть на точки скорости от 1 секунды до 11 секунд (10 секунд, но только 9 точек данных в этом случае). Я пытаюсь отойти от поиска индекса этих точек, поскольку, похоже, это то, что замедляет работу, но это может быть невозможно. – user4740054

Смежные вопросы