使用ruby压缩时间序列数据流

时间:2015-10-07 13:45:06

标签: ruby filter compression time-series iot

我有一个我想从传感器读取的流。流永远不会结束。大多数情况下,值会随着时间的推移而重复。所以我想识别值的运行,并保持每次运行的第一个和最后一个,并保持它们的时间戳。

以下是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]]。懒惰

我想将此数据压缩为: [[' 8:00',4],[' 8:02',4],[' 8:03',7],[&#39 ; 8:04',7],[' 8:05',8],[' 8:06',9],[' 8:07&# 39;,13],[' 8:09',13]]

我一直试图通过chunk,each_cons,each_with_object之类的可枚举函数来实现这一目标。但是,这个问题似乎具有内在功能。我可以在ruby中使用lazy枚举器来完成这个吗?

3 个答案:

答案 0 :(得分: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的功能样式和首选方式,但是产生了很多不必要的中间数组。您可以通过实际附加(<<)来创建一个新数组。

答案 1 :(得分: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]]

答案 2 :(得分:0)

我正在发布我自己的问题的解决方案。我开始使用Kristján的解决方案,该解决方案使用了reduce。请注意,我的解决方案无法生成最终的采样时间,但我选择接受此行为,因为我的示例只是模拟流。因此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]]