Группа Ruby Array и среднее по часам

Мы получаем данные от датчика, который записывает и хранит такие данные, как хэши. В любое время он измеряет несколько таких вещей:

{:temperature => 30, :pression => 100, :recorded_at => 14:34:23}
{:temperature => 30, :pression => 101, :recorded_at => 14:34:53}
{:temperature => 31, :pression => 102, :recorded_at => 14:34:24}
{:temperature => 30, :pression => 101, :recorded_at => 14:34:55}
{:temperature => 30, :pression => 102, :recorded_at => 14:34:25}
{:temperature => 31, :pression => 101, :recorded_at => 14:34:56}

Нам нужно иметь возможность экспортировать эти данные в формате JSON, но у нас слишком много данных (датчик записывает примерно каждые 30 секунд), и нам нужно удалить некоторые данные. В идеале мы хотели бы экспортировать 1 меру в час за последние 24 часа, чтобы у нас было что-то вроде

{0 => {:temperature => 30, :pression => 100}, 1 => {:temperature => 30, :pression => 100}, 2 => {:temperature => 30, :pression => 100}, 3 => {:temperature => 30, :pression => 100}, 4 => {:temperature => 30, :pression => 100}}

Для каждого часа температура представляет собой среднее значение всех температур, измеренных в течение этого часа. Кроме того, если по какой-либо причине некоторые данные отсутствуют за 1 час, я хотел бы экстраполировать их, став средним значением между предыдущим и следующим часом. Кто-нибудь может помочь?


person Julien Genestoux    schedule 04.12.2009    source источник


Ответы (3)


Более функциональная версия (с простой интерполяцией пропущенных значений)

probs = [{:temperature => .. }] # array of measurings

def average(list, key)
  list.reduce(0){|acc,el| acc+el[key]} / list.length unless list.empty
end

prob_groups = probs.group_by{|prob| prob[:recorded_at][0,2].to_i}
average_groups = prob_groups.map do |hour,prob_group|
  { hour => {
      :temperature => average(prob_group, :temperature),
      :pression    => average(prob_group, :pression)
  }}
end.reduce{|acc,el| acc.merge(el)}

def interpolate(p, n, key)
  (p[key] + n[key])/2 unless p.nil? || n.nil? || p[key].nil? || n[key].nil?
end

resuls = (1..24).map do |hour|
  if average_groups[hour]
    { hour => average_groups[hour] }
  else
    { hour => {
      :temperature => interpolate(average_groups[hour-1], average_groups[hour+1], :temperature),
      :pression => interpolate(average_groups[hour-1], average_groups[hour+1], :pression)
    }}
  end
end.reduce{|acc,el| acc.merge(el)}

Надеюсь, что это работает

person MBO    schedule 04.12.2009
comment
Обратите внимание, что Enumerable#reduce был представлен в версии 1.8.7 в качестве нового предпочтительного псевдонима для #inject (я этого не знал, поэтому другие могут не знать об этом). - person Mike Woodhouse; 04.12.2009

что-то вроде этого

t = [....] - array of measurings
result = {}

(1..24).each do|hour| 
    #  measurings of given hour
    measurings = t.select{|measuring| measuring[:recorded_at][0, 2].to_i == hour}

    #  average temperature of hour
    sum = measurings.inject(0){|sum, measuring| sum + measuring[:temperature].to_i} 
    average_temperature = (measurings.length == 0)? nil: sum/measurings.length.to_f

    result[hour] = average_temperature
end
person andrykonchin    schedule 04.12.2009

Если вас не интересует история, а только приблизительные фактические значения, рассмотрите возможность использования «движущейся метрики» (http://en.wikipedia.org)./wiki/Moving_average).

person Carlo Pecchia    schedule 04.12.2009