Ця проблема вимагає отримання z-балу або стандартного бала, який враховуватиме середню історію, як згадують інші люди, а також стандартне відхилення цих історичних даних, що робить його більш надійним, ніж просто використання середнього.
У вашому випадку z-оцінка обчислюється за наступною формулою, де тенденція буде такою швидкістю, як перегляди / день.
z-score = ([current trend] - [average historic trends]) / [standard deviation of historic trends]
Якщо використовується z-оцінка, то вищий чи нижчий z-оцінка тим більше аномальний тренд, тому, наприклад, якщо z-оцінка є дуже позитивним, то тенденція аномально зростає, тоді як якщо вона є дуже негативною, вона ненормально падає . Отже, як тільки ви обчислите z-бал для всіх кандидатських тенденцій, найвищі 10 z-балів будуть стосуватися найбільш аномально зростаючих z-балів.
Будь ласка, перегляньте Вікіпедію для отримання додаткової інформації про z-результати.
Код
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
Вибірка зразка
>>> 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
Примітки
Цей метод можна використовувати з розсувним вікном (тобто за останні 30 днів), якщо ви не хочете брати до уваги велику історію, що зробить короткострокові тенденції більш вираженими та може скоротити час обробки.
Ви також можете використовувати z-бал для таких значень, як зміна поглядів від одного дня до наступного дня, щоб знайти аномальні значення для збільшення / зменшення переглядів на день. Це подібно до використання нахилу або похідної графіки переглядів на день.
Якщо ви відстежуєте поточний чисельність населення, поточну загальну чисельність населення та поточну загальну кількість x ^ 2 населення, вам не потрібно перераховувати ці значення, лише оновлювати їх, отже, вам потрібно лише зберігайте ці значення для історії, а не кожне значення даних. Наступний код демонструє це.
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()
Використовуючи цей метод, ваш робочий процес буде таким. Для кожної теми, тегу чи сторінки створіть поле з плаваючою точкою за загальну кількість днів, суму переглядів та суму переглядів у квадраті вашої бази даних. Якщо у вас є історичні дані, ініціалізуйте ці поля за допомогою цих даних, інакше ініціалізуйте до нуля. В кінці кожного дня обчислюйте z-бал, використовуючи кількість переглядів дня за попередні дані, що зберігаються у трьох полях бази даних. Теми, теги чи сторінки з найвищими показниками z z X - це ваші X "найбільш популярні тенденції" дня. Нарешті оновіть кожне з 3-х полів зі значенням дня та повторіть процес завтра.
Нове доповнення
Звичайні z-бали, про які йшлося вище, не враховують порядок даних, а отже, z-оцінка для спостереження '1' або '9' матиме однакову величину щодо послідовності [1, 1, 1, 1 , 9, 9, 9, 9]. Очевидно, що для пошуку тенденцій найсучасніші дані повинні мати більшу вагу, ніж старі дані, і тому ми хочемо, щоб спостереження "1" мали більшу оцінку, ніж спостереження "9". Для цього я пропоную плаваючий середній z-бал. Повинно бути зрозуміло, що цей метод НЕ гарантовано є статистично обгрунтованим, але повинен бути корисним для пошуку тенденцій чи подібних. Основна відмінність стандартного z-балу від плаваючого середнього z-бала полягає у використанні плаваючого середнього для обчислення середнього значення населення та середнього значення кількості населення у квадраті. Докладні відомості див. У коді:
Код
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
>>> 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
Оновлення
Як правильно зазначав Девід Кемп, якщо задається серія постійних значень, а потім запитується zscore для спостережуваного значення, яке відрізняється від інших значень, результат, ймовірно, повинен бути не нульовим. Насправді повернене значення повинно бути нескінченним. Тому я змінив цю лінію,
if self.std() == 0: return 0
до:
if self.std() == 0: return (obs - self.avg) * float("infinity")
Ця зміна відображена в коді рішення фасскоре. Якщо людина не хоче мати справу з нескінченними значеннями, прийнятним рішенням може бути замість цього змінити рядок на:
if self.std() == 0: return obs - self.avg