Ich habe in der Vergangenheit den folgenden Ansatz verwendet, um die Absolutionsabweichung mäßig effizient zu berechnen (beachten Sie, dass dies ein Programmieransatz ist, kein Statistiker, so dass es zweifellos clevere Tricks wie Shabbychefs gibt , die effizienter sein könnten).
WARNUNG: Dies ist kein Online-Algorithmus. Es erfordert O(n)
Speicher. Darüber hinaus hat es eine Worst-Case-Performance von O(n)
, für Datensätze wie [1, -2, 4, -8, 16, -32, ...]
(dh die gleiche wie die vollständige Neuberechnung). [1]
Da es jedoch in vielen Anwendungsfällen immer noch eine gute Leistung bringt, kann es sich lohnen, es hier zu veröffentlichen. Um zum Beispiel die absolute Abweichung von 10000 Zufallszahlen zwischen -100 und 100 zu berechnen , benötigt mein Algorithmus weniger als eine Sekunde, während die vollständige Neuberechnung über 17 Sekunden dauert (auf meinem Computer variieren je nach Computer und entsprechend den eingegebenen Daten). Sie müssen jedoch den gesamten Vektor im Speicher behalten, was für einige Anwendungen eine Einschränkung sein kann. Der Umriss des Algorithmus lautet wie folgt:
- Verwenden Sie statt eines einzelnen Vektors zum Speichern vergangener Messungen drei sortierte Prioritätswarteschlangen (so etwas wie einen Min / Max-Heap). Diese drei Listen unterteilen die Eingabe in drei Elemente: Elemente, die größer als der Mittelwert sind, Elemente, die kleiner als der Mittelwert sind, und Elemente, die dem Mittelwert entsprechen.
- (Fast) jedes Mal, wenn Sie ein Element hinzufügen, ändert sich der Mittelwert, sodass wir neu partitionieren müssen. Das Entscheidende ist die Sortierung der Partitionen. Dies bedeutet, dass wir nicht jedes Element in der Liste nach Partitionen durchsuchen, sondern nur die Elemente lesen müssen, die wir gerade verschieben. Während dies im schlimmsten Fall immer noch
O(n)
Verschiebevorgänge erfordert, ist dies in vielen Anwendungsfällen nicht der Fall.
- Mit einer ausgeklügelten Buchführung können wir sicherstellen, dass die Abweichung jederzeit korrekt berechnet wird, wenn wir neu partitionieren und neue Elemente hinzufügen.
Einige Beispielcodes in Python finden Sie weiter unten. Beachten Sie, dass nur Elemente zur Liste hinzugefügt und nicht entfernt werden können. Dies könnte leicht hinzugefügt werden, aber zu dem Zeitpunkt, als ich dies schrieb, hatte ich keine Notwendigkeit dafür. Anstatt die Priority Queues selbst zu implementieren, habe ich die sortierte Liste aus Daniel Stutzbachs exzellentem Blist-Paket verwendet , die intern B + Tree s verwendet.
Betrachten Sie diesen Code als unter der MIT-Lizenz lizenziert . Es wurde nicht wesentlich optimiert oder poliert, hat aber in der Vergangenheit für mich funktioniert. Neue Versionen verfügbar sein hier . Lassen Sie mich wissen, wenn Sie Fragen haben oder Fehler finden.
from blist import sortedlist
import operator
class deviance_list:
def __init__(self):
self.mean = 0.0
self._old_mean = 0.0
self._sum = 0L
self._n = 0 #n items
# items greater than the mean
self._toplist = sortedlist()
# items less than the mean
self._bottomlist = sortedlist(key = operator.neg)
# Since all items in the "eq list" have the same value (self.mean) we don't need
# to maintain an eq list, only a count
self._eqlistlen = 0
self._top_deviance = 0
self._bottom_deviance = 0
@property
def absolute_deviance(self):
return self._top_deviance + self._bottom_deviance
def append(self, n):
# Update summary stats
self._sum += n
self._n += 1
self._old_mean = self.mean
self.mean = self._sum / float(self._n)
# Move existing things around
going_up = self.mean > self._old_mean
self._rebalance(going_up)
# Add new item to appropriate list
if n > self.mean:
self._toplist.add(n)
self._top_deviance += n - self.mean
elif n == self.mean:
self._eqlistlen += 1
else:
self._bottomlist.add(n)
self._bottom_deviance += self.mean - n
def _move_eqs(self, going_up):
if going_up:
self._bottomlist.update([self._old_mean] * self._eqlistlen)
self._bottom_deviance += (self.mean - self._old_mean) * self._eqlistlen
self._eqlistlen = 0
else:
self._toplist.update([self._old_mean] * self._eqlistlen)
self._top_deviance += (self._old_mean - self.mean) * self._eqlistlen
self._eqlistlen = 0
def _rebalance(self, going_up):
move_count, eq_move_count = 0, 0
if going_up:
# increase the bottom deviance of the items already in the bottomlist
if self.mean != self._old_mean:
self._bottom_deviance += len(self._bottomlist) * (self.mean - self._old_mean)
self._move_eqs(going_up)
# transfer items from top to bottom (or eq) list, and change the deviances
for n in iter(self._toplist):
if n < self.mean:
self._top_deviance -= n - self._old_mean
self._bottom_deviance += (self.mean - n)
# we increment movecount and move them after the list
# has finished iterating so we don't modify the list during iteration
move_count += 1
elif n == self.mean:
self._top_deviance -= n - self._old_mean
self._eqlistlen += 1
eq_move_count += 1
else:
break
for _ in xrange(0, move_count):
self._bottomlist.add(self._toplist.pop(0))
for _ in xrange(0, eq_move_count):
self._toplist.pop(0)
# decrease the top deviance of the items remain in the toplist
self._top_deviance -= len(self._toplist) * (self.mean - self._old_mean)
else:
if self.mean != self._old_mean:
self._top_deviance += len(self._toplist) * (self._old_mean - self.mean)
self._move_eqs(going_up)
for n in iter(self._bottomlist):
if n > self.mean:
self._bottom_deviance -= self._old_mean - n
self._top_deviance += n - self.mean
move_count += 1
elif n == self.mean:
self._bottom_deviance -= self._old_mean - n
self._eqlistlen += 1
eq_move_count += 1
else:
break
for _ in xrange(0, move_count):
self._toplist.add(self._bottomlist.pop(0))
for _ in xrange(0, eq_move_count):
self._bottomlist.pop(0)
# decrease the bottom deviance of the items remain in the bottomlist
self._bottom_deviance -= len(self._bottomlist) * (self._old_mean - self.mean)
if __name__ == "__main__":
import random
dv = deviance_list()
# Test against some random data, and calculate result manually (nb. slowly) to ensure correctness
rands = [random.randint(-100, 100) for _ in range(0, 1000)]
ns = []
for n in rands:
dv.append(n)
ns.append(n)
print("added:%4d, mean:%3.2f, oldmean:%3.2f, mean ad:%3.2f" %
(n, dv.mean, dv._old_mean, dv.absolute_deviance / dv.mean))
assert sum(ns) == dv._sum, "Sums not equal!"
assert len(ns) == dv._n, "Counts not equal!"
m = sum(ns) / float(len(ns))
assert m == dv.mean, "Means not equal!"
real_abs_dev = sum([abs(m - x) for x in ns])
# Due to floating point imprecision, we check if the difference between the
# two ways of calculating the asb. dev. is small rather than checking equality
assert abs(real_abs_dev - dv.absolute_deviance) < 0.01, (
"Absolute deviances not equal. Real:%.2f, calc:%.2f" % (real_abs_dev, dv.absolute_deviance))
[1] Wenn die Symptome anhalten, wenden Sie sich an Ihren Arzt.