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

14

我正在使用Python进行一些数据分析。我有两个表格,第一个(我们称之为'A')有1000万行和10列,第二个('B')有7300万行和2列。它们有一个共同的id列,我想基于该列对这两个表进行交集操作,特别地,我需要的是内连接(inner join)。

我无法将表B加载到内存中作为pandas dataframe来使用普通的pandas merge函数。我尝试按块读取表B的文件,将每个块与A相交,并将这些相交部分连接起来(即inner join的输出)。这在速度上还可以,但有时会出现问题并且会出现分段错误……不太好。这个错误很难复现,但它发生在两台不同的机器上(Mac OS X v10.6(Snow Leopard)和UNIX,Red Hat Linux)。

最后,我尝试了Pandas和PyTables的组合方式,将表格B写入磁盘,然后迭代表格A并从表格B中选择匹配的行。这种方法虽然可行,但速度较慢。Pytables中的表B已经默认索引过了。

我该如何解决这个问题?


1
为什么你不能在数据库中计算这个? - eLRuLL
我对数据库和SQL不是很熟悉。相比SQLite和其他数据库,PyTables似乎要快得多。我认为这可能是一个不错的选择。我会尝试使用SQLite,看看效果如何。 - user2027051
1个回答

18

这段代码有些类似于伪代码,但我认为应该非常快。

直接使用基于磁盘的合并方式,所有表都在磁盘上。关键是您不是在进行选择,而是通过开始/停止索引到表格中,这非常快速。

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

此外,如果您愿意,这是一个非常并行的问题(只是不要从多个进程将结果写入同一个文件。对于此类操作,pytables不安全)。

请参见这个答案,其中有评论说执行join操作实际上将是“inner” join。

对于您的merge_a_b操作,我认为您可以使用标准的pandas join,这很有效(当内存足够时)。

另一个选择(取决于A的大小)可能是将A分成两个部分(其索引相同),在第一个表格中使用较小的索引器(可能只使用单个列);而不是直接存储合并结果,存储行索引;稍后您可以提取所需的数据(有点像使用索引器和take)。请参见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)

非常感谢您的帮助,Jeff。我无法使用Pandas的多表查询选项,因为它们要求表A和B具有相同的行数。 - user2027051
我避免使用pandas的join/concat/merge选项,因为它们经常会出现"segmentation fault: 11"错误。我很难复制这个错误:a)它不在任何一个表的特定行上,b)它发生在不同的计算机/操作系统上。虽然它并不总是发生,但仍意味着我不能使用那条路线。到目前为止,我正在利用索引将B中的值填入A列中。这可能像是一种低效的"join"。欢迎任何其他想法。 - user2027051
多表查询仅适用于您的A表,仅当您需要大量仅在计算结束时才需要的列时才使用。您是否尝试过上述算法?我很想看看您得到了什么样的性能;另外,磁盘上的文件有多大? - Jeff
嗨,表A并没有很多列,只有8列。我仍然需要从表B中获取信息,这是瓶颈所在。磁盘上的文件大约为500 M(A表)和1.5 GB(B表)。我会尝试您的方法,看看速度有多快。 - user2027051
我最终使用了与你的解决方案非常相似的方法。表A可以保存在内存中。表A和B都按感兴趣的列进行索引。我每次从HDF5表B中获取100万行并进行交集运算。实际上速度非常快。如果表A不能安全地加载到内存中,那么你的代码将是解决方案。谢谢! - user2027051
1
@user2027051,我正在寻找这个解决方案。你能帮我分享一些示例代码吗? - avinash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接