在 Python 中合并具有数百万行的两个表

Posted

技术标签:

【中文标题】在 Python 中合并具有数百万行的两个表【英文标题】:Merging two tables with millions of rows in Python 【发布时间】:2013-01-14 21:20:19 【问题描述】:

我正在使用 Python 进行一些数据分析。我有两个表,第一个(我们称之为'A')有 1000 万行和 10 列,第二个('B')有 7300 万行和 2 列。他们有 1 列具有共同的 id,我想根据该列将两个表相交。特别是我想要表的内部连接。

我无法将表 B 作为 pandas 数据框加载到内存中,以便在 pandas 上使用正常的合并功能。我尝试通过读取块上的表 B 的文件,将每个块与 A 相交并连接这些交叉点(内部连接的输出)。这在速度上是可以的,但时不时地给我带来问题并吐出分段错误......没有那么好。此错误很难重现,但它发生在两台不同的机器上(Mac OS X v10.6 (Snow Leopard) 和 UNIX、Red Hat Linux)。

我最终尝试了 Pandas 和 PyTables 的组合,方法是将表 B 写入磁盘,然后遍历表 A 并从表 B 中选择匹配的行。最后一个选项有效,但速度很慢。默认情况下,pytables 上的表 B 已被索引。

我该如何解决这个问题?

【问题讨论】:

为什么不能在数据库中计算? 我对数据库和 SQL 不是很熟悉。与 SQLite 和其他相比,PyTables 似乎相当快。我确实认为这将是一个很好的前进方向。我将尝试使用 SQLlite,看看效果如何。 【参考方案1】:

这有点伪代码,但我认为应该很快。

简单的基于磁盘的合并,所有表都在磁盘上。这 关键是你本身没有做选择,只是索引 通过启动/停止进入表格,速度非常快。

选择符合 B 中条件的行(使用 A 的 ID)不会 非常快,因为我认为它可能会将数据带入 Python 空间 而不是内核搜索(我不确定,但你可能想要 在内核优化部分中对 pytables.org 进行更多调查。 有一种方法可以判断它是否在内核中)。

另外,如果你能做到,这是一个非常平行的问题(只是不要写 结果从多个进程到同一个文件。 pytables 不是写安全的)。

请参阅this answer 以获取有关如何执行连接操作实际上是“内部”连接的评论。

对于您的 merge_a_b 操作,我认为您可以使用标准的 pandas join 这非常有效(在内存中时)。

另一个选项(取决于 A 的“大”程度)可能是将 A 分成 2 部分(索引相同),在第一个表中使用较小的(可能使用单列);与其存储合并结果本身,不如存储行索引;稍后您可以提取所需的数据(有点像使用索引器并获取)。见http://pandas.pydata.org/pandas-docs/stable/io.html#multiple-table-queries

A = HDFStore('A.h5')
B = HDFStore('B.h5')

nrows_a = A.get_storer('df').nrows
nrows_b = B.get_storer('df').nrows
a_chunk_size = 1000000
b_chunk_size = 1000000

def merge_a_b(a,b):
    # Function that returns an operation on passed
    # frames, a and b.
    # It could be a merge, join, concat, or other operation that
    # results in a single frame.


for a in xrange(int(nrows_a / a_chunk_size) + 1):

    a_start_i = a * a_chunk_size
    a_stop_i  = min((a + 1) * a_chunk_size, nrows_a)

    a = A.select('df', start = a_start_i, stop = a_stop_i)

    for b in xrange(int(nrows_b / b_chunk_size) + 1):

        b_start_i = b * b_chunk_size
        b_stop_i = min((b + 1) * b_chunk_size, nrows_b)

        b = B.select('df', start = b_start_i, stop = b_stop_i)

        # This is your result store
        m = merge_a_b(a, b)

        if len(m):
            store.append('df_result', m)

【讨论】:

感谢您的帮助杰夫。我不能使用 pandas 的多表查询选项,因为要求表 A 和 B 具有相同的行数。 我避免使用 pandas 的 join/concat/merge 选项,因为它们都会时不时地吐出“分段错误:11”。我很难重现这个错误:a)它不在任一表的特定行上,b)它发生在不同的计算机/操作系统上。它不会一直发生,但仍然意味着我不能使用那条路线。到目前为止,我正在使用索引来用来自 B 的值填充 A 上的列。这可能就像一个低效的“连接”。欢迎任何其他想法。 多表查询仅适用于您的 A 表,并且仅当您需要说有很多仅在计算结束时才需要的列时。您是否尝试过上述算法?有兴趣看看你得到什么样的表现;还有磁盘上的文件有多大? 嗨,表 A 没有很多列,只有 8 个。我仍然需要从表 B 获取信息,这是瓶颈。磁盘上的文件 A 大约 500 M,B 大约 1.5 GB。我会试试你的方法,看看它有多快。 @user2027051.. 我正在寻找这个解决方案。你能帮我分享任何示例代码吗?

以上是关于在 Python 中合并具有数百万行的两个表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 pandas 或 python 将具有数百万行的表从 PostgreSQL 复制到 Amazon Redshift

如何在 SQL Server 中更新具有数百万行的大表?

优化两个数百万行表之间的内部联接

mysql中数百万行的基于键的分区

在 MySQL 中计算特定日期之间数百万行的最佳方法

数百万行的数据库设计