Среднее значение и стандартное отклонение очень большого набора данных
Мне интересно, есть ли алгоритм, который вычисляет среднее значение и стандартное отклонение несвязанного набора данных.
Например, я контролирую измеренное значение, скажем, электрический ток. Я хотел бы иметь среднее значение всех исторических данных. Когда приходит новое значение, обновляйте среднее значение и stdev? Поскольку данные слишком велики для хранения, я надеюсь, что он может просто обновить среднее значение и stdev "на лету", не сохраняя данные.
Четные данные сохраняются, стандартный способ (d1 +... + dn)/n не работает, сумма выдувает представление данных.
I через сумму (d1/n + d2/n +... d3/n), если n является hugh, ошибка слишком велика и накапливается. Кроме того, в этом случае n несвязано.
Количество данных определенно несвязано, когда оно приходит, требуется обновить значение.
Кто-нибудь знает, есть ли для этого алгоритм?
Ответы
Ответ 1
[изменился вопрос? возможно, я только прочитал начало. я обновил/отредактировал, чтобы дать лучший ответ:]
нет идеального решения (в постоянной памяти), о котором я знаю, но могу дать различные подходы.
во-первых, для базового расчета вам нужна сумма всех значений (sum_x
), сумма их квадратов (sum_x2
) и общее количество (n
). то:
mean = sum_x / n
stdev = sqrt( sum_x2/n - mean^2 )
и все эти значения (sum_x
, sum_x2
, n
) могут быть обновлены из потока.
проблема (как вы говорите) имеет дело с переполнением и/или ограниченной точностью. вы можете увидеть это, если считаете с плавающей точкой, когда sum_x2
настолько велико, что внутреннее представление не включает значения величины одного квадрата.
простой способ избежать этой проблемы - использовать точную арифметику, но это будет все более медленным (а также использует память O (log (n)).
другой способ, который может помочь, - нормализовать значения - если вы знаете, что значения обычно X
, тогда вы можете делать вычисления на x - X
, что делает суммы меньше (очевидно, вы затем добавляете X
в среднее значение!), что помогает отложить точку, в которой точность теряется (и может/должна быть объединена с любым другим способом здесь - при биннинге, например, вы можете использовать среднее значение предыдущего бина). см. этот алгоритм (метод knuth) для того, чтобы сделать это постепенно.
если вы не возражаете против стоимости памяти (небольшого постоянного коэффициента) O (n), вы можете перезапустить все значения n
(например, миллион - умнее все равно было бы адаптировать это значение, обнаружив, что точность слишком низкая) сохраняя предыдущее среднее значение и stdev, а затем объединяются для конечного результата (так что ваше среднее значение является соответствующим образом взвешенным по сравнению с недавним текущим итогом и старыми значениями бит).
подход биннинга, вероятно, может быть обобщен на несколько уровней (вы начинаете биннирование бункеров) и сократите использование памяти O (log (n)), но я не разработал детали.
наконец, более практичным решением, вероятно, будет сделать первоначальный подход, скажем, для 1000 значений, а затем начать новую сумму параллельно. вы можете отобразить средневзвешенное значение двух и, после еще 1000 значений, отбросить старые суммы (после постепенного уменьшения их веса) и начать новый набор. поэтому вы всегда имеете два набора сумм и отображаете средневзвешенное значение между ними, которое дает непрерывные данные, которые отражают последние 1000 значений (только). в некоторых случаях это будет достаточно хорошо, я думаю (это не точное значение, поскольку оно только для "последних" данных, но оно гладкое и репрезентативное и использует фиксированный объем памяти).
ps, что-то, что произошло со мной позже - действительно, делать это "навсегда" не имеет никакого смысла, так как вы доберетесь до точки, в которой значения абсолютно доминируют старые данные. было бы лучше использовать "текущее среднее", которое уменьшает вес до старых значений. см. например https://en.wikipedia.org/wiki/Moving_average - однако я не знаю об общем эквиваленте stdev.
Ответ 2
Интересный вопрос.
Сначала обсудим среднее значение, хотя бы потому, что оно немного проще.
Вы правы относительно ошибки округления в общей сумме. Это убьет вашу точность для достаточно большого набора данных. Вы хотите предварительно сохранить данные, в совокупности с небольшими данными; но, конечно, это невозможно в вашем случае. Тем не менее, вы можете получить большую часть преимуществ отсортированных данных, сохранив несколько текущих итогов.
Концептуальный пример: C- или С++-стиль:
const double max_small = 0.001;
const double max_medium = 1000.0;
double total_small;
double total_medium;
double total_large;
while(true) {
const double datum = get_datum(); // (Use here whatever function you use to get a datum.)
if (!is_datum_valid()) break;
if (abs(datum) <= max_small) total_small += datum;
else if (abs(datum) <= max_medium) total_medium += datum;
else total_large += datum;
}
double total = 0.0;
total += total_small;
total += total_medium;
total += total_large;
В реалистичном коде вы, вероятно, будете хранить более трех текущих итогов - и, конечно же, вы также будете вести итоговые значения квадратов данных, но приведенный выше пример передает идею. Вы можете заполнить детали.
Кроме того, приспосабливая идею @andrewcooke, вы можете расширить цикл таким образом:
while(true) {
const double datum = get_datum();
if (!is_datum_valid()) break;
if (abs(datum) <= max_small) {
total_small += datum;
if (abs(total_small) > max_medium) {
total_large += total_small;
total_small = 0.0;
}
}
else if (abs(datum) <= max_medium) total_medium += datum;
else total_large += datum;
}
Снова вы можете заполнить детали. Удачи.
ПРИЛОЖЕНИЕ: ПРАКТИЧЕСКИЙ РАСЧЕТ СТАНДАРТНОГО ОТКЛОНЕНИЯ
В различных комментариях обсуждался хороший вопрос о том, как рассчитывать стандартное отклонение без предварительного знания среднего. К счастью, известно, что трюк вычисляет стандартное отклонение так. Я подготовил две страницы заметок, которые объясняют трюк здесь (в формате PDF).
Во всяком случае, все, что необходимо для включения стандартного отклонения между текущей статистикой, заключается в сумме не только данных, но и квадратов данных; и, конечно, квадраты можно суммировать так же, как сами данные, следуя той же схеме, что и в коде выше.
Ответ 3
<удаp > Нет.Забастовкa >
(Я думал: нет, но я оказался ошибочным).
Вы можете переносить сумму и счетчик, чтобы
sum(i)=500, count(i)=50, => avg:=10
next value = 20
sum=520, count=51 => avg:= 10.19
но stddev не может быть построен таким образом. Вы должны произвести дельта для каждого значения к новому среднему значению и квадратировать их, и только после этого вы делите на N.
Забастовкa > Однако: Вопрос в том, какие ценности есть (с математической точки зрения - держитесь подальше от физики!:)). В нормальных условиях я не ожидал, что значения изменится после 2000 элементов. Кроме того, может быть сомнительным, чтобы в первую очередь создавать среднее и stddev.
И для 2000 элементов должно быть возможно быстро вычислить значение.
Возможно, вы можете использовать буфер и всегда вычислять avg и stddev для последних значений 2000 значений каждые 2000 значений. Является ли это заведомо значимыми данными - это то, что вам нужно решить.
Мы не можем продолжать наше обсуждение в чате так хорошо,...
потому что он не уточнил уценку. Поэтому я использую свой пост, чтобы уточнить свою позицию, которая распространена по комментариям с помощью thb, в основном, но, судя по всему, andrew полагает, что скользящий подсчет stddev тоже.
Вот широкая таблица, чтобы сделать расчет очевидным и легким в использовании. Столбцы:
- i: текущий индекс. Сначала вычисляем значения 1-3, затем для значений 1-5.
- x (i) - данные, произвольно выбранные мной. 3,4,5 и 4,6
- сумма - это просто то, что они суммируют. Интересный - последний для группы: 12 и 22. Примечание: Мы не берем сумму для трех значений и 2 значений, но для первых 3, а затем для первых 5.
- Среднее значение равно 12/3 или 22/5. Среднее значение avg можно рассчитать, если вы знаете я и сумму.
sum(i+1) = (sum (i)+x(i))/i+1
Пока нет споров.
- Чтобы вычислить stddev., мы должны принять разницу для каждого значения в avg и квадратировать его (тем самым теряя знак, что еще недействило бы разницу - оно всегда было бы 0). Второй эффект заключается в том, что несколько больших расстояний приводят к большему числу stddev, чем к малым расстояниям. Расстояние
(1,1,-1,-1)=> 4*1² = 4.
Напротив: (2,-2)=> 2² + -2² = 4+4 = 8
. Первый столбец предназначен для трех значений, второй для 5 значений (для выполнения расчета).
- Следующий столбец (последний) ² выполняет возведение в квадрат.
- Подведите итоги
- разделите на n-1
- возьмите квадратный корень
![spreadsheed with calculation (oocalc screenshot)]()
Может быть, мы можем согласиться, что это правильный способ вычисления stddev. Теперь вопрос заключается в том, как вычислить его, если вы знаете полную строку 3 (кроме x (3) = 5), и теперь вы получаете два отдельных значения (4, 6), как показано на листе, но не зная (x ( i) для я = 1, 2, 3.
Мое требование не удалось: вы можете.
Хорошо - Пытался использовать формулу.
ð² = 1/(N-1) (Сумма (x i ²) - 1/N (Сумма (x i)) ²)
Итак, для 4 значений я получаю
- N = 5
- sum (x i) = 22
- sum (x i ²) = 102
Вставляется в формулу:
ð² = 1/(N-1) (Sum (x<sub>i</sub>²) - 1/N (Sum (x<sub>i</sub>))²)
ð² = 1/4 (102 - 1/5 (22²))
ð² = 1/4 (102 - 1/5 (484))
ð² = 1/4 (102 - 96.8)
ð² = 1/4 (5.2)
ð² = 1.3
ð = 1.140
Мой результат был 1,14, ваш 1,14. Итак, есть ярлык. Очень интересно - я все еще удивлен.
Ответ 4
Фактически даже при меньших наборах данных при вычислении стандартного отклонения вы должны не вычислять сумму квадратов. Эта проблема называется катастрофическая аннулирование (ссылка на Википедию).
В Википедии также есть две статьи, которые помогут вам избавиться от этой проблемы:
- алгоритм суммирования Kahan, который имеет перенос, чтобы избежать систематической ошибки при суммировании большого количества очень малых значений (например, при суммировании всех x/n)
- Алгоритмы вычисления отклонения, в частности, "онлайн" версия должна соответствовать большим наборам данных. Он будет постепенно обновлять среднее значение для каждого наблюдения, поэтому значение остается в масштабе ваших данных! Возможно, вам придется использовать версию более высокого порядка для дисперсии, потому что первый онлайн-алгоритм по-прежнему вычисляет суммы квадратов-отклонений от среднего значения, поэтому для больших n это может привести к снижению вашего диапазона значений. M2 в версии более высокого порядка должен содержать среднеквадратичное отклонение от среднего значения, которое находится в масштабе вашего вывода.
Это, вероятно, одна из наиболее распространенных проблем с наивными статистическими вычислениями.
Обратите внимание, что проблема не возникает, когда среднее значение остается вокруг 0, намного меньше, чем дисперсия.