Vấn đề này đòi hỏi điểm z hoặc điểm chuẩn, sẽ tính đến mức trung bình lịch sử, như những người khác đã đề cập, nhưng cũng là độ lệch chuẩn của dữ liệu lịch sử này, làm cho nó mạnh hơn so với chỉ sử dụng mức trung bình.
Trong trường hợp của bạn, điểm z được tính theo công thức sau, trong đó xu hướng sẽ là tỷ lệ như lượt xem / ngày.
z-score = ([current trend] - [average historic trends]) / [standard deviation of historic trends]
Khi sử dụng điểm z, điểm z càng cao hoặc thấp thì xu hướng càng bất thường, vì vậy, nếu điểm z có giá trị dương cao thì xu hướng tăng bất thường, trong khi nếu âm rất cao thì nó lại giảm bất thường. . Vì vậy, một khi bạn tính điểm z cho tất cả các xu hướng ứng cử viên, 10 điểm z cao nhất sẽ liên quan đến điểm z tăng bất thường nhất.
Vui lòng xem Wikipedia để biết thêm thông tin, về điểm z.
Mã
from math import sqrt
def zscore(obs, pop):
# Size of population.
number = float(len(pop))
# Average population value.
avg = sum(pop) / number
# Standard deviation of population.
std = sqrt(sum(((c - avg) ** 2) for c in pop) / number)
# Zscore Calculation.
return (obs - avg) / std
Đầu ra mẫu
>>> zscore(12, [2, 4, 4, 4, 5, 5, 7, 9])
3.5
>>> zscore(20, [21, 22, 19, 18, 17, 22, 20, 20])
0.0739221270955
>>> zscore(20, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1])
1.00303599234
>>> zscore(2, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1])
-0.922793112954
>>> zscore(9, [1, 2, 0, 3, 1, 3, 1, 2, 9, 8, 7, 10, 9, 5, 2, 4, 1, 1, 0])
1.65291949506
Ghi chú
Bạn có thể sử dụng phương pháp này với một cửa sổ trượt (tức là 30 ngày qua) nếu bạn không muốn tính đến nhiều lịch sử, điều này sẽ làm cho các xu hướng ngắn hạn rõ rệt hơn và có thể cắt giảm thời gian xử lý.
Bạn cũng có thể sử dụng điểm z cho các giá trị, chẳng hạn như thay đổi lượt xem từ một ngày sang ngày tiếp theo để xác định các giá trị bất thường để tăng / giảm lượt xem mỗi ngày. Điều này giống như sử dụng độ dốc hoặc đạo hàm của biểu đồ mỗi ngày.
Nếu bạn theo dõi kích thước hiện tại của dân số, tổng dân số hiện tại và tổng số x ^ 2 hiện tại của dân số, bạn không cần phải tính toán lại các giá trị này, chỉ cần cập nhật chúng và do đó bạn chỉ cần giữ các giá trị này cho lịch sử, không phải mỗi giá trị dữ liệu. Các mã sau đây chứng minh điều này.
from math import sqrt
class zscore:
def __init__(self, pop = []):
self.number = float(len(pop))
self.total = sum(pop)
self.sqrTotal = sum(x ** 2 for x in pop)
def update(self, value):
self.number += 1.0
self.total += value
self.sqrTotal += value ** 2
def avg(self):
return self.total / self.number
def std(self):
return sqrt((self.sqrTotal / self.number) - self.avg() ** 2)
def score(self, obs):
return (obs - self.avg()) / self.std()
Sử dụng phương pháp này luồng công việc của bạn sẽ như sau. Đối với mỗi chủ đề, thẻ hoặc trang tạo một trường dấu phẩy động, cho tổng số ngày, tổng số lượt xem và tổng số lượt xem bình phương trong cơ sở dữ liệu của bạn. Nếu bạn có dữ liệu lịch sử, hãy khởi tạo các trường này bằng dữ liệu đó, nếu không thì khởi tạo thành không. Vào cuối mỗi ngày, hãy tính điểm z bằng cách sử dụng số lượt xem trong ngày so với dữ liệu lịch sử được lưu trữ trong ba trường cơ sở dữ liệu. Các chủ đề, thẻ hoặc trang có điểm số z z cao nhất là "xu hướng nóng nhất" trong ngày của bạn. Cuối cùng cập nhật từng trường trong số 3 trường với giá trị của ngày và lặp lại quy trình vào ngày mai.
Bổ sung mới
Điểm z thông thường như được thảo luận ở trên không tính đến thứ tự của dữ liệu và do đó, điểm z cho quan sát '1' hoặc '9' sẽ có cùng độ lớn so với chuỗi [1, 1, 1, 1 , 9, 9, 9, 9]. Rõ ràng đối với việc tìm kiếm xu hướng, dữ liệu mới nhất nên có trọng số lớn hơn dữ liệu cũ và do đó chúng tôi muốn quan sát '1' có điểm số lớn hơn so với quan sát '9'. Để đạt được điều này, tôi đề xuất một điểm z trung bình nổi. Cần phải rõ ràng rằng phương pháp này KHÔNG được đảm bảo là hợp lý về mặt thống kê nhưng sẽ hữu ích cho việc tìm kiếm xu hướng hoặc tương tự. Sự khác biệt chính giữa điểm z chuẩn và điểm z trung bình nổi là việc sử dụng trung bình nổi để tính giá trị dân số trung bình và giá trị dân số trung bình bình phương. Xem mã để biết chi tiết:
Mã
class fazscore:
def __init__(self, decay, pop = []):
self.sqrAvg = self.avg = 0
# The rate at which the historic data's effect will diminish.
self.decay = decay
for x in pop: self.update(x)
def update(self, value):
# Set initial averages to the first value in the sequence.
if self.avg == 0 and self.sqrAvg == 0:
self.avg = float(value)
self.sqrAvg = float((value ** 2))
# Calculate the average of the rest of the values using a
# floating average.
else:
self.avg = self.avg * self.decay + value * (1 - self.decay)
self.sqrAvg = self.sqrAvg * self.decay + (value ** 2) * (1 - self.decay)
return self
def std(self):
# Somewhat ad-hoc standard deviation calculation.
return sqrt(self.sqrAvg - self.avg ** 2)
def score(self, obs):
if self.std() == 0: return (obs - self.avg) * float("infinity")
else: return (obs - self.avg) / self.std()
IO mẫu
>>> fazscore(0.8, [1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9]).score(1)
-1.67770595327
>>> fazscore(0.8, [1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9]).score(9)
0.596052006642
>>> fazscore(0.9, [2, 4, 4, 4, 5, 5, 7, 9]).score(12)
3.46442230724
>>> fazscore(0.9, [2, 4, 4, 4, 5, 5, 7, 9]).score(22)
7.7773245459
>>> fazscore(0.9, [21, 22, 19, 18, 17, 22, 20, 20]).score(20)
-0.24633160155
>>> fazscore(0.9, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1]).score(20)
1.1069362749
>>> fazscore(0.9, [21, 22, 19, 18, 17, 22, 20, 20, 1, 2, 3, 1, 2, 1, 0, 1]).score(2)
-0.786764452966
>>> fazscore(0.9, [1, 2, 0, 3, 1, 3, 1, 2, 9, 8, 7, 10, 9, 5, 2, 4, 1, 1, 0]).score(9)
1.82262469243
>>> fazscore(0.8, [40] * 200).score(1)
-inf
Cập nhật
Như David Kemp đã chỉ ra một cách chính xác, nếu đưa ra một loạt các giá trị không đổi và sau đó một zscore cho một giá trị quan sát khác với các giá trị khác được yêu cầu, kết quả có thể phải khác không. Trong thực tế, giá trị trả về phải là vô cùng. Vì vậy, tôi đã thay đổi dòng này,
if self.std() == 0: return 0
đến:
if self.std() == 0: return (obs - self.avg) * float("infinity")
Sự thay đổi này được phản ánh trong mã giải pháp fazscore. Nếu một người không muốn đối phó với các giá trị vô hạn, một giải pháp có thể chấp nhận thay vào đó có thể thay đổi dòng thành:
if self.std() == 0: return obs - self.avg