numpy.average()
函数有一个权重选项,但是numpy.std()
没有。是否有人有解决方法?
numpy.average()
函数有一个权重选项,但是numpy.std()
没有。是否有人有解决方法?
def weighted_avg_and_std(values, weights):
"""
Return the weighted average and standard deviation.
They weights are in effect first normalized so that they
sum to 1 (and so they must not all be 0).
values, weights -- NumPy ndarrays with the same shape.
"""
average = numpy.average(values, weights=weights)
# Fast and numerically precise:
variance = numpy.average((values-average)**2, weights=weights)
return (average, math.sqrt(variance))
numpy.average
来计算方差? - user2357112numpy.std()
的加权版本。 - Eric O. Lebigotnp.sqrt()
可以工作,但由于 variance
是一个简单的(Numpy)浮点数(而不是NumPy数组),因此 math.sqrt()
更加明确和适当(如果这很重要,则通常更快)。 - Eric O. Lebigot%timeit math.sqrt(1.5)
每次循环61.6纳秒±0.661纳秒,%timeit np.sqrt(1.5)
每次循环554纳秒±14.5纳秒。 math.sqrt
几乎比 numpy
快了 10 倍(Python 3.10.9、numpy v1.23.3)。 - olq_plostatsmodels
有一个类可以轻松计算加权统计数据:statsmodels.stats.weightstats.DescrStatsW
。
假设这个数据集和权重如下:
import numpy as np
from statsmodels.stats.weightstats import DescrStatsW
array = np.array([1,2,1,2,1,2,1,3])
weights = np.ones_like(array)
weights[3] = 100
你需要初始化这个类(注意,你必须在此时传入校正因子和自由度):
weighted_stats = DescrStatsW(array, weights=weights, ddof=0)
然后你可以计算:
.mean
权重平均值:
>>> weighted_stats.mean
1.97196261682243
.std
表示加权标准差:
>>> weighted_stats.std
0.21434289609681711
.var
代表加权方差:
>>> weighted_stats.var
0.045942877107170932
>>> weighted_stats.std_mean
0.020818822467555047
如果你对标准误差和标准差之间的关系感兴趣,那么请注意:标准误差(当ddof == 0
时)被计算为加权标准差除以权重总和减1的平方根。(在 GitHub 上 statsmodels
版本0.9 的相应源代码)。
standard_error = standard_deviation / sqrt(sum(weights) - 1)
这里还有一个选项:
np.sqrt(np.cov(values, aweights=weights))
ddof=0
调用np.cov
,则两个计算在1D数据上是一致的。 - Eric O. Lebigot有一个非常好的例子是由gaborous提出的:
import pandas as pd
import numpy as np
# X is the dataset, as a Pandas' DataFrame
# Compute the weighted sample mean (fast, efficient and precise)
mean = mean = np.ma.average(X, axis=0, weights=weights)
# Convert to a Pandas' Series (it's just aesthetic and more
# ergonomic; no difference in computed values)
mean = pd.Series(mean, index=list(X.keys()))
xm = X-mean # xm = X diff to mean
# fill NaN with 0
# a variance of 0 is just void, but at least it keeps the other
# covariance's values computed correctly))
xm = xm.fillna(0)
# Compute the unbiased weighted sample covariance
sigma2 = 1./(w.sum()-1) * xm.mul(w, axis=0).T.dot(xm);
这是对“样本”或“无偏”标准差的后续讨论,涉及到“频率权重”。因为“加权样本标准差python”在谷歌搜索中会导向这篇文章。
def frequency_sample_std_dev(X, n):
"""
Sample standard deviation for X and n,
where X[i] is the quantity each person in group i has,
and n[i] is the number of people in group i.
See Equation 6.4 of:
Montgomery, Douglas, C. and George C. Runger. Applied Statistics
and Probability for Engineers, Enhanced eText. Available from:
WileyPLUS, (7th Edition). Wiley Global Education US, 2018.
"""
n_groups = len(n)
n_people = sum(n)
lhs_numerator = sum([ni*Xi**2 for Xi, ni in zip(X, n)])
rhs_numerator = sum([Xi*ni for Xi, ni in zip(X,n)])**2/n_people
denominator = n_people-1
var = (lhs_numerator - rhs_numerator) / denominator
std = sqrt(var)
return std
或者按照@Eric的回答进行修改:
def weighted_sample_avg_std(values, weights):
"""
Return the weighted average and weighted sample standard deviation.
values, weights -- Numpy ndarrays with the same shape.
Assumes that weights contains only integers (e.g. how many samples in each group).
See also https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Frequency_weights
"""
average = np.average(values, weights=weights)
variance = np.average((values-average)**2, weights=weights)
variance = variance*sum(weights)/(sum(weights)-1)
return (average, sqrt(variance))
print(weighted_sample_avg_std(X, n))
weighted_sample_avg_std()
,在第三行中你提到方差公式的第二部分时,方差不应该乘以总和比率,而是应该乘以非零权重数的比率(https://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf)。 - DouglasCoenen我刚刚在寻找一个API等同于numpy的 np.std
功能,同时也允许设置 axis
参数:
(我只在两个维度上进行了测试,如果有任何错误,请随时改进。)
def std(values, weights=None, axis=None):
"""
Return the weighted standard deviation.
axis -- the axis for std calculation
values, weights -- Numpy ndarrays with the same shape on the according axis.
"""
average = np.expand_dims(np.average(values, weights=weights, axis=axis), axis=axis)
# Fast and numerically precise:
variance = np.average((values-average)**2, weights=weights, axis=axis)
return np.sqrt(variance)
感谢Eric O Lebigot提供原始答案。