【问题标题】:Pivoting SQLite table, setwise like SQL should be透视 SQLite 表,像 SQL 一样设置
【发布时间】:2009-12-02 19:57:35
【问题描述】:

我有一些数据。 224,000 行,在 SQLite 数据库中。我想从中提取时间序列信息以提供数据可视化工具。本质上,数据库中的每一行都是一个事件,它具有(除其他不严格相关的)自纪元以来以秒为单位的时间日期组和负责它的名称。我想提取每个名称在数据库中每周有多少事件。

这很简单:

SELECT COUNT(*), 
       name, 
       strf("%W:%Y", time, "unixepoch") 
  FROM events 
 GROUP BY strf("%W:%Y", time, "unixepoch"), name 
 ORDER BY time

我们得到大约六千行数据。

count          name        week:year  
23............ fudge.......23:2009  
etc...

但我不希望每个名字在每个星期都有一行 - 我希望每个名字有一行,每周有一个列,如下所示:

Name      23:2009       24:2009    25:2009  
fudge........23............6............19  
fish.........1.............0............12  
etc...

现在,监控过程已经运行了 69 周,唯一名称的计数为 502。很明显,我并不热衷于任何涉及硬编码所有列且更不用说行的解决方案。我对涉及迭代大量的任何事情都不感兴趣,比如使用 python 的 executemany(),但如果有必要我愿意接受它。 SQL 应该是按集合设置的,该死的。

【问题讨论】:

  • @yorksranter:您打算投票并接受答案吗?
  • 我没有 15 个声望点。
  • @yorkstranter:没关系,如果你真的喜欢代码,你可以在你的代表更高时重新访问 :-)

标签: python sql sqlite visualization


【解决方案1】:

在这种情况下,一个好的方法是不要将 SQL 推到它变得复杂且难以理解和维护的地步。让 SQL 做它方便的事情,并在 Python 中对查询结果进行后处理。

这是我编写的简单交叉表生成器的精简版。完整版提供行/列/总计。

您会注意到它具有内置的“分组依据”——最初的用例是用于汇总使用 Python 和 xlrd 从 Excel 文件中获得的数据。

您提供的row_keycol_key 不需要像示例中那样是字符串;它们可以是元组——例如(year, week) 在您的情况下 - 或者它们可能是整数 - 例如你有一个字符串列名到整数排序键的映射。

import sys

class CrossTab(object):

    def __init__(
        self,
        missing=0, # what to return for an empty cell. Alternatives: '', 0.0, None, 'NULL'
        ):
        self.missing = missing
        self.col_key_set = set()
        self.cell_dict = {}
        self.headings_OK = False

    def add_item(self, row_key, col_key, value):
        self.col_key_set.add(col_key)
        try:
            self.cell_dict[row_key][col_key] += value
        except KeyError:
            try:
                self.cell_dict[row_key][col_key] = value
            except KeyError:
                self.cell_dict[row_key] = {col_key: value}

    def _process_headings(self):
        if self.headings_OK:
            return
        self.row_headings = list(sorted(self.cell_dict.iterkeys()))
        self.col_headings = list(sorted(self.col_key_set))
        self.headings_OK = True

    def get_col_headings(self):
        self._process_headings()
        return self.col_headings

    def generate_row_info(self):
        self._process_headings()
        for row_key in self.row_headings:
            row_dict = self.cell_dict[row_key]
            row_vals = [row_dict.get(col_key, self.missing) for col_key in self.col_headings]
            yield row_key, row_vals

    def dump(self, f=None, header=None, footer='', ):
        if f is None:
            f = sys.stdout
        alist = self.__dict__.items()
        alist.sort()
        if header is not None:
            print >> f, header
        for attr, value in alist:
            print >> f, "%s: %r" % (attr, value)
        if footer is not None:
            print >> f, footer

if __name__ == "__main__":

    data = [
        ['Rob', 'Morn', 240],
        ['Rob', 'Aft',  300],
        ['Joe', 'Morn',  70],
        ['Joe', 'Aft',   80],
        ['Jill', 'Morn', 100],
        ['Jill', 'Aft',  150],
        ['Rob', 'Aft',   40],
        ['Rob', 'aft',    5],
        ['Dozy', 'Aft',   1],
        # Dozy doesn't show up till lunch-time
        ['Nemo', 'never', -1],
        ]
    NAME, TIME, AMOUNT = range(3)
    xlate_time = {'morn': "AM", "aft": "PM"}

    print
    ctab = CrossTab(missing=None, )
    # ctab.dump(header='=== after init ===')
    for s in data:
        ctab.add_item(
            row_key=s[NAME],
            col_key= xlate_time.get(s[TIME].lower(), "XXXX"),
            value=s[AMOUNT])
        # ctab.dump(header='=== after add_item ===')
    print ctab.get_col_headings()
    # ctab.dump(header='=== after get_col_headings ===')
    for x in ctab.generate_row_info():
        print x

输出:

['AM', 'PM', 'XXXX']
('Dozy', [None, 1, None])
('Jill', [100, 150, None])
('Joe', [70, 80, None])
('Nemo', [None, None, -1])
('Rob', [240, 345, None])

【讨论】:

    【解决方案2】:

    我会先做你的查询

    SELECT COUNT(*), 
           name, 
           strf("%W:%Y", time, "unixepoch") 
      FROM events 
     GROUP BY strf("%W:%Y", time, "unixepoch"), name 
     ORDER BY time
    

    然后用python进行后期处理。

    因此,您不必迭代超过 224,000 行,而是超过 6,000 行。您可以轻松地将这 6,000 行存储在内存中(用于使用 Python 进行处理)。我认为您也可以在内存中存储 224,000 行,但它需要更多的内存。

    但是:新版本的 sqlite 支持聚合函数 group_concat。也许你可以使用这个函数来使用 SQL 进行数据透视?我无法尝试,因为我使用的是旧版本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-07
      • 1970-01-01
      • 1970-01-01
      • 2021-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-16
      相关资源
      最近更新 更多