使用Python ORM进行交叉表(列联表)?

3 投票
1 回答
1484 浏览
提问于 2025-04-16 13:30

任何在关系数据库中进行非常基础的数据统计分析的人,都需要计算交叉表,也叫做列联表(维基百科页面)。这些表格在你需要统计同时属于多个类别的项目时是必不可少的。比如:有多少顾客是女性并且喜欢巧克力?

Scipy 提供了一些方法来处理矩阵,使用的是一种叫做 histogram2d 的变体。但如果想进行有意义的统计分析,你需要一个包含变量名称的表格,这样你才能指定想要统计哪些变量。此外,这个方法还需要适用于其他类型的变量,而不仅仅是数字。实际上,数字统计是更复杂的,因为它需要将数据分组。R 自带一个叫做 table 的函数,这个函数可以很容易地移植到 Python 中。不过,我在标题中提到我想使用 ORM,为什么呢?因为交叉表的数据量通常比生成它所需的数据要小得多,你可以从数十亿条记录中计算出一个 2x2 的表格。我的意思是:在严肃的应用中,你不能把所有数据都加载到内存中并逐条处理。因此,你需要将表格设计转换成 SQL 查询,这样所有的统计工作就可以由数据库引擎来完成。而 ORM 则负责处理必要的 SQL 方言调整,这样你就可以在任何数据库后端上运行你的代码。

一个简单交叉表的 SQL 示例(使用 MySQL 方言)可以在 这里 找到。

现在我觉得我已经让你理解了这个问题,接下来是问题:这个功能在任何 Python ORM 中实现了吗?如果使用 SQLAlchemy 或 Django ORM,你会如何实现这个功能?

1 个回答

2

我不太喜欢自己回答自己的问题,但有时候我们真的等不及别人来帮忙。既然我找到了答案,而且这个答案还不错,我觉得有必要和大家分享一下。所以就来看看吧:

table = self.session.query(Table.var1, Table.var2, func.count(Table)).group_by(Table.var1, Table.var2).all()

这个代码会返回一个包含元组的列表,格式是(行,列,计数)。从这个列表中,你可以组装出你的列联表,并且如果需要的话,还可以计算边际总和。值得一提的是,计算这个表格花了0.28秒,而这个表格有296110条记录,var1和var2分别有5和90个级别。

接下来是一个小函数,用来组装和打印这个(二维)表格:

def pprint_table():
    colnames = list(set([i[1] for i in table]))
    rows = defaultdict(lambda:[0]*len(colnames))
    for r in table:
        rows[r[0]][colnames.index(r[1])] = r[2]
    print colnames, 'total'
    for rn, r in rows.items():
        print rn, r, sum(r)

撰写回答