寻找存储历史数据的有效方法

时间:2016-07-19 18:09:00

标签: python storage

数据是一个python dict,表示随时间变化缓慢的状态。价值经常变化,通常一次变化一件或两件。钥匙也可以改变,但这是一个罕见的事件。每次更改后,都会记住新数据集以供将来检查。

结果是一个长序列,时间戳增加。一个非常简单的例子" b"打开和关闭再打开:

(timestamp1, {'a':False, 'b':False, 'c':False}),
(timestamp2, {'a':False, 'b':True, 'c':False}),
(timestamp3, {'a':False, 'b':False, 'c':False}), 
(timestamp4, {'a':False, 'b':True, 'c':False}),

这个序列非常方便,但显然效率很低。几乎相同的数据被反复复制。真正的dict有大约100项。这就是为什么我要寻找一种不同的方式将数据历史存储在内存和磁盘上。

我很确定过去曾经多次解决这个问题。 此问题是否有任何标准/推荐方式?解决方案并非必须完美。足够好就足够了。

除非某种善良的人表现出更好的方法,否则我会这样做。仅存储增量更改是节省空间的:

(timestamp1, FULL, {'a':False, 'b':False, 'c':False}),
(timestamp2, INCREMENTAL, {'b':True}),
(timestamp3, INCREMENTAL, {'b':False}),
(timestamp4, INCREMENTAL, {'b':True}),

但是数据不容易访问,因为它必须从上一个FULL状态以几个步骤恢复。为了限制缺点,每个第N个记录将被存储为FULL,其他所有记录将被存储为INCREMENTAL。

我可能会添加这个小改进:添加对已经记录的相同状态的引用以防止重复:

(timestamp1, FULL, {'a':False, 'b':False, 'c':False}),
(timestamp2, INCREMENTAL, {'b':True}),
(timestamp3, SAME_AS, timestamp1),
(timestamp4, SAME_AS, timestamp2),

3 个答案:

答案 0 :(得分:3)

更节省空间的方法是为每个"列保留一组#34;数据的。也就是说,我们为列abc保留一组。该集合跟踪列的值为True的时间戳。例如,对于数据:

(timestamp1, {'a':False, 'b':False, 'c':False}),
(timestamp2, {'a':False, 'b':True, 'c':False}),
(timestamp3, {'a':False, 'b':False, 'c':False}), 
(timestamp4, {'a':False, 'b':True, 'c':False}),

a的集合将为空,列b的集合将包含时间戳2和4,列c的集合将再次为空。

请注意,这或多或少是用于存储稀疏二进制向量的方法。我们不是存储整个矢量,而是跟踪矢量的位置1.实际上,您可能需要考虑使用SciPy中的稀疏矩阵数据类型。

设置提供有效(恒定时间)成员资格查找,因此这也是一种节省时间的方法。

为了使数据易于访问,您可以编写一个包装集合的小类。例如:

class SparseStates(object):

    def __init__(self, columns):
        self.data = {col: set() for col in columns}

    def __getitem__(self, key):
        row, column = key
        return row in self.data[column]

    def turn_on(self, row, column):
        self.data[column].add(row)

用法:

>>> states = SparseStates(['a', 'b', 'c'])
>>> states.turn_on(2, 'b')
>>> states.turn_on(4, 'b')
>>> states[2, 'a']
False
>>> states[2, 'b']
True
>>> states.data['a']
{}
>>> states.data['b']
{2, 4}

答案 1 :(得分:1)

您可能希望了解PeopleSoft的EFFDT系统的灵感。它用于存储和查询数据库,但原则仍然存在。

每个项目都是单独存储的,包括一个键,一个生效时间(EFFDT)和各种值。

所以,拿'a'和'b',你会有这种类型的存储:

KEY     EFFDT       Active  VALUES
a       2016-07-16  True    False
a       2016-03-20  True    True
a       2016-01-16  True    False

        #note that 2016-11-22 is a future date.  its data will "activate"
        #any time your selection date criteria is >= Nov 22
b       2016-11-22  False   True

b       2016-05-16  True    False
b       2016-01-16  True    True

今天A和B的数据库查找:

select * from storage 
where KEY in ('A','B') 
and EFFDT = 
    /* pick the last date that is before the limit date (today */
    (select MAX(EFFDT) 
    from storage sub 
    where sub.key = storage.key
    and   sub.effdt <= '2016-07-19'
    )

您基本上必须将EFFDT视为“数据具有特定值时的时间范围的边界”。 “活动”列允许您通过将其设置为False来逻辑删除键。

您可以将上述内容存储在复合对象中,使用您的键,然后是一个已排序的日期列表。然后,查找将包括查找密钥,然后是符合条件的最新日期。

{
    #key    timestamp     active values.
    "a" : [("2016-07-16", True, (True,)),
           ("2016-03-20", True, (False,)),
           ("2016-01-16", True, (True,),
          },
    "b" : [("2016-05-16", True, (False,)),
           ("2016-01-16", True, (True,)),
          ],
}

注意:我添加了Active,因为这使得这个系统成为通用的历史存储限定符。但在你的情况下,你不会对它感兴趣。

截至2016-12-24,所有仍处于活动状态的密钥的查找只会返回'a':( False)并希望在SQL中显示:

select * from storage 
where Active = True   /* dont want Active=False data so 
                         we filter the subquery results.*/
and EFFDT = 
    /* pick the last date that is before the limit date (today */
    (select MAX(EFFDT) 
    from storage sub 
    where sub.key = storage.key
    and   sub.effdt <= '2016-12-24'
    )

答案 2 :(得分:0)

我会使用pickle来存储字典内容,具体取决于时间戳。您只需生成带有时间戳作为名称的pickle文件。您也可以使用pickle压缩文件。 Pickle应该以一种你不会手动压缩内容的方式实现。

如果你想确保你的词典的两个相同配置存储在同一个pickle文件中,你可以另外计算字典的字符串表示形式的哈希值。