Python:以最有效的方式合并许多数据帧

时间:2017-08-14 20:01:00

标签: python pandas dataframe merge

现在我有许多不同的统计信息,其名称都附加在不同的数据框中。为了合并,我必须继续重写一个新的数据帧吗?有没有更有效的方法来做到这一点?

如果在合并时列的名称相同,pd.merge是否会更容易?

我是否必须以递归方式编写

pd.merge(left=something, right=somethingelse, left_on='name', right_on='site')

3 个答案:

答案 0 :(得分:2)

您可以先创建所有数据框的列表,然后使用reduce函数

获取rsult。
# create some data
columns = ['v1','v2','v3']
df1 = pd.DataFrame(np.random.randint(10, size=(3,3)),columns=columns)
df2 = pd.DataFrame(np.random.randint(10, size=(3,3)),columns=columns)
df3 = pd.DataFrame(np.random.randint(10, size=(3,3)),columns=columns)

dfs = [df1,df2,df3] # store in one list
df_merge = reduce(lambda  left,right: pd.merge(left,right,on=['v1'], how='outer'), dfs)

答案 1 :(得分:2)

如果要合并的列具有唯一值,则 有一种更快的方法:将要合并的列设置为索引,然后将所有数据框连接到pd.concat

import itertools as IT
import numpy as np
import functools

count = IT.count()
M, N, P = 100, 10, 4
dfs = [pd.DataFrame({
    next(count): np.random.randint(4, size=(N)) for j in range(P)}) for i in range(M)]

for i in range(M):
    dfs[i]['foo'] = np.random.choice(N, size=N, replace=False)

def using_merge(dfs):
    result = dfs[0]
    for df in dfs[1:]:
        result = pd.merge(result, df, on='foo')
    return result

def using_reduce(dfs):
    return functools.reduce(lambda  left,right: 
                            pd.merge(left, right, on=['foo']), dfs)

def using_concat(dfs):
    return pd.concat([df.set_index('foo') for df in dfs], axis=1)    

在循环中调用merge的问题是它返回一个中间DataFrame,需要复制leftright DataFrames中的值。在循环中完成时,会导致quadraticly increasing amounts of copying

当索引是唯一的时,可以使用pd.concat来避免二次复制 - dfs中所有DataFrame的值只需要复制一次到结果中。

以下是上述示例的微基准测试。

In [160]: %timeit using_concat(dfs)
10 loops, best of 3: 81.2 ms per loop

In [161]: %timeit using_merge(dfs)
1 loop, best of 3: 660 ms per loop

In [162]: %timeit using_reduce(dfs)
1 loop, best of 3: 659 ms per loop

速度优势是可变的 - 它取决于DataFrames的数量M, 合并。随着M的增加,速度也会增加 using_concat优于using_mergeusing_reduce的优势。但是,让我吧 再次强调pd.concat只能用作pd.merge的替代品 当列值唯一时 - 也就是说,合并是1对1,而不是 多对一或多对多。

如果要合并的列没有每个DataFrame的唯一值,那么 我没有看到比调用pd.merge更快的计算所需结果的方法 在循环中。

答案 2 :(得分:1)

首先,我们定义如何合并2个数据框

def merge_two(a,b, col):
    if b is None:
        return a
    return pd.merge(a,b,on=col)

接下来,我们希望合并为尽可能小的部分

from multiprocessing import Pool
from itertools import izip_longest as izip
from functools import partial

def merge_many(dfs, col):
    p = Pool(8) # number of cores
    merge = partial(merge_two, col=col)
    while len(dfs)>1:
        dfs = p.map(merge, izip(islice(dfs,0,None,2),islice(dfs,1,None,2)))
    return dfs[0]

由于数据帧是独立的,因此使用multiprocessing模块使其并行运行没有任何害处