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

14 投票
1 回答
26420 浏览
提问于 2025-04-17 14:15

我在用Python做数据分析。我有两个表,第一个表(我们叫它'A')有1000万行和10列,第二个表(叫'B')有7300万行和2列。它们有一列是共同的ID,我想根据这一列把两个表交叉合并。具体来说,我想要的是这两个表的内连接。

我无法把表B加载到内存中,不能用pandas的普通合并功能。我试着把表B分块读取,每次把一块和表A交叉合并,然后把这些交叉合并的结果拼接起来。这样速度还不错,但有时候会出问题,出现段错误……这可不太好。这个错误很难重现,但在两台不同的机器上(Mac OS X v10.6(雪豹)和UNIX,红帽Linux)都会发生。

最后,我尝试用Pandas和PyTables的组合,把表B写到磁盘上,然后遍历表A,从表B中选择匹配的行。这个方法可行,但速度比较慢。表B在pytables中默认已经建立了索引。

我该如何解决这个问题呢?

1 个回答

18

这段话有点像伪代码,但我觉得应该会很快。

这是一个简单的基于磁盘的合并方法,所有的数据表都在磁盘上。关键是你并不是在做选择,而是通过起始和结束位置来索引表,这样速度会很快。

如果你想从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)

撰写回答