1

您好我正在尝试计算一系列整数或浮点数的加权方差和加权标准差。我找到了这些链接:

http://math.tutorvista.com/statistics/standard-deviation.html#weighted-standard-deviation

http://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf(警告pdf)

到目前为止,这是我的模板功能。方差和标准差工作正常,但对于我的生活,我无法让加权版本与 pdf 底部的测试用例相匹配:

template <class T>
inline float    Mean( T samples[], int count )
{
    float   mean = 0.0f;

    if( count >= 1 )
    {
        for( int i = 0; i < count; i++ )
            mean += samples[i];

        mean /= (float) count;
    }

    return mean;
}

template <class T>
inline float    Variance( T samples[], int count )
{
    float   variance = 0.0f;

    if( count > 1 )
    {
        float   mean = 0.0f;

        for( int i = 0; i < count; i++ )
            mean += samples[i];

        mean /= (float) count;

        for( int i = 0; i < count; i++ )
        {
            float   sum = (float) samples[i] - mean;

            variance += sum*sum;
        }

        variance /= (float) count - 1.0f;
    }

    return variance;
}

template <class T>
inline float    StdDev( T samples[], int count )
{
    return sqrtf( Variance( samples, count ) );
}

template <class T>
inline float    VarianceWeighted( T samples[], T weights[], int count )
{
    float   varianceWeighted = 0.0f;

    if( count > 1 )
    {
        float   sumWeights = 0.0f, meanWeighted = 0.0f;
        int     numNonzero = 0;

        for( int i = 0; i < count; i++ )
        {
            meanWeighted += samples[i]*weights[i];
            sumWeights += weights[i];

            if( ((float) weights[i]) != 0.0f ) numNonzero++;
        }

        if( sumWeights != 0.0f && numNonzero > 1 )
        {
            meanWeighted /= sumWeights;

            for( int i = 0; i < count; i++ )
            {
                float   sum = samples[i] - meanWeighted;

                varianceWeighted += weights[i]*sum*sum;
            }

            varianceWeighted *= ((float) numNonzero)/((float) count*(numNonzero - 1.0f)*sumWeights);    // this should be right but isn't?!
        }
    }

    return varianceWeighted;
}

template <class T>
inline float    StdDevWeighted( T samples[], T weights[], int count )
{
    return sqrtf( VarianceWeighted( samples, weights, count ) );
}

测试用例:

int     samples[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23 };

printf( "%.2f\n", StdDev( samples, 9 ) );

int     weights[] = { 1, 1, 0, 0, 4, 1, 2, 1, 0 };

printf( "%.2f\n", StdDevWeighted( samples, weights, 9 ) );

结果:

7.46
1.94

应该:

7.46
5.82

我认为问题在于加权方差有几种不同的解释,我不知道哪个是标准的。我发现了这种变化:

http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Weighted_incremental_algorithm

template <class T>
inline float    VarianceWeighted( T samples[], T weights[], int count )
{
    float   varianceWeighted = 0.0f;

    if( count > 1 )
    {
        float   sumWeights = 0.0f, meanWeighted = 0.0f, m2 = 0.0f;

        for( int i = 0; i < count; i++ )
        {
            float   temp = weights[i] + sumWeights,
                    delta = samples[i] - meanWeighted,
                    r = delta*weights[i]/temp;

            meanWeighted += r;
            m2 += sumWeights*delta*r;   // Alternatively, m2 += weights[i] * delta * (samples[i]−meanWeighted)
            sumWeights = temp;
        }

        varianceWeighted = (m2/sumWeights)*((float) count/(count - 1));
    }

    return varianceWeighted;
}

结果:

7.46
5.64

我还尝试查看 boost 和 esutil 但它们并没有太大帮助:

http://www.boost.org/doc/libs/1_48_0/boost/accumulators/statistics/weighted_variance.hpp http://esutil.googlecode.com/svn-history/r269/trunk/esutil/stat/util.py

我不需要整个统计库,更重要的是,我想了解实现。

有人可以发布函数来正确计算这些吗?

如果您的函数可以一次性完成,则可以加分。

另外,有谁知道加权方差是否与具有重复值的普通方差给出相同的结果?例如,samples[] = { 1, 2, 3, 3 } 的方差是否与 samples[] = { 1, 2, 3 }, weights[] = { 1, 1, 2 } 的加权方差相同?

更新:这是我为探索问题而设置的谷歌文档电子表格。不幸的是,我的答案与 NIST pdf 相去甚远。我认为问题出在 unbias 步骤中,但我不知道如何解决它。

https://docs.google.com/spreadsheet/ccc?key=0ApzPh5nRin0ldGNNYjhCUTlWTks2TGJrZW4wQUcyZnc&usp=sharing

结果是加权方差为 3.77,这是我在 c++ 代码中得到的加权标准差 1.94 的平方。

我正在尝试在我的 Mac OS X 设置上安装 octave,以便我可以使用权重运行他们的 var() 函数,但是使用 brew 安装它需要很长时间。我现在很喜欢刮牦牛毛

4

3 回答 3

3
float mean(uint16_t* x, uint16_t n) {
    uint16_t sum_xi = 0;
    int i;
    for (i = 0; i < n; i++) {
        sum_xi += x[i];
    }
    return (float) sum_xi / n;
}

/**
 * http://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weigmean.pdf
 */
