Как добавить значения в массив, если есть нулевая запись?

Я хочу создать реальный массив временных рядов. В настоящее время я использую жемчужину статистики для получения значений за каждый «день»:

define_statistic :sent_count, :count
=> :all, :group => 'DATE(date_sent)',    
:filter_on => {:email_id => 'email_id
> = ?'}, :order => 'DATE(date_sent) ASC'

Это создает массив, в котором есть значения для даты, например

[["12-20-2010",1], ["12-24-2010",3]]

Но мне нужно, чтобы он заполнил нулевые значения, так что это больше похоже на:

[["12-20-2010",1], ["12-21-2010",0], ["12-22-2010",0], ["12-23-2010",0], ["12-24-2010",3]]

Обратите внимание, что во втором примере значения «0» соответствуют дням, отсутствующим в первом массиве.


person Satchel    schedule 27.12.2010    source источник
comment
Это звучит как вопрос для жемчужины статистики; не могли бы вы дать ссылку на более подробную информацию об этом?   -  person Phrogz    schedule 27.12.2010
comment
Я думал, что с этим можно справиться с помощью какой-нибудь функции ruby-массива для заполнения нулевых значений, но вот ссылка: github. com/acatighera/statistics   -  person Satchel    schedule 27.12.2010


Ответы (2)


#!/usr/bin/ruby1.8

require 'date'
require 'pp'

def add_missing_dates(series)
  series.map do |date, value|
    [Date.strptime(date, '%m-%d-%Y'), value]
  end.inject([]) do |series, date_and_value|
    filler = if series.empty?
               []
             else
               ((series.last[0]+ 1)..(date_and_value[0] - 1)).map do |date|
                 [date, 0]
               end
             end
    series + filler + [date_and_value]
  end.map do |date, value|
    [date.to_s, value]
  end
end

a = [["12-20-2010",1], ["12-24-2010",3]]
pp add_missing_dates(a)
# => [["2010-12-20", 1],
# =>  ["2010-12-21", 0],
# =>  ["2010-12-22", 0],
# =>  ["2010-12-23", 0],
# =>  ["2010-12-24", 3]]

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

module AddMissingDates
  def add_missing_dates(series)
    ...
  end
end

class MyClass
  include AddMissingDates
  ...
end

Однако, если вы действительно хотите:

def Array.add_missing_dates(series)
  ...
end
person Wayne Conrad    schedule 27.12.2010
comment
правильно, да, я хотел бы сделать это как модуль, поэтому я бы сохранил его в каталоге lib/ как add_missing_dates.rb? - person Satchel; 27.12.2010
comment
Понятно... спасибо... Я поискал в Google "pp", чтобы немного лучше понять, что делается, но не нашел. Что такое «пп»? :) - person Satchel; 28.12.2010
comment
@Angela, это похоже на p, стандартный способ проверки объекта на стандартный вывод, но делает вывод более красивым (я думаю, pp означает красивый шрифт). ruby-doc.org/core/files/lib/pp_rb.html< /а> - person Wayne Conrad; 28.12.2010
comment
@wayne, спасибо, извините, что так медленно с вашим элегантным решением, я заблудился на серии + наполнитель + [date_and_value]. Кажется, что серия должна представлять исходные значения, преобразованные в дату, и вы использовали inject, чтобы перейти к промежуточному состоянию и добавить наполнитель даты, 0. Но что такое date_and_value тогда? Спасибо... извините, пытаюсь понять, как я реализую.... :\ - person Satchel; 28.12.2010
comment
@wayne -- привет, я получаю сообщение об ошибке неверной даты, не знаю, как ее отладить, так как я не получаю больше информации, это около строки strptime - person Satchel; 28.12.2010
comment
@wayne -- хе-хе, я исправил ошибку с недействительной датой :) это работает, просто пытаюсь понять это лучше... - person Satchel; 28.12.2010
comment
@Анжела, не беспокойтесь. Чтобы разгадать тайну inject, см. stackoverflow.com/questions/710501/. Ключом к чтению строки series + filler... является то, что series — это новая серия на данный момент. - person Wayne Conrad; 28.12.2010
comment
я думаю, что теперь вижу... [date_and_value] содержит оставшийся набор оставшихся пар ключей "дата-значение".... Я думаю... - person Satchel; 28.12.2010
comment
@Анжела, почти. date_and_value содержит одну пару дата/значение. inject вызывает свой блок один раз для каждой пары дата/значение, передавая накопленный вывод (series) как и пару дата_значение (date_and_value). Результат блока становится новым накопленным выходом. - person Wayne Conrad; 28.12.2010

Это работает:

#!/usr/bin/env ruby

require 'pp'
require 'date'

# convert the MM-DD-YYYY format date string to a Date
DATE_FORMAT = '%m-%d-%Y'
def parse_date(s)
  Date.strptime(s, DATE_FORMAT)
end

dates = [["12-20-2010",1], ["12-24-2010",3]]

# build a hash of the known dates so we can skip the ones that already exist.
date_hash = Hash[*dates.map{ |i| [parse_date(i[0]), i[-1]] }.flatten]

start_date_range = parse_date(dates[0].first) 
end_date_range   = parse_date(dates[-1].first)

# loop over the date range...
start_date_range.upto(end_date_range) do |d|
  # ...and adding entries for the missing ones.
  date_hash[d] = 0 if (!date_hash.has_key?(d))
end

# convert the hash back into an array with all dates
all_dates = date_hash.keys.sort.map{ |d| [d.strftime(DATE_FORMAT), date_hash[d] ] }
pp all_dates

# >> [["12-20-2010", 1],
# >>  ["12-21-2010", 0],
# >>  ["12-22-2010", 0],
# >>  ["12-23-2010", 0],
# >>  ["12-24-2010", 3]]

Большая часть кода занимается подготовкой либо для построения нового массива, либо для возврата объектов даты обратно в строки.

person the Tin Man    schedule 27.12.2010
comment
круто, как бы вы предложили мне его создать? Возможно ли, что это может быть библиотека, чтобы я мог взять исходный массив и запустить на нем метод? например, date.convert_to_timeseries? - person Satchel; 27.12.2010