13

我正在使用 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 已经被索引。

我该如何解决这个问题?

4

1 回答 1

17

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

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

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

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

有关如何执行联接操作实际上是“内部”联接的评论,请参阅此答案。

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

另一个选项(取决于 A 的“大”程度)可能是将 A 分成两部分(索引相同),在第一个表中使用较小的(可能使用单列);与其存储合并结果本身,不如存储行索引;稍后您可以提取所需的数据(有点像使用索引器并获取)。见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)
于 2013-01-31T03:24:35.147 回答