float weighted_mean(uint16_t* x, uint16_t* w, uint16_t n) {
    int sum_wixi = 0;
    int sum_wi = 0;
    int i;
    for (i = 0; i < n; i++) {
        sum_wixi += w[i] * x[i];
        sum_wi += w[i];
    }
    return (float) sum_wixi / (float) sum_wi;
}

float variance(uint16_t* x, uint16_t n) {
    float mean_x = mean(x, n);
    float dist, dist2;
    float sum_dist2 = 0;

    int i;
    for (i = 0; i < n; i++) {
        dist = x[i] - mean_x;
        dist2 = dist * dist;
        sum_dist2 += dist2;
    }

    return sum_dist2 / (n - 1);
}

/**
 * http://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weighvar.pdf
 */
float weighted_variance(uint16_t* x, uint16_t* w, uint16_t n) {
    float xw = weighted_mean(x, w, n);
    float dist, dist2;
    float sum_wi_times_dist2 = 0;
    int sum_wi = 0;
    int n_prime = 0;

    int i;
    for (i = 0; i < n; i++) {
        dist = x[i] - xw;
        dist2 = dist * dist;
        sum_wi_times_dist2 += w[i] * dist2;
        sum_wi += w[i];

        if (w[i] > 0)
            n_prime++;
    }

    if (n_prime > 1) {
        return sum_wi_times_dist2 / ((float) ((n_prime - 1) * sum_wi) / n_prime);
    } else {
        return 0.0f;
    }
}

/**
 * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Weighted_incremental_algorithm
 */
float weighted_incremental_variance(uint16_t* x, uint16_t* w, uint16_t n) {
    uint16_t sumweight = 0;
    float mean = 0;
    float M2 = 0;
    int n_prime = 0;

    uint16_t temp;
    float delta;
    float R;

    int i;
    for (i = 0; i < n; i++) {
        if (w[i] == 0)
            continue;

        temp = w[i] + sumweight;
        delta = x[i] - mean;
        R = delta * w[i] / temp;
        mean += R;
        M2 += sumweight * delta * R;
        sumweight = temp;

        n_prime++;
    }

    if (n_prime > 1) {
        float variance_n = M2 / sumweight;
        return variance_n * n_prime / (n_prime - 1);
    } else {
        return 0.0f;
    }
}

void main(void) {
    uint16_t n = 9;
    uint16_t x[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
    uint16_t w[] = { 1, 1, 0, 0,  4,  1,  2,  1,  0 };

    printf("%f\n", weighted_variance(x, w, n)); /* outputs: 33.900002 */
    printf("%f\n", weighted_incremental_variance(x, w, n)); /* outputs: 33.900005 */
}
于 2013-06-26T21:05:28.863 回答
0

解决方案

您不小心在方差更新项的分母中添加了一个额外的项“计数”。

使用下面的更正时,我得到了您的预期答案

5.82

仅供参考,当您进行代码审查时,一种处理此类事情的方法是进行“维度分析”。等式的“单位”是错误的。当它应该是 N 阶项时,您实际上是除以 N 阶平方项。

template <class T>
inline float    VarianceWeighted( T samples[], T weights[], int count )
{
    ...
            varianceWeighted *= ((float) numNonzero)/((float) count*(numNonzero - 1.0f)*sumWeights);    // this should be right but isn't?!
    ...
}

删除“计数”这一行应替换为

template <class T>
inline float    VarianceWeighted( T samples[], T weights[], int count )
{
    ...
            varianceWeighted *= ((float) numNonzero)/((float) (numNonzero - 1.0f)*sumWeights);  // removed count term
    ...
}
于 2014-07-24T00:32:12.973 回答
0

这是一个更短的版本,带有一个工作Demo

 #include <iostream>
 #include <vector>
 #include <boost/accumulators/accumulators.hpp>
 #include <boost/accumulators/statistics/stats.hpp>
 #include <boost/accumulators/statistics/weighted_variance.hpp>
 #include <boost/accumulators/statistics/variance.hpp>

 namespace ba = boost::accumulators;

 int main() {
     std::vector<double> numbers{2, 3, 5, 7, 11, 13, 17, 19, 23};
     std::vector<double> weights{1, 1, 0, 0,  4,  1,  2,  1, 0 };

     ba::accumulator_set<double, ba::stats<ba::tag::variance          >          > acc;
     ba::accumulator_set<double, ba::stats<ba::tag::weighted_variance > , double > acc_weighted;

     double n = numbers.size();
     double N = n;

     for(size_t i = 0 ; i<numbers.size() ; i++ ) {
         acc         ( numbers[i] );
         acc_weighted( numbers[i] ,   ba::weight = weights[i] );
         if(weights[i] == 0) {
             n=n-1;
         }
     };

     std::cout << "Sample Standard Deviation, s: "          << std::sqrt(ba::variance(acc)                  *N/(N-1))        << std::endl;
     std::cout << "Weighted Sample Standard Deviation, s: " << std::sqrt(ba::weighted_variance(acc_weighted)*n/(n-1))        << std::endl;
 }

请注意,n必须反映具有非零权重的样本数量,因此需要额外的n=n-1;行。

Sample Standard Deviation, s: 7.45729
Weighted Sample Standard Deviation, s: 5.82237
于 2019-02-18T20:47:00.837 回答