添加数据帧并根据可用性划分结果

时间:2017-08-17 09:23:44

标签: python pandas dataframe pandas-groupby

我想添加两个我可以通过添加功能实现的数据帧。 现在,我想根据初始数据帧(df1,df2,df3)中是否存在相应的值来划分结果数据帧的每个值。例如。

df1 = pd.DataFrame([[1,2],[3,4]], index =['A','B'], columns = ['C','D'])
df2 = pd.DataFrame([[11,12], [13,14]], index = ['A','B'], columns = ['D','E'])
df3 = df1.add(df2, fill_value=0)

这会产生像

这样的df
     C   D     E
A  1.0  13  12.0
B  3.0  17  14.0

我需要一个df,如:

     C    D     E
A  1.0  6.5  12.0
B  3.0  8.5  14.0

因为在两个数据帧中都找到了D列,所以我将这些值除以2。 任何人都可以提供一个通用的解决方案,假设我需要添加2个以上的数据帧(因此分割因子也会发生变化)并且每个数据帧中都有超过100列。

3 个答案:

答案 0 :(得分:5)

我们可以在一个步骤中水平连接所有DF:

In [13]: df = pd.concat([df1,df2], axis=1).fillna(0)

这会产生:

In [15]: df
Out[15]:
   C  D   D   E
A  1  2  11  12
B  3  4  13  14

现在我们可以按列进行分组,计算平均值(mean):

In [14]: df.groupby(df.columns, axis=1).mean()
Out[14]:
     C    D     E
A  1.0  6.5  12.0
B  3.0  8.5  14.0

或者我们可以一步完成(感谢{{3}}):

In [60]: pd.concat([df1,df2], axis=1).fillna(0).groupby(level=0, axis=1).mean()
Out[60]:
     C    D     E
A  1.0  6.5  12.0
B  3.0  8.5  14.0

<强>定时:

In [38]: df1 = pd.concat([df1] * 10**5, ignore_index=True)

In [39]: df2 = pd.concat([df2] * 10**5, ignore_index=True)

In [40]: %%timeit
    ...: df = pd.concat([df1,df2], axis=1).fillna(0)
    ...: df.groupby(df.columns, axis=1).mean()
    ...:
63.4 ms ± 2.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [41]: %%timeit
    ...: s = pd.Series(np.concatenate([df1.columns, df2.columns])).value_counts()
    ...: df1.add(df2, fill_value=0).div(s)
    ...:
28.7 ms ± 712 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [42]: %%timeit
    ...: pd.concat([df1,df2]).mean(level = 0)
    ...:
65.5 ms ± 555 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [43]: df1.shape
Out[43]: (200000, 2)

In [44]: df2.shape
Out[44]: (200000, 2)

目前获胜者: @jezrael(28.7 ms ± 712 µs) - 恭喜!

答案 1 :(得分:4)

看起来你正试图计算一个平均值。如果您可以提供帮助,请不要使用数据框方法和单个列进行太多操作,因为它很慢。

df = pd.concat([df1,df2]) # concatenate all your dataframes together
df.mean(level = 0)

第二行计算沿垂直轴的平均值(默认为axis = 0),level = 0告诉pandas获取每个唯一索引的平均值。

答案 2 :(得分:4)

更快的解决方案除以列的大小:

s = pd.Series(np.concatenate([df1.columns, df2.columns])).value_counts()
print (s)
C    1
D    2
E    1
dtype: int64

df3 = df1.add(df2, fill_value=0).div(s)
print (df3)
     C    D     E
A  1.0  6.5  12.0
B  3.0  8.5  14.0

计时(提到的100列像OP一样):

np.random.seed(123)
N = 100000
df1 = pd.DataFrame(np.random.randint(10, size=(N, 100)))
df1.columns = 'col' + df1.columns.astype(str)
df2 = df1.mul(10)

#MaxU solution 
In [127]: %timeit (pd.concat([df1,df2], axis=1).fillna(0).groupby(level=0, axis=1).mean())
1 loop, best of 3: 952 ms per loop

#Ken Wei solution
In [128]: %timeit (pd.concat([df1,df2]).mean(level = 0))
1 loop, best of 3: 895 ms per loop

#jez solution
In [129]: %timeit (df1.add(df2, fill_value=0).div(pd.Series(np.concatenate([df1.columns, df2.columns])).value_counts()))
10 loops, best of 3: 161 ms per loop

更一般的解决方案:

如果有DataFrames的列表,可能会像:

df = df1.add(df2, fill_value=0).add(df3, fill_value=0)

但更好的是使用reduce

from functools import reduce

dfs = [df1,df2, df3]
s = pd.Series(np.concatenate([x.columns for x in dfs])).value_counts()
df5 = reduce(lambda x, y: x.add(y, fill_value=0), dfs).div(s)