У меня есть массив списков чисел, например:
[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) или псевдокоде.
источник
Ответы:
Ответ - использовать алгоритм Велфорда, который очень четко определен после «наивных методов» в:
Он более стабилен в числовом отношении, чем двухпроходный или интерактивный сборщик простой суммы квадратов, предложенный в других ответах. Стабильность действительно имеет значение только тогда, когда у вас есть много значений, которые близки друг к другу, поскольку они приводят к так называемой « катастрофической отмене » в литературе с плавающей запятой.
Вы также можете освежить в памяти разницу между делением на количество выборок (N) и N-1 при расчете дисперсии (квадратичное отклонение). Деление на N-1 приводит к объективной оценке дисперсии по выборке, тогда как деление на N в среднем недооценивает дисперсию (поскольку не принимает во внимание дисперсию между средним по выборке и истинным средним).
Я написал две записи в блоге по этой теме, в которых подробно рассказывается, в том числе о том, как удалить предыдущие значения в Интернете:
Вы также можете взглянуть на мою реализацию Java; документация javadoc, исходный код и модульные тесты доступны онлайн:
stats.OnlineNormalEstimator
stats.OnlineNormalEstimator.java
test.unit.stats.OnlineNormalEstimatorTest.java
источник
Основной ответ - накапливать сумму как x (назовите это «сумма_x1»), так и x 2 (назовите это «сумма_x2») по мере продвижения. Тогда значение стандартного отклонения:
stdev = sqrt((sum_x2 / n) - (mean * mean))
где
mean = sum_x / n
Это стандартное отклонение выборки; вы получите стандартное отклонение совокупности, используя в качестве делителя «n» вместо «n - 1».
Возможно, вам придется побеспокоиться о числовой стабильности измерения разницы между двумя большими числами, если вы имеете дело с большими выборками. Перейдите к внешним ссылкам в других ответах (Википедия и т. Д.) Для получения дополнительной информации.
источник
int
в C для хранения суммы квадратов, вы столкнетесь с проблемами переполнения со значениями, которые вы перечисляете.Вот дословный перевод реализации алгоритма Велфорда на чистый Python с http://www.johndcook.com/standard_deviation.html :
https://github.com/liyanage/python-modules/blob/master/running_stats.py
import math 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() print(f'Mean: {mean}, Variance: {variance}, Std. Dev.: {stdev}')
источник
Возможно, это не то, о чем вы спрашивали, но ... Если вы используете массив 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 runstats предназначен именно для этого. Установите 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()
Сводные статистические данные основаны на методе Кнута и Велфорда для вычисления стандартного отклонения за один проход, как описано в «Искусство компьютерного программирования», том 2, стр. 232, 3-е издание. Преимущество этого - числовая стабильность и точность результатов.
Отказ от ответственности: я являюсь автором модуля Python runstats.
источник
Statistics
has для.pop
расчета скользящей статистики.runstats
не поддерживает внутренний список значений, поэтому я не уверен, что это возможно. Но запросы на включение приветствуются.Статистика :: 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
источник
Взгляните на PDL (произносится как «пиддл!»).
Это язык данных 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 для получения дополнительной информации о статусе. функции . Похоже, это наводит на мысль, что ADEV - это «стандартное отклонение».
Однако это может быть PRMS (что показано в примере Sinan Statistics :: Descriptive) или RMS (что показано в примере Ars NumPy). Думаю, один из этих трех должен быть прав ;-)
Для получения дополнительной информации о PDL посмотрите:
источник
Насколько велик ваш массив? Если это не миллионы элементов, не беспокойтесь о повторении цикла дважды. Код прост и легко тестируется.
Я бы предпочел использовать расширение numpy array maths для преобразования вашего массива массивов в numpy 2D-массив и напрямую получить стандартное отклонение:
>>> 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
Это не так элегантно, как решение для понимания списка выше.
источник
Вы можете посмотреть статью в Википедии о стандартном отклонении , в частности, раздел о методах быстрого расчета.
Я также нашел статью, в которой используется Python, вы сможете использовать код в ней без особых изменений: Подсознательные сообщения - выполнение стандартных отклонений .
источник
Думаю, этот вопрос вам поможет. Стандартное отклонение
источник
Вот "однострочный", разбросанный по нескольким строкам, в стиле функционального программирования:
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)))
источник
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
источник
Как описывает следующий ответ: Предоставляет ли pandas / scipy / numpy функцию совокупного стандартного отклонения? Модуль Python Pandas содержит метод вычисления текущего или совокупного стандартного отклонения . Для этого вам нужно будет преобразовать ваши данные в фреймворк pandas (или серию, если это 1D), но для этого есть функции.
источник
Мне нравится выражать обновление так:
def running_update(x, N, mu, var): ''' @arg x: the current data sample @arg N : the number of previous samples @arg mu: the mean of the previous samples @arg var : the variance over the previous samples @retval (N+1, mu', var') -- updated mean, variance and count ''' N = N + 1 rho = 1.0/N d = x - mu mu += rho*d var += rho*((1-rho)*d**2 - var) return (N, mu, var)
так, чтобы однопроходная функция выглядела так:
def one_pass(data): N = 0 mu = 0.0 var = 0.0 for x in data: N = N + 1 rho = 1.0/N d = x - mu mu += rho*d var += rho*((1-rho)*d**2 - var) # could yield here if you want partial results return (N, mu, var)
обратите внимание, что здесь вычисляется дисперсия выборки (1 / N), а не несмещенная оценка дисперсии генеральной совокупности (которая использует коэффициент нормализации 1 / (N-1)). В отличие от других ответов, переменная
var
, отслеживающая текущую дисперсию, не растет пропорционально количеству выборок. Всегда это просто дисперсия набора выборок, наблюдаемых до сих пор (нет окончательного «деления на n» для получения дисперсии).В классе это будет выглядеть так:
class RunningMeanVar(object): def __init__(self): self.N = 0 self.mu = 0.0 self.var = 0.0 def push(self, x): self.N = self.N + 1 rho = 1.0/N d = x-self.mu self.mu += rho*d self.var += + rho*((1-rho)*d**2-self.var) # reset, accessors etc. can be setup as you see fit
Это также работает для взвешенных образцов:
def running_update(w, x, N, mu, var): ''' @arg w: the weight of the current sample @arg x: the current data sample @arg mu: the mean of the previous N sample @arg var : the variance over the previous N samples @arg N : the number of previous samples @retval (N+w, mu', var') -- updated mean, variance and count ''' N = N + w rho = w/N d = x - mu mu += rho*d var += rho*((1-rho)*d**2 - var) return (N, mu, var)
источник
Вот практический пример того, как можно реализовать текущее стандартное отклонение с помощью python и
numpy
:a = np.arange(1, 10) s = 0 s2 = 0 for i in range(0, len(a)): s += a[i] s2 += a[i] ** 2 n = (i + 1) m = s / n std = np.sqrt((s2 / n) - (m * m)) print(std, np.std(a[:i + 1]))
Это распечатает рассчитанное стандартное отклонение и стандартное отклонение проверки, рассчитанное с помощью numpy:
Я просто использую формулу, описанную в этой теме:
stdev = sqrt((sum_x2 / n) - (mean * mean))
источник