У меня есть список списков чисел, например:
[0] (0.01, 0.01, 0.02, 0.04, 0.03)
[1] (0.00, 0.02, 0.02, 0.03, 0.02)
[2] (0.01, 0.02, 0.02, 0.03, 0.02)
...
[n] (0.01, 0.00, 0.01, 0.05, 0.03)
То, что я хотел бы сделать, - это эффективное вычисление среднего и стандартного отклонения для каждого индекса списка по всем элементам массива.
Чтобы сделать среднее значение, я прошел цикл по массиву и суммировал значение по заданному индексу списка. В конце я делю каждое значение в своем "списке средних значений" на n
.
Чтобы выполнить стандартное отклонение, я снова прокручиваюсь, теперь, когда у меня есть среднее значение.
Я хотел бы избежать прохождения массива дважды, один раз для среднего, а затем один раз для SD (после того, как я имею в виду).
Есть ли эффективный метод для вычисления обоих значений, только один раз через массив? Любой код в интерпретируемом языке (например, Perl или Python) или псевдокод в порядке.
Ответ заключается в использовании алгоритма Welford, который очень четко определен после "наивных методов" в:
Он более численно устойчив, чем двухпроходная или онлайн простая сумма квадратов коллекционеров, предложенная в других ответах. Устойчивость действительно имеет значение, когда у вас много значений, которые близки друг к другу, поскольку они приводят к так называемому катастрофическому отмене в литература с плавающей точкой.
Вы также можете освежить разницу между делением на количество выборок (N) и N-1 в вычислении дисперсии (отклонение в квадрате). Разделение на N-1 приводит к несмещенной оценке дисперсии по выборке, тогда как деление на N в среднем недооценивает дисперсию (поскольку оно не учитывает дисперсию между средним значением выборки и истинным средним значением).
Я написал две записи в блоге по этой теме, которые приводятся в более подробной информации, в том числе о том, как удалить предыдущие значения в Интернете:
Вы также можете взглянуть на мой Java-инструмент; тесты javadoc, source и unit все онлайн:
Основным ответом является накопление суммы как x (назовем его sum_x1), так и x 2 (назовите его 'sum_x2') по ходу. Значение стандартного отклонения:
stdev = sqrt((sum_x2 / n) - (mean * mean))
где
mean = sum_x / n
Это стандартное отклонение выборки; вы получаете стандартное отклонение населения, используя "n" вместо "n - 1" в качестве делителя.
Возможно, вам придется беспокоиться о численной стабильности при разнице между двумя большими числами, если вы имеете дело с большими образцами. Перейдите к внешним ссылкам в других ответах (Wikipedia и т.д.) Для получения дополнительной информации.
Возможно, не то, что вы просили, но... Если вы используете массив numpy, он будет эффективно работать для вас:
from numpy import array
nums = array(((0.01, 0.01, 0.02, 0.04, 0.03),
(0.00, 0.02, 0.02, 0.03, 0.02),
(0.01, 0.02, 0.02, 0.03, 0.02),
(0.01, 0.00, 0.01, 0.05, 0.03)))
print nums.std(axis=1)
# [ 0.0116619 0.00979796 0.00632456 0.01788854]
print nums.mean(axis=1)
# [ 0.022 0.018 0.02 0.02 ]
Кстати, в этом блоге есть интересная дискуссия и комментарии к однопроходным методам вычисления средств и отклонений:
Вот буквальный чистый перевод Python реализации алгоритма Welford из http://www.johndcook.com/standard_deviation.html:
https://github.com/liyanage/python-modules/blob/master/running_stats.py
class RunningStats:
def __init__(self):
self.n = 0
self.old_m = 0
self.new_m = 0
self.old_s = 0
self.new_s = 0
def clear(self):
self.n = 0
def push(self, x):
self.n += 1
if self.n == 1:
self.old_m = self.new_m = x
self.old_s = 0
else:
self.new_m = self.old_m + (x - self.old_m) / self.n
self.new_s = self.old_s + (x - self.old_m) * (x - self.new_m)
self.old_m = self.new_m
self.old_s = self.new_s
def mean(self):
return self.new_m if self.n else 0.0
def variance(self):
return self.new_s / (self.n - 1) if self.n > 1 else 0.0
def standard_deviation(self):
return math.sqrt(self.variance())
Использование:
rs = RunningStats()
rs.push(17.0);
rs.push(19.0);
rs.push(24.0);
mean = rs.mean();
variance = rs.variance();
stdev = rs.standard_deviation();
Модуль Python runstats Module предназначен именно для этого. Установить runstats из PyPI:
pip install runstats
Резюме runstats могут вызывать среднее, дисперсию, стандартное отклонение, асимметрию и эксцесс за один проход данных. Мы можем использовать это для создания вашей "бегущей" версии.
from runstats import Statistics
stats = [Statistics() for num in range(len(data[0]))]
for row in data:
for index, val in enumerate(row):
stats[index].push(val)
for index, stat in enumerate(stats):
print 'Index', index, 'mean:', stat.mean()
print 'Index', index, 'standard deviation:', stat.stddev()
Статистические сводки основаны на методе Кнута и Велфорда для вычисления стандартного отклонения за один проход, как описано в Art of Computer Programming, Vol. 2, p. 232, 3-е издание. Преимущество этого - это численно стабильные и точные результаты.
Отказ от ответственности: Я являюсь автором модуля runstats Python.
Statistics
была метод .pop
чтобы можно было также рассчитать скользящую статистику.
runstats
не поддерживает внутренний список значений, поэтому я не уверен, что это возможно. Но запросы на тягу приветствуются.
Посмотрите PDL (произносится как "piddle!" ).
Это язык данных Perl, который предназначен для высокоточной математики и научных вычислений.
Вот пример использования ваших цифр....
use strict;
use warnings;
use PDL;
my $figs = pdl [
[0.01, 0.01, 0.02, 0.04, 0.03],
[0.00, 0.02, 0.02, 0.03, 0.02],
[0.01, 0.02, 0.02, 0.03, 0.02],
[0.01, 0.00, 0.01, 0.05, 0.03],
];
my ( $mean, $prms, $median, $min, $max, $adev, $rms ) = statsover( $figs );
say "Mean scores: ", $mean;
say "Std dev? (adev): ", $adev;
say "Std dev? (prms): ", $prms;
say "Std dev? (rms): ", $rms;
Что производит:
Mean scores: [0.022 0.018 0.02 0.02]
Std dev? (adev): [0.0104 0.0072 0.004 0.016]
Std dev? (prms): [0.013038405 0.010954451 0.0070710678 0.02]
Std dev? (rms): [0.011661904 0.009797959 0.0063245553 0.017888544]
Посмотрите PDL:: Primitive для получения дополнительной информации о функции statsover. Это, по-видимому, свидетельствует о том, что ADEV является "стандартным отклонением".
Однако это может быть PRMS (что показывает Sinan Statistics:: Descriptive example) или RMS (что показывает пример NumPy). Я думаю, что один из этих трех должен быть прав: -)
Для получения дополнительной информации о PDL посмотрите:
Statistics::Descriptive - очень приличный модуль Perl для этих типов вычислений:
#!/usr/bin/perl
use strict; use warnings;
use Statistics::Descriptive qw( :all );
my $data = [
[ 0.01, 0.01, 0.02, 0.04, 0.03 ],
[ 0.00, 0.02, 0.02, 0.03, 0.02 ],
[ 0.01, 0.02, 0.02, 0.03, 0.02 ],
[ 0.01, 0.00, 0.01, 0.05, 0.03 ],
];
my $stat = Statistics::Descriptive::Full->new;
# You also have the option of using sparse data structures
for my $ref ( @$data ) {
$stat->add_data( @$ref );
printf "Running mean: %f\n", $stat->mean;
printf "Running stdev: %f\n", $stat->standard_deviation;
}
__END__
Вывод:
C:\Temp> g
Running mean: 0.022000
Running stdev: 0.013038
Running mean: 0.020000
Running stdev: 0.011547
Running mean: 0.020000
Running stdev: 0.010000
Running mean: 0.020000
Running stdev: 0.012566
Насколько велик ваш массив? Если это не будет длинные элементы, не беспокойтесь о том, чтобы прокручивать его дважды. Код прост и легко протестирован.
Моим преимуществом было бы использовать расширение numpy, чтобы преобразовать массив массивов в двумерный массив numpy и получить стандартное отклонение:
>>> x = [ [ 1, 2, 4, 3, 4, 5 ], [ 3, 4, 5, 6, 7, 8 ] ] * 10
>>> import numpy
>>> a = numpy.array(x)
>>> a.std(axis=0)
array([ 1. , 1. , 0.5, 1.5, 1.5, 1.5])
>>> a.mean(axis=0)
array([ 2. , 3. , 4.5, 4.5, 5.5, 6.5])
Если это не вариант и вам нужно чистое решение Python, продолжайте читать...
Если ваш массив
x = [
[ 1, 2, 4, 3, 4, 5 ],
[ 3, 4, 5, 6, 7, 8 ],
....
]
Тогда стандартное отклонение:
d = len(x[0])
n = len(x)
sum_x = [ sum(v[i] for v in x) for i in range(d) ]
sum_x2 = [ sum(v[i]**2 for v in x) for i in range(d) ]
std_dev = [ sqrt((sx2 - sx**2)/N) for sx, sx2 in zip(sum_x, sum_x2) ]
Если вы настроены на циклическое перемещение массива только один раз, текущие суммы могут быть объединены.
sum_x = [ 0 ] * d
sum_x2 = [ 0 ] * d
for v in x:
for i, t in enumerate(v):
sum_x[i] += t
sum_x2[i] += t**2
Это не так элегантно, как решение для понимания списка выше.
Вы можете посмотреть статью Википедии на Стандартное отклонение, в частности раздел о методах расчета Rapid.
Также найдена статья, в которой используется Python, вы можете использовать код в ней без особых изменений: Subliminal Messages - Выполнение стандартных отклонений.
Я думаю, эта проблема вам поможет. Стандартное отклонение
Как говорится в следующем ответе: Предоставляет ли pandas/scipy/numpy функцию кумулятивного стандартного отклонения? Модуль Python Pandas содержит метод вычисления текущего или совокупного стандартного отклонения. Для этого вам нужно будет преобразовать ваши данные в фреймворк Pandas (или серию, если это 1D), но для этого есть функции.
n=int(raw_input("Enter no. of terms:"))
L=[]
for i in range (1,n+1):
x=float(raw_input("Enter term:"))
L.append(x)
sum=0
for i in range(n):
sum=sum+L[i]
avg=sum/n
sumdev=0
for j in range(n):
sumdev=sumdev+(L[j]-avg)**2
dev=(sumdev/n)**0.5
print "Standard deviation is", dev
Здесь "однострочный", распространяемый по нескольким строкам, в стиле функционального программирования:
def variance(data, opt=0):
return (lambda (m2, i, _): m2 / (opt + i - 1))(
reduce(
lambda (m2, i, avg), x:
(
m2 + (x - avg) ** 2 * i / (i + 1),
i + 1,
avg + (x - avg) / (i + 1)
),
data,
(0, 0, 0)))