Эта проблема требует 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-оценку, используя количество просмотров за день по сравнению с историческими данными, хранящимися в трех полях базы данных. Темы, теги или страницы с наивысшими оценками X z являются вашими 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()
Пример ввода-вывода
>>> 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
to:
if self.std() == 0: return (obs - self.avg) * float("infinity")
Это изменение отражено в коде решения fazscore. Если кто-то не хочет иметь дело с бесконечными значениями, приемлемым решением может быть изменение строки на:
if self.std() == 0: return obs - self.avg
person
Community
schedule
05.05.2009