pandas具有相同索引的两个数据帧的外积

时间:2017-07-24 16:08:34

标签: python pandas numpy

考虑以下数据框d1d1

d1 = pd.DataFrame([
    [1, 2, 3],
    [2, 3, 4],
    [3, 4, 5],
    [1, 2, 3],
    [2, 3, 4],
    [3, 4, 5]
], columns=list('ABC'))

d2 = pd.get_dummies(list('XYZZXY'))
d1

   A  B  C
0  1  2  3
1  2  3  4
2  3  4  5
3  1  2  3
4  2  3  4
5  3  4  5
d2

   X  Y  Z
0  1  0  0
1  0  1  0
2  0  0  1
3  0  0  1
4  1  0  0
5  0  1  0

我需要获取一个包含多索引列对象的新数据框,该对象包含来自d1d2

的每个列组合的乘积

到目前为止,我已经完成了这个......

from itertools import product
pd.concat({(x, y): d1[x] * d2[y] for x, y in product(d1, d2)}, axis=1)

   A        B        C      
   X  Y  Z  X  Y  Z  X  Y  Z
0  1  0  0  2  0  0  3  0  0
1  0  2  0  0  3  0  0  4  0
2  0  0  3  0  0  4  0  0  5
3  0  0  1  0  0  2  0  0  3
4  2  0  0  3  0  0  4  0  0
5  0  3  0  0  4  0  0  5  0

这种方法没有错。但我正在寻找评估的替代方案。

受Yakym Pirozhenko的启发

m, n = len(d1.columns), len(d2.columns)
lvl0 = np.repeat(np.arange(m), n)
lvl1 = np.tile(np.arange(n), m)
v1, v2 = d1.values, d2.values

pd.DataFrame(
    v1[:, lvl0] * v2[:, lvl1],
    d1.index,
    pd.MultiIndex.from_tuples(list(zip(d1.columns[lvl0], d2.columns[lvl1])))
)

然而,这是一个更笨拙的numpy广播实施,Divakar更好地覆盖了。

时序
所有的答案都是很好的答案,并展示了熊猫和numpy的不同方面。如果您认为它们有用且信息丰富,请考虑对它们进行投票。

%%timeit
m, n = len(d1.columns), len(d2.columns)
lvl0 = np.repeat(np.arange(m), n)
lvl1 = np.tile(np.arange(n), m)
v1, v2 = d1.values, d2.values

pd.DataFrame(
    v1[:, lvl0] * v2[:, lvl1],
    d1.index,
    pd.MultiIndex.from_tuples(list(zip(d1.columns[lvl0], d2.columns[lvl1])))
)

%%timeit 
vals = (d2.values[:,None,:] * d1.values[:,:,None]).reshape(d1.shape[0],-1)
cols = pd.MultiIndex.from_product([d1.columns, d2.columns])
pd.DataFrame(vals, columns=cols, index=d1.index)

%timeit d1.apply(lambda x: d2.mul(x, axis=0).stack()).unstack()
%timeit pd.concat({x : d2.mul(d1[x], axis=0) for x in d1.columns}, axis=1)
%timeit pd.concat({(x, y): d1[x] * d2[y] for x, y in product(d1, d2)}, axis=1)

1000 loops, best of 3: 663 µs per loop
1000 loops, best of 3: 624 µs per loop
100 loops, best of 3: 3.38 ms per loop
1000 loops, best of 3: 860 µs per loop
100 loops, best of 3: 2.01 ms per loop

4 个答案:

答案 0 :(得分:3)

这是一个矢量化版本。可能有更好的方法。

In [846]: pd.concat({x : d2.mul(d1[x], axis=0) for x in d1.columns}, axis=1)
Out[846]:
   A        B        C
   X  Y  Z  X  Y  Z  X  Y  Z
0  1  0  0  2  0  0  3  0  0
1  0  2  0  0  3  0  0  4  0
2  0  0  3  0  0  4  0  0  5
3  0  0  1  0  0  2  0  0  3
4  2  0  0  3  0  0  4  0  0
5  0  3  0  0  4  0  0  5  0

答案 1 :(得分:3)

这是NumPy broadcasting -

的一种方法
set

示例运行 -

vals = (d2.values[:,None,:] * d1.values[:,:,None]).reshape(d1.shape[0],-1)
cols = pd.MultiIndex.from_product([d1.columns, d2.columns])
df_out = pd.DataFrame(vals, columns=cols, index=d1.index)

答案 2 :(得分:3)

这是一个使用pandas stackunstack方法的单行程序。 “技巧”是使用stack,因此apply内每次计算的结果都是时间序列。然后使用unstack获取Multiindex表单。

d1.apply(lambda x: d2.mul(x, axis=0).stack()).unstack()

给出了:

     A              B              C          
     X    Y    Z    X    Y    Z    X    Y    Z
0  1.0  0.0  0.0  2.0  0.0  0.0  3.0  0.0  0.0
1  0.0  2.0  0.0  0.0  3.0  0.0  0.0  4.0  0.0
2  0.0  0.0  3.0  0.0  0.0  4.0  0.0  0.0  5.0
3  0.0  0.0  1.0  0.0  0.0  2.0  0.0  0.0  3.0
4  2.0  0.0  0.0  3.0  0.0  0.0  4.0  0.0  0.0
5  0.0  3.0  0.0  0.0  4.0  0.0  0.0  5.0  0.0

答案 3 :(得分:2)

你可以先得到多索引,用它来获取形状并直接乘法。

cols = pd.MultiIndex.from_tuples(
        [(c1, c2) for c1 in d1.columns for c2 in d2.columns])

a = d1.loc[:,cols.get_level_values(0)]
b = d2.loc[:,cols.get_level_values(1)]
a.columns = b.columns = cols

res = a * b