如何在pandas中计算一行所有元素的加权和?

13
我有一个包含多列的pandas数据框。我想从行中的值和另一个名为weight的列向量数据框创建一个新的weighted_sum列。 weighted_sum应该具有以下值: row[weighted_sum] = row[col0]*weight[0] + row[col1]*weight[1] + row[col2]*weight[2] + ... 我发现函数sum(axis=1),但它不允许我与weight相乘。
编辑: 我稍微改变了一下。 weight长这样:
     0
col1 0.5
col2 0.3
col3 0.2

df 的样子是这样的:

col1 col2 col3
1.0  2.2  3.5
6.1  0.4  1.2

df*weight 返回一个充满 Nan 值的数据框。


你能展示一下你的 DataFrameweights 吗?不清楚为什么你会遇到这个问题。如果你只是想要行值与 weights 的点积,那么可以使用 ndarray.dot 方法:row.values.dot(weights.values) - Phillip Cloud
2个回答

14

问题在于你正在将一个大小和行索引不同的帧乘以另一个帧。以下是解决方案:

问题是你正在将不同大小和行索引的框架相乘。这里是解决方案:

In [121]: df = DataFrame([[1,2.2,3.5],[6.1,0.4,1.2]], columns=list('abc'))

In [122]: weight = DataFrame(Series([0.5, 0.3, 0.2], index=list('abc'), name=0))

In [123]: df
Out[123]:
           a          b          c
0       1.00       2.20       3.50
1       6.10       0.40       1.20

In [124]: weight
Out[124]:
           0
a       0.50
b       0.30
c       0.20

In [125]: df * weight
Out[125]:
           0          a          b          c
0        nan        nan        nan        nan
1        nan        nan        nan        nan
a        nan        nan        nan        nan
b        nan        nan        nan        nan
c        nan        nan        nan        nan

您可以通过以下两种方式访问该列:

In [126]: df * weight[0]
Out[126]:
           a          b          c
0       0.50       0.66       0.70
1       3.05       0.12       0.24

In [128]: (df * weight[0]).sum(1)
Out[128]:
0         1.86
1         3.41
dtype: float64

或使用dot获取另一个DataFrame

In [127]: df.dot(weight)
Out[127]:
           0
0       1.86
1       3.41

综上所述:

In [130]: df['weighted_sum'] = df.dot(weight)

In [131]: df
Out[131]:
           a          b          c  weighted_sum
0       1.00       2.20       3.50          1.86
1       6.10       0.40       1.20          3.41

这是每种方法的时间记录,使用一个更大的 DataFrame

In [145]: df = DataFrame(randn(10000000, 3), columns=list('abc'))
weight
In [146]: weight = DataFrame(Series([0.5, 0.3, 0.2], index=list('abc'), name=0))

In [147]: timeit df.dot(weight)
10 loops, best of 3: 57.5 ms per loop

In [148]: timeit (df * weight[0]).sum(1)
10 loops, best of 3: 125 ms per loop

对于一个宽的DataFrame

In [162]: df = DataFrame(randn(10000, 1000))

In [163]: weight = DataFrame(randn(1000, 1))

In [164]: timeit df.dot(weight)
100 loops, best of 3: 5.14 ms per loop

In [165]: timeit (df * weight[0]).sum(1)
10 loops, best of 3: 41.8 ms per loop
所以,dot更快更易读。 注意:如果你的数据包含任何NaN,则不应使用dot,而应该使用乘法和求和方法。dot不能处理NaN,因为它只是numpy.dot()的一个薄包装器(它不能处理NaN)。

原来 dotnumpy 一样对待 NaN:会直接将其抛回给你。 - Phillip Cloud
(df * weight).sum(1).head() != df.dot(weight).head() - Andy Hayden
你在使用哪一个“weight”?是“Series”还是“DataFrame”? - Phillip Cloud
显然,使用点运算符是这样做的方法(而不是乘以和求和)。 :) - Andy Hayden
除非你有我注意到的 NaN。:s - Phillip Cloud
显示剩余5条评论

10
假设 weights 是一个包含各列权重的 Series,你只需将它们相乘并进行求和:
In [11]: df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=['a', 'b', 'c'])

In [12]: weights = pd.Series([7, 8, 9], index=['a', 'b', 'c'])

In [13]: (df * weights)
Out[13]: 
    a   b   c
0   7  16  27
1  28  40  54

In [14]: (df * weights).sum(1)
Out[14]: 
0     50
1    122
dtype: int64

这种方法的好处在于它可以处理您不想加权的列:

In [21]: weights = pd.Series([7, 8], index=['a', 'b'])

In [22]: (df * weights)
Out[22]: 
    a   b   c
0   7  16 NaN
1  28  40 NaN

In [23]: (df * weights).sum(1)
Out[23]: 
0    23
1    68
dtype: float64

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接