【问题标题】:Weighted standard deviation in NumPyNumPy 中的加权标准差
【发布时间】:2011-01-25 16:36:10
【问题描述】:

numpy.average() 有一个权重选项,但numpy.std() 没有。有人有解决方法的建议吗?

【问题讨论】:

标签: python numpy statsmodels standard-deviation weighted


【解决方案1】:

以下简短的“手动计算”怎么样?

def weighted_avg_and_std(values, weights):
    """
    Return the weighted average and standard deviation.

    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 作为方差?
  • 只是想指出这会产生偏差方差。对于小样本量,您可能需要重新缩放方差(在 sqrt 之前)以获得无偏方差。见en.wikipedia.org/wiki/…
  • 是的,无偏方差估计量会略有不同。这个答案给出了标准偏差,因为问题要求numpy.std() 的加权版本。
  • 感谢这个解决方案......但你为什么最后使用math.sqrt而不是np.sqrt
  • np.sqrt() 会起作用,但是因为variance 是一个简单的(Numpy)浮点数(而不是 NumPy 数组),math.sqrt() 更明确和合适(因此通常更快,如果这很重要)。
【解决方案2】:

statsmodels 中有一个类可以很容易地计算加权统计: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

你初始化类(注意你必须传入校正因子,此时的delta degrees of freedom):

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
    
  • .std_meanstandard error加权平均:

    >>> weighted_stats.std_mean  
    0.020818822467555047
    

    以防万一您对标准误差和标准偏差之间的关系感兴趣:标准误差(对于ddof == 0)计算为加权标准偏差除以权重总和的平方根减去1 (corresponding source for statsmodels version 0.9 on GitHub):

    standard_error = standard_deviation / sqrt(sum(weights) - 1)
    

【讨论】:

  • 要使用这种方法轻松计算加权变异系数,请参阅this answer
【解决方案3】:

这里还有一个选项:

np.sqrt(np.cov(values, aweights=weights))

【讨论】:

    【解决方案4】:

    在 numpy/scipy 中似乎还没有这样的功能,但是有一个 ticket 提出了这个附加功能。包括在那里,您会发现 Statistics.py 实现加权标准偏差。

    【讨论】:

      【解决方案5】:

      gaborous提出了一个很好的例子:

      import pandas as pd
      import numpy as np
      # X is the dataset, as a Pandas' DataFrame
      mean = mean = np.ma.average(X, axis=0, weights=weights) # Computing the 
      weighted sample mean (fast, efficient and precise)
      
      # 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
      xm = xm.fillna(0) # fill NaN with 0 (because anyway a variance of 0 is 
      just void, but at least it keeps the other covariance's values computed 
      correctly))
      sigma2 = 1./(w.sum()-1) * xm.mul(w, axis=0).T.dot(xm); # Compute the 
      unbiased weighted sample covariance
      

      Correct equation for weighted unbiased sample covariance, URL (version: 2016-06-28)

      【讨论】:

        【解决方案6】:

        frequency weights”意义上的“样本”或“无偏”标准偏差的后续行动,因为“加权样本标准偏差 python”Google 搜索导致了这篇文章:

        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(),在第三行,你有方差方程的第二部分,方差不应该乘以总和的比率,而是乘以非零数的比率权重 (itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf)。
        • 嗯..这是一个很好的观点。你介意建议编辑吗?我之前研究过这个(但在你发表评论之后),但实际的变化对我来说并不明显。
        猜你喜欢
        • 2013-05-21
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-01
        • 1970-01-01
        • 2017-03-04
        • 2020-04-15
        相关资源
        最近更新 更多