2015-10-07 1 views
2

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

Ниже приведен пример 10 минут данных:

[[ '8:00', 4], [ '8:01', 4], [ '8:02', 4], [ '8:03', 7], ['8:04', 7], ['8:05', 8], ['8:06', 9], ['8:07', 13], [ '8:08', 13], ['8:09', 13]]. Lazy

Я хочу сжать эти данные к этому: [['8:00', 4], ['8: 02 ', 4], [' 8:03 ', 7], [' 8:04 ', 7], [' 8:05 ', 8], [' 8:06 ', 9], [' 8: 07 ', 13], [' 8:09 ', 13]]

Я пытался выполнить это через перечислимые функции, такие как chunk, each_cons, each_with_object. Однако эта проблема кажется функционально функциональной. Могу ли я выполнить это, используя ленивый перечислитель в рубине?

ответ

0
data.reduce([data.first]) do |result, item| 
    result.last.last == item.last ? result : result + [item] 
end 

Это не дает желаемого результата - он пропускает последний элемент пробега. Но хорошей новостью является то, что вам не нужен последний элемент, потому что вы знаете, что его значение совпадает с вашим первым элементом, и вы знаете, что его метка времени меньше, чем следующий элемент. (Если ваши временные метки не последовательны, то это не хорошо). Если последняя запись также не находится на Time.now, простейшая вещь, которую нужно сделать, - это просто вручную включить ее в конце.

Что он делает:

  • Инициализировать результат с первым значением. Это просто, чтобы избежать случая nil в начале.
  • Для каждого item в data
    • Если значение в item.last такое же, как в последней записи в настоящее время в result, ничего не делать
    • Если значение item.last отличается, добавьте его в result

Я написал это так, чтобы каждая итерация создавала новый массив result с result + [item], который является функциональным стилем и предпочтительным способом использования reduce, но это создает много ненужных промежуточных массивов. Вы можете создать только один новый массив, фактически добавив (<<).

+0

Ваше решение работает так, как вы описываете, и делает это элегантно. Он захватывает первый образец каждого прогона одинаковых значений, но не фиксирует окончательный образец пробега. Однако мне нужна эта окончательная выборка. В моем примере образцы поступают с регулярным интервалом - один раз в минуту, но на самом деле я никогда не смогу наслаждаться такой предсказуемостью из-за проблем с передачей данных и проблем с сенсором. Будут пробелы. Поэтому было бы невозможно вычислить окончательное время, в течение которого измерялся последний образец пробега. – mojo2go

+0

Да, подумал, что может быть так. Рад, что ты нашел способ! –

0

Это не изящное решение, но оно работает.

data = ['8:00', 4],['8:01', 4],['8:02', 4],['8:03', 7],['8:04', 7],['8:05', 8],['8:06', 9],['8:07', 13],['8:08', 13],['8:09', 13] 

def clean_array(data) 
    item_to_delete = [] 

    (0..(data.count-3)).each do |i| 
     if data[i][1].eql?(data[i+2][1]) 
      item_to_delete << data[i+1] 
     end 
    end 

    data - item_to_delete 
end 

new_data = clean_array(data) 

Выход, как и ожидалось является

=> [["8:00", 4], ["8:02", 4], ["8:03", 7], ["8:04", 7], ["8:05", 8], ["8:06", 9], ["8:07", 13], ["8:09", 13]] 

Редактировать

Другой подход

data = ['8:00', 4],['8:01', 4],['8:02', 4],['8:03', 7],['8:04', 7],['8:05', 8],['8:06', 9],['8:07', 13],['8:08', 13],['8:09', 13]  
new_data = [] 

data.each { |item| (new_data[-2] and item[1].eql?(new_data[-2][1])) ? new_data[-1] = item : new_data << item } 

new_data 

# => => [["8:00", 4], ["8:02", 4], ["8:03", 7], ["8:04", 7], ["8:05", 8], ["8:06", 9], ["8:07", 13], ["8:09", 13]] 
+0

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

+0

проверить мое редактирование немного более элегантным способом. – Wagner

0

Я отправляю решение моего вопроса. Я начал с решения Кристьяна, который использовал сокращение.Обратите внимание, что мое решение не дает окончательного времени выборки, но я решил принять это поведение, потому что мой пример просто предназначен для моделирования потока. Так что выбор 8:09 не должен быть окончательным значением. Следующий входящий образец определит, будет ли сохранено значение 8:09. Так что подробности моего первоначального сообщения могли быть лучше объяснены.

samples = [['8:00', 4],['8:01', 4],['8:02', 4],['8:03', 7],['8:04', 7],['8:05', 8],['8:06', 9],['8:07', 13],['8:08', 13],['8:09', 13]].lazy 

prev = [] 
compressed = samples.reduce([samples.first]) do |keepers, sample| 
    keepers << prev << sample if keepers.last.last != sample.last 
    prev = sample 
    keepers 
end 
puts compressed.inspect 

# => [["8:00", 4], ["8:02", 4], ["8:03", 7], ["8:04", 7], ["8:05", 8], ["8:05", 8], ["8:06", 9], ["8:06", 9], ["8:07", 13]]