Взвешенная дисперсия и взвешенное стандартное отклонение в C++

Привет, я пытаюсь рассчитать взвешенную дисперсию и взвешенное стандартное отклонение ряда целых чисел или чисел с плавающей запятой. Я нашел эти ссылки:

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

Мне не нужна целая библиотека статистики, и, что более важно, я хочу понять реализацию.

Может ли кто-нибудь опубликовать функции для их правильного расчета?

Бонусные баллы, если ваши функции могут сделать это за один проход.

Кроме того, кто-нибудь знает, дает ли взвешенная дисперсия тот же результат, что и обычная дисперсия с повторяющимися значениями? Например, будет ли дисперсия выборок [] = {1, 2, 3, 3} такой же, как взвешенная дисперсия выборок [] = {1, 2, 3}, весов [] = {1, 1, 2} ?

Обновление: вот таблица документов Google, которую я создал для изучения проблемы. К сожалению, мои ответы далеки от PDF-файла NIST. Я думаю, что проблема в шаге unbias, но я не вижу, как это исправить.

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

Результатом является взвешенная дисперсия 3,77, которая является квадратом взвешенного стандартного отклонения 1,94, полученного в моем коде на С++.

Я пытаюсь установить октаву в своей установке Mac OS X, чтобы я мог запускать их функцию var() с весами, но установка ее с помощью brew занимает целую вечность. Сейчас я увлечен бритьем яков.


person Zack Morris    schedule 26.05.2013    source источник
comment
Удалось ли вам получить неинкрементную версию кода, чтобы получить правильный результат?   -  person Oliver Charlesworth    schedule 26.05.2013
comment
Нет, пока это единственные алгоритмы, которые я пробовал. Я попробовал взвешенную дисперсию на wolframalpha.com для проверки тестовых случаев, но я не думаю, что она у них еще есть.   -  person Zack Morris    schedule 26.05.2013
comment
Хорошо, тогда похоже, что настоящая проблема заключается в том, чтобы найти определение взвешенной дисперсии. Возможно, этот вопрос лучше подходит для stats.stackexchange.com.   -  person Oliver Charlesworth    schedule 26.05.2013
comment
Хорошо спасибо. Вот еще одна ссылка, которую я нашел с тестом взвешенной дисперсии itl.nist.gov/div898/software/dataplot/refman2/ch2/weighvar.pdf они получают 33,9 для набора образцов, который я предоставил, поэтому взвешенное стандартное отклонение снова будет sqrt (33,9) = 5,82.   -  person Zack Morris    schedule 26.05.2013
comment
Связанный вопрос (без ответа): stats.stackexchange. ком/вопросы/51442/   -  person Zack Morris    schedule 26.05.2013


Ответы (2)


Решение

Вы случайно добавили дополнительный термин "количество" в знаменатель термина обновления отклонения.

При использовании исправления ниже я получаю ожидаемый ответ

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?!
    ...
}

После

Удалив "count", эту строку следует заменить на

template <class T>
inline float    VarianceWeighted( T samples[], T weights[], int count )
{
    ...
            varianceWeighted *= ((float) numNonzero)/((float) (numNonzero - 1.0f)*sumWeights);  // removed count term
    ...
}
person dpmcmlxxvi    schedule 24.07.2014

Вот гораздо более короткая версия с работающей демо :

 #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
person cosurgi    schedule 18.02.2019