在Python中合并两个包含数百万行的表

时间:2013-01-30 21:51:56

标签: python join merge pandas pytables

我正在使用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已默认编入索引。

我该如何解决这个问题?

1 个答案:

答案 0 :(得分:16)

这是一个小伪代码,但我认为应该很快。

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

选择符合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)