看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
我们知道,NumPy 提供的 np.average()
可以支持加权平均计算,但今天的这个案例似乎有点特别,它的特别之处在于目标数列中有缺失值,缺失值不应用对应的权重。接下来,我们来看看源数据和需求细节。
有这样一份数据:
import pandas as pd
data = {'a':[7, 8, 7, 6, 5],
'b':[3, 2, 8, 4, 5],
'c':[6, None, 7, 6, 5],
'd':[9, 6, 3, None, 5],
'e':[7, 8, 7, 4, 7]}
factors = [0.1, 0.2, 0.15, 0.3, 0.25]
df = pd.DataFrame(data)
df
'''
a b c d e
0 7 3 6.0 9.0 7
1 8 2 NaN 6.0 8
2 7 8 7.0 3.0 7
3 6 4 6.0 NaN 4
4 5 5 5.0 5.0 7
'''
有 a、b、c、d、e 列数据,他们对应的权重是 factors 变量列表中所示的值,现在需要增加一列 f,计算出每行的加权平均数。其中两行有缺失值,在计算缺失值时,不进行权重计算,即计算索引 0 行时正常计算:
(7*10%+3*20%+6*15%+9*30%+7*25%)/(10%+20%+15%+30%+25%) = 6.65
计算索引 1 行时,排除缺失值及缺失值对应的权重:
(8*10%+2*20%+6*30%+8*25%)/(10%+20%+30%+25%) = 5.882352941
在 Excel 中实现时可以用以下公式:
G1:G6 = SUMPRODUCT(B2:F2,B$7:F$7)/SUMIFS($B$7:$F$7,B2:F2,"<>nan")
那么,Python 如何实现呢?
由于 np.average
提供的加权平均计算会忽略缺失值,得到以下结果:
df.apply(np.average, weights=factors, axis=1)
'''
0 6.65
1 NaN
2 6.00
3 NaN
4 5.50
dtype: float64
'''
这不是我们想要的,因此,我们就需要重新编写一个自己的加权平均算法函数。
我们先将权重转为 Series,方便它与各行的 Series 做矩阵运算:
# 取第二列
s = df.loc[1]
s
'''
a 8.0
b 2.0
c NaN
d 6.0
e 8.0
Name: 1, dtype: float64
'''
# 权重转为 Series
fa = pd.Series(factors, index=df.columns)
fa
'''
a 0.10
b 0.20
c 0.15
d 0.30
e 0.25
dtype: float64
'''
# 做矩阵运算
s*fa
'''
a 0.8
b 0.4
c NaN
d 1.8
e 2.0
dtype: float64
'''
# 计算和
(s*fa).sum()
# 5.0
根据计算方法,我们就按权重计算出了分母上的值,分子由于 c 为缺失值,对应的权重 0.15 不能参与计算,所在我们就要排除 fa 序列中的 c 值:
# 根据目标序列 s 排队权重序列对应该值
fa[s.notna()]
'''
a 0.10
b 0.20
d 0.30
e 0.25
dtype: float64
'''
# 加和
fa[s.notna()].sum()
# 0.8500000000000001
最后分子和分母完成除法计算得到结果:
(s*fa).sum()/fa[s.notna()].sum()
# 5.88235294117647
我们将以上单个行的计算过程写一个函数,然后用 apply 横向调用,同时还可以将权重序列作为变量传入。
def my_average(s, factors):
fa = pd.Series(factors, index=s.index)
# s.mul 乘法,可写为 (s*fa)
return s.mul(fa).sum()/fa[s.notna()].sum()
df.assign(f=df.apply(my_average, factors=factors, axis=1))
'''
a b c d e f
0 7 3 6.0 9.0 7 6.650000
1 8 2 NaN 6.0 8 5.882353
2 7 8 7.0 3.0 7 6.000000
3 6 4 6.0 NaN 4 4.714286
4 5 5 5.0 5.0 7 5.500000
'''
此问题来自于知乎上网友对我的提问。
(完)
更新时间:2024-08-18 15:37:33 标签:pandas python 平均数