Trước đây, tôi đã sử dụng cách tiếp cận sau đây để tính toán độ lệch áp suất vừa phải một cách hiệu quả (lưu ý, đây là cách tiếp cận của lập trình viên, không phải là thống kê, do đó, có thể có những thủ thuật thông minh như shabbychef có thể hiệu quả hơn).
CẢNH BÁO: Đây không phải là một thuật toán trực tuyến. Nó đòi hỏi O(n)
trí nhớ. Hơn nữa, nó có hiệu suất trường hợp xấu nhất O(n)
, đối với các bộ dữ liệu như [1, -2, 4, -8, 16, -32, ...]
(nghĩa là giống như tính toán lại đầy đủ). [1]
Tuy nhiên, vì nó vẫn hoạt động tốt trong nhiều trường hợp sử dụng nên có thể đáng để đăng ở đây. Ví dụ: để tính độ lệch tuyệt đối của 10000 số ngẫu nhiên trong khoảng từ 100 đến 100 khi mỗi mục đến, thuật toán của tôi mất chưa đến một giây, trong khi tính toán lại đầy đủ mất hơn 17 giây (trên máy của tôi, sẽ thay đổi theo từng máy và theo dữ liệu đầu vào). Tuy nhiên, bạn cần duy trì toàn bộ vectơ trong bộ nhớ, đây có thể là một hạn chế đối với một số mục đích sử dụng. Các phác thảo của thuật toán như sau:
- Thay vì có một vectơ duy nhất để lưu trữ các phép đo trong quá khứ, hãy sử dụng ba hàng đợi ưu tiên được sắp xếp (một cái gì đó như một đống tối thiểu / tối đa). Ba danh sách này phân vùng đầu vào thành ba: các mục lớn hơn giá trị trung bình, các mục nhỏ hơn giá trị trung bình và các mục bằng giá trị trung bình.
- (Hầu hết) mỗi khi bạn thêm một mục có nghĩa là thay đổi, vì vậy chúng tôi cần phân vùng lại. Điều cốt yếu là bản chất được sắp xếp của các phân vùng, điều đó có nghĩa là thay vì quét mọi mục trong danh sách để phân vùng lại, chúng ta chỉ cần đọc những mục chúng ta đang di chuyển. Mặc dù trong trường hợp xấu nhất, điều này vẫn sẽ yêu cầu các
O(n)
thao tác di chuyển, đối với nhiều trường hợp sử dụng thì không phải như vậy.
- Sử dụng một số sổ sách thông minh, chúng tôi có thể đảm bảo rằng độ lệch được tính toán chính xác tại mọi thời điểm, khi phân vùng lại và khi thêm các mục mới.
Một số mã mẫu, trong python, dưới đây. Lưu ý rằng nó chỉ cho phép các mục được thêm vào danh sách, không bị xóa. Điều này có thể dễ dàng được thêm vào, nhưng tại thời điểm tôi viết bài này, tôi không cần nó. Thay vì tự thực hiện các hàng đợi ưu tiên, tôi đã sử dụng danh sách được sắp xếp từ gói vỉ tuyệt vời của Daniel Stutzbach , sử dụng nội bộ B + Tree .
Xem xét mã này được cấp phép theo giấy phép MIT . Nó đã không được tối ưu hóa hoặc đánh bóng đáng kể, nhưng đã làm việc cho tôi trong quá khứ. Phiên bản mới sẽ có sẵn ở đây . Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi, hoặc tìm thấy bất kỳ lỗi.
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] Nếu các triệu chứng vẫn tồn tại, hãy đi khám bác sĩ.