现在我有许多不同的统计信息,其名称都附加在不同的数据框中。为了合并,我必须继续重写一个新的数据帧吗?有没有更有效的方法来做到这一点?
如果在合并时列的名称相同,pd.merge是否会更容易?
我是否必须以递归方式编写
pd.merge(left=something, right=somethingelse, left_on='name', right_on='site')
答案 0 :(得分:2)
您可以先创建所有数据框的列表,然后使用reduce
函数
# 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,需要复制left
和right
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_merge
或using_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
模块使其并行运行没有任何害处