Как лучше всего вычислять популярные темы или теги?

Многие сайты предлагают статистику вроде «Самые горячие темы за последние 24 часа». Например, Topix.com показывает это в своем разделе «Тенденции новостей». Здесь вы можете увидеть темы, количество упоминаний которых растет быстрее всего.

Я тоже хочу посчитать такой "кайф" для темы. Как я мог это сделать? Алгоритм должен меньше взвешивать темы, которые всегда актуальны. Темы, которые обычно (почти) никто не упоминает, должны быть самыми горячими.

Google предлагает «Горячие тенденции», topix.com показывает «Горячие темы», fav.or.it показывает «Тенденции ключевых слов» - все эти сервисы имеют одну общую черту: они показывают только предстоящие тенденции, которые в настоящий момент являются аномально популярными.

Такие термины, как «Бритни Спирс», «погода» или «Пэрис Хилтон» не появятся в этих списках, потому что они всегда горячие и частые. В этой статье это называется «Проблема Бритни Спирс».

Мой вопрос: как вы можете закодировать алгоритм или использовать существующий для решения этой проблемы? Имея список с ключевыми словами, которые искали за последние 24 часа, алгоритм должен показать вам 10 (например) самых популярных из них.

Я знаю, что в статье выше упоминается какой-то алгоритм. Я пытался закодировать его на PHP, но не думаю, что это буду работать. Оно просто находит большинство, не так ли?

Надеюсь, вы можете мне помочь (примеры кодирования были бы отличными).


person caw    schedule 24.04.2009    source источник
comment
Это именно то, что должен делать Topix.com. Связанный вопрос не дает никакого кода, но он определенно дает алгоритм. Используйте алгоритм Демейна, цитируемый в конце страницы 4 статьи, чтобы вычислить первые десять (или что-то еще) поисковых запросов из журнала за последние 24 часа. Если вы хотите ранжировать их, вам нужно снова просмотреть журнал и подсчитать вхождения каждого поиска. Это длинная и довольно техническая статья, но на самом деле она содержит информацию, необходимую для решения самых актуальных тем с возможностью масштабирования.   -  person Quantum7    schedule 28.04.2009
comment
Итак, Topix.com должен ли я использовать алгоритм большинства? Правильный ли следующий подход? paste.bradleygill.com/index.php?paste_id=9117   -  person caw    schedule 02.05.2009
comment
Вы действительно находите самые горячие темы, используя алгоритм Демейна? Или в результате будут всегда горячие темы (Бритни Спирс, погода, ...)?   -  person caw    schedule 03.05.2009
comment
Пожалуйста, дайте мне еще несколько ответов! Моя проблема еще не решена, и я скоро должен выбрать ответ на награду.   -  person caw    schedule 04.05.2009
comment
Это точно такой же вопрос, и он даже заявляет об этом! Почему люди голосуют за это!   -  person Darryl Hein    schedule 05.05.2009
comment
Эх, это жеребьевка. Этот специально запрашивает код. В остальном они по сути одинаковы.   -  person Adam Davis    schedule 05.05.2009
comment
Я немного не понимаю, какой результат вы ищете. Статья, кажется, указывает на то, что Бритни Спирс будет постоянно находиться в горячем списке, потому что очень много людей ищут этот термин, но в вашем вопросе говорится, что он НЕ появится в списке, потому что количество поисков по этому термину не сильно увеличится. время (они остаются высокими, но стабильными). Какого результата вы пытаетесь добиться? Должна ли Бритни Спирс занимать высокое или низкое место?   -  person e.James    schedule 05.05.2009
comment
@Adam - Ему дали несколько разных вариантов псевдокода в другом вопросе, но он отклонил их, потому что он их не понимал. Похоже, ему нужно решение на серебряном блюде, а не стратегия или алгоритм решения проблемы.   -  person James Van Huis    schedule 05.05.2009
comment
@eJames, Бритни Спирс не должна занимать высокие позиции, потому что она постоянно является популярным поисковым запросом, а он ищет поисковые запросы с высокой скоростью.   -  person mmcdole    schedule 05.05.2009
comment
@James - Что ж, из предложенных предложений только одно намекало на алгоритм, который отклоняет постоянно горячие темы, но улавливает аномально горячую - градиент. К сожалению, при 1000 тем в час градиент не масштабируется. Возможно, для него было бы лучше обновить вопрос, добавив в него более актуальную информацию, но теперь эта версия также содержит достаточно хорошую информацию. Я предвзято, потратив немного времени, отвечая на его вопрос, но что лучше из двух? Если это дубликаты, следует закрыть тот, который хуже, а не самый последний.   -  person Adam Davis    schedule 05.05.2009
comment
@ Адам Дэвис: я согласен, что другой должен быть закрыт   -  person TStamper    schedule 06.05.2009
comment
@Simucal: Именно этого я и хотел бы добиться. @ Джеймс Ван Хьюис: Извините, но это неправильно. Был только один ответ с псевдокодом. И я не могу использовать это для своих целей. @ALL: Думаю, в этом вопросе я намного лучше описал свою проблему. Так что здесь еще есть ответы получше. Его нельзя закрывать! @ Адам Дэвис: Спасибо за ответ! Этот и все остальные намного лучше, чем ответы на другой вопрос. Так что - если вообще - закройте другой вопрос, пожалуйста.   -  person caw    schedule 06.05.2009
comment
Возможно, вы забыли, что SO имеет такую ​​же возможность на главной странице. Я просто говорю... ;-)   -  person Kredns    schedule 07.05.2009
comment
Голосование за повторное открытие: это дополнительный вопрос к исходному вопросу, в котором говорится об особой проблеме, которая возникает при попытке решить исходную проблему.   -  person Fabian Steeg    schedule 07.05.2009
comment
Ни точный дубликат, ни даже почти дубликат. Этот вопрос касается решения конкретной проблемы с помощью определенного алгоритма.   -  person thomasrutter    schedule 07.05.2009
comment
Я тоже не думаю, что это дубликат. Жалко, что два вопроса совмещены. Ответы на вторые вопросы были намного лучше, но я больше не могу выбирать их как лучший ответ.   -  person caw    schedule 07.05.2009
comment
Ого, святые вопросы слияния, Бэтмен! Теперь я больше не наверху, потому что вопрос, на который я ответил, был объединен с вопросом, на который уже был принят принятый ответ. Какое-то странное ...   -  person Adam Davis    schedule 09.05.2009
comment
Да, это действительно странно. Я хотел бы выбрать другой ответ как лучший. Но это кажется невозможным.   -  person caw    schedule 09.05.2009
comment
Метод Z-оценки не будет работать, если количество ключевых слов (или темы) невелико. В этом случае я бы рекомендовал моделировать проблему как распределение Пуассона.   -  person Zebra    schedule 28.05.2019


Ответы (10)


Эта проблема требует 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
comment
Большое тебе спасибо! Такой подход выглядит очень хорошо. Но у меня все еще есть вопрос: :) Правильный ли следующий код PHP? paste.bradleygill.com/index.php?paste_id=9205 - person caw; 06.05.2009
comment
Нет, в следующей строке в вашем коде есть небольшая ошибка. $ z_score = $ hits_today - ($ average_hits_per_day / $ standard_deviation); Это должно быть: $ z_score = ($ hits_today- $ average_hits_per_day) / $ standard_deviation; Обратите внимание на изменение в скобках. - person Nixuz; 06.05.2009
comment
Хорошо спасибо! :) Теперь это правильно? paste.bradleygill.com/index.php?paste_id=9206 - person caw; 06.05.2009
comment
Спасибо за редактирование скользящей средней. Я тоже пытался реализовать это в своем сценарии. Это правильно? paste.bradleygill.com/index.php?paste_id=9224 - person caw; 07.05.2009
comment
Я бы сказал, что это неправильно. Во-первых, если вы измените $ old_average_value на $ average_hits_per_day, вы можете избавиться от строки: $ old_average_value = $ average_hits_per_day ;. Во-вторых, что более важно, вам необходимо выполнить стандартное отклонение специального скользящего среднего, а также обычное скользящее среднее. Для этого вам нужно использовать метод расчета обновления (см. Пункт 3 примечания), и когда вы суммируете x ^ 2, вы собираетесь взвесить их так же, как и ваше скользящее среднее. Третье начало с «1» для скользящих средних может быть не очень хорошей идеей. - person Nixuz; 07.05.2009
comment
Чтобы прояснить ситуацию, при обновлении мы используем формулу стандартного отклонения upload.wikimedia.org/math/9/… (см. Справа). А при вычислении z-показателя скользящего среднего среднее значение берется из скользящего среднего x, а среднее x ^ 2 - из скользящего среднего x ^ 2. С этими значениями формула стандартного отклонения становится std = sqrt ([среднее x ^ 2] - [среднее x] ^ 2). Как только у вас есть скользящее среднее и скользящее стандартное отклонение, расчет z-показателя будет таким же. - person Nixuz; 07.05.2009
comment
Спасибо, вы очень подробно описали проблему, но я ее не совсем понял. URL-адрес вашей формулы отображается неправильно. Мне нужно изменить следующую строку? $ standard_deviation_temp = array_sum ($ devitions_on_single_days) / count ($ devitions_on_single_days) - person caw; 07.05.2009
comment
Просмотрите изменения, которые я внес в ваш код: paste.bradleygill.com/index.php? paste_id = 9230 URL-адрес формулы: upload.wikimedia .org / math / 9 / e / 9 / - person Nixuz; 08.05.2009
comment
Спасибо большое, Никсуз! Отредактированный код на codepaste работает отлично. Я хотел бы выбрать ваш ответ как лучший, но, к сожалению, кто-то объединил оба вопроса, так что лучший ответ уже есть. Или все же есть возможность выбрать свой ответ? - person caw; 08.05.2009
comment
Я почти уверен, что переполнение стека позволит вам изменить лучший ответ. Для этого просто щелкните галочку рядом с вашим новым выбором. - person Nixuz; 09.05.2009
comment
Нет, это невозможно. Если вы уже отметили ответ как лучший, флажки рядом с другими ответами исчезнут. Может быть, здесь поможет поддержка, мод !? - person caw; 09.05.2009
comment
@nixuz - я что-то упустил: fazscore (0.8, map (lambda x: 40, range (0,200))). score (1) == 0 (для любых значений)? - person kͩeͣmͮpͥ ͩ; 17.12.2010
comment
Да, это небольшая проблема. Проблема в том, что стандартное отклонение 200 40 имеет нулевое стандартное отклонение, что вызовет проблему для стандартной функции оценки, поскольку она пытается разделить на ноль. Я просто установил нулевой результат, когда стандартное отклонение всегда равно нулю. Ясно, что это было не лучшее решение. Я обновлю ответ, чтобы немного отразить исправление. - person Nixuz; 17.12.2010
comment
@Nixus - Думал, что могу выкопать это из могилы. Не могли бы вы повторно опубликовать реализацию этого PHP? Ссылки paste вроде не работают ... спасибо! - person Drewness; 23.04.2013
comment
Тупой вопрос: когда мы говорим о временных рамках для оценки (ов), должны ли они перекрывать друг друга или должны быть плавающими, и в этом случае будут использоваться два временных периода (окно)? IE, если я основываю это на хитах в неделю, должен ли я делать эксклюзивные недели или использовать плавающие хиты в неделю с дневным окном - person thouliha; 23.06.2015
comment
@thouliha Это полностью зависит от вас - оба будут работать. - person Nixuz; 27.06.2015
comment
Для всех, кому это нравится, теперь у меня есть SQL-запросы для этого. - person thouliha; 08.02.2016
comment
@thouliha вы можете вставить ссылку для запросов sql - person Shashank Singh; 02.01.2017
comment
@ShashankSingh Вот метод ранжирования postgres, который вы можете использовать: github.com/dessalines/flowchat/blob/master/service/src/main/. - person thouliha; 03.01.2017
comment
Распад здесь противоречит интуиции; если вы введете 2 значения, скажем [10, 20] со спадом 0,8, AVG будет 10 * 0,8 + 20 * 0,2 = 12. Вы ожидаете, что значение будет выше 15, так как 20 должны иметь больший вес, чем 10, если есть распад. Существует гораздо лучшая альтернатива, использующая средневзвешенное значение в numpy.average, где вы создаете параллельный список с весами. Например: data = range (10,30,10) decay = 0.8 decay_weights = [decay ** a for a in range (len (data), 0, -1)] print np.average (data, weights = decay_weights) - person Jeroen; 01.06.2018
comment
Z-оценка соответствует нормальному распределению. Работает ли этот подход, когда базовые данные ненормальны? (чего не будет в большинстве случаев) - person Zebra; 07.05.2019
comment
Лучше всего будет использовать распределение, соответствующее вашим данным. Обычно распределенные данные - это всего лишь предположение, но вы должны измерить эту базу в зависимости от вашего случая использования. - person Nixuz; 07.05.2019
comment
Почему-то этот метод неплохо работает. Но я пытаюсь объяснить, почему это работает, хотя мой дистрибутив не является нормальным. - person Zebra; 07.05.2019

Вам нужен алгоритм, который измеряет скорость темы - или, другими словами, если вы построите график, вы хотите показать те, которые растут с невероятной скоростью.

Это первая производная линии тренда, и ее нетрудно включить в качестве взвешенного фактора в ваши общие расчеты.

Нормализовать

Один из способов, который вам нужно сделать, - это нормализовать все ваши данные. Для каждой темы, которую вы отслеживаете, держите очень низкий фильтр, который определяет базовый уровень этой темы. Теперь каждая точка данных, которая поступает по этой теме, должна быть нормализована - вычтите ее базовый уровень, и вы получите ВСЕ ваши темы около 0, с пиками выше и ниже линии. Вместо этого вы можете разделить сигнал на его базовую величину, что приведет к значению сигнала примерно 1,0 - это не только приведет все сигналы в соответствие друг с другом (нормализует базовую линию), но также нормализует выбросы. Спайк Бритни будет на порядок больше, чем спайк другого человека, но это не значит, что вы должны обращать на него внимание - спайк может быть очень маленьким по сравнению с ее исходным уровнем.

Производные

После того, как вы все нормализуете, определите наклон каждой темы. Возьмите две последовательные точки и измерьте разницу. Положительная разница имеет тенденцию к увеличению, отрицательная разница имеет тенденцию к снижению. Затем вы можете сравнить нормализованные различия и выяснить, какие темы набирают популярность по сравнению с другими темами, при этом каждая тема масштабируется в соответствии с ее собственной «нормальностью», величина которой может быть на порядок отличной от других тем.

Это действительно первый шаг к решению проблемы. Есть более продвинутые методы, которые вам нужно использовать (в основном комбинация вышеперечисленных с другими алгоритмами, взвешенными в соответствии с вашими потребностями), но этого должно быть достаточно, чтобы вы начали.

По поводу статьи

Статья посвящена трендам тем, но не о том, как вычислить, что популярно, а что нет, а о том, как обрабатывать огромный объем информации, которую такой алгоритм должен обрабатывать в таких местах, как Lycos и Google. Пространство и время, необходимые для того, чтобы дать каждой теме счетчик и найти счетчик каждой темы при поиске, огромны. Эта статья посвящена проблемам, с которыми можно столкнуться при выполнении такой задачи. В нем упоминается эффект Бритни, но не говорится о том, как его преодолеть.

Как указывает Nixuz, это также называется Z или Стандартный балл.

person Adam Davis    schedule 05.05.2009
comment
Спасибо! Я бы сделал псевдокод, но сейчас у меня нет времени. Может быть, позже, а может быть, кто-то другой возьмет эти концепции и воплотит их в жизнь ... - person Adam Davis; 05.05.2009
comment
Большое спасибо, Адам Дэвис! Если Nixuz действительно описал то же самое, я думаю, что у меня есть решение на PHP: вставить. bradleygill.com/index.php?paste_id=9206 Как вы думаете, этот код правильный? - person caw; 06.05.2009
comment
Разве это не должно быть ускорение темы, а не скорость? Посмотрите последний ответ - person Sap; 31.05.2013

Чад Берч и Адам Дэвис правы в том, что вам придется оглянуться назад, чтобы установить исходный уровень. Ваш вопрос, как он сформулирован, предполагает, что вы хотите просматривать данные только за последние 24 часа, а это не совсем так.

Один из способов предоставить вашим данным немного памяти без необходимости запрашивать большой массив исторических данных - использовать экспоненциальная скользящая средняя. Преимущество этого метода в том, что вы можете обновлять его один раз за период, а затем очищать все старые данные, поэтому вам нужно запомнить только одно значение. Итак, если у вас дневной цикл, вам необходимо поддерживать атрибут «среднесуточное значение» для каждой темы, что вы можете сделать следующим образом:

a_n = a_(n-1)*b + c_n*(1-b)

Где a_n - скользящая средняя на день n, b - некоторая константа между 0 и 1 (чем ближе к 1, тем длиннее память), а c_n - количество совпадений в день n. Прелесть в том, что если вы выполните это обновление в конце n дня, вы сможете сбросить c_n и a_(n-1).

Единственное предостережение: изначально он будет чувствителен к тому, что вы выберете в качестве начального значения a.

РЕДАКТИРОВАТЬ

Если это помогает визуализировать этот подход, возьмите n = 5, a_0 = 1 и b = .9.

Скажем, новые значения 5,0,0,1,4:

a_0 = 1
c_1 = 5 : a_1 = .9*1 + .1*5 = 1.4
c_2 = 0 : a_2 = .9*1.4 + .1*0 = 1.26
c_3 = 0 : a_3 = .9*1.26 + .1*0 = 1.134
c_4 = 1 : a_4 = .9*1.134 + .1*1 = 1.1206
c_5 = 4 : a_5 = .9*1.1206 + .1*5 = 1.40854

Не очень похоже на среднее, не так ли? Обратите внимание на то, что значение осталось близким к 1, хотя нашим следующим вводом было 5. Что происходит? Если расширить математику, то получится:

a_n = (1-b)*c_n + (1-b)*b*c_(n-1) + (1-b)*b^2*c_(n-2) + ... + (leftover weight)*a_0

Что я имею в виду под остаточным весом? Ну, в любом среднем, все веса должны складываться к 1. Если бы n было бесконечным и ... могло бы продолжаться бесконечно, тогда все веса были бы в сумме 1. Но если n относительно мало, у вас останется хороший вес. на исходном входе.

Если вы изучите приведенную выше формулу, вы должны понять несколько вещей об этом использовании:

  1. Все данные всегда вносят что-то в среднее значение. Фактически, есть момент, когда вклад действительно очень мал.
  2. Недавние ценности вносят больший вклад, чем старые.
  3. Чем выше b, тем менее важны новые значения и более длинные старые значения имеют значение. Однако чем выше b, тем больше данных вам нужно для уменьшения начального значения a.

Я думаю, что первые две характеристики - это именно то, что вам нужно. Чтобы дать вам представление о том, как это просто реализовать, вот реализация на Python (за вычетом всего взаимодействия с базой данных):

>>> class EMA(object):
...  def __init__(self, base, decay):
...   self.val = base
...   self.decay = decay
...   print self.val
...  def update(self, value):
...   self.val = self.val*self.decay + (1-self.decay)*value
...   print self.val
... 
>>> a = EMA(1, .9)
1
>>> a.update(10)
1.9
>>> a.update(10)
2.71
>>> a.update(10)
3.439
>>> a.update(10)
4.0951
>>> a.update(10)
4.68559
>>> a.update(10)
5.217031
>>> a.update(10)
5.6953279
>>> a.update(10)
6.12579511
>>> a.update(10)
6.513215599
>>> a.update(10)
6.8618940391
>>> a.update(10)
7.17570463519
person David Berger    schedule 05.05.2009
comment
Это также известно как фильтр с бесконечной импульсной характеристикой (БИХ). - person Adam Davis; 05.05.2009
comment
@ Адам В самом деле? Я с ними не знаком. Это частный случай ИДК? Статьи, которые я просматриваю, похоже, не содержат формул, которые в простом случае сводятся к экспоненциальной скользящей средней. - person David Berger; 06.05.2009
comment
Большое спасибо, Дэвид Бергер! Если это сработает, это будет отличным дополнением к другим ответам! Однако у меня есть несколько вопросов. Надеюсь, вы сможете на них ответить: 1) Определяет ли коэффициент b, насколько быстро худеют старые данные? 2) Дает ли этот подход примерно эквивалентные результаты по сравнению с простым хранением старых данных и вычислением среднего? 3) Это ваша формула на словах? $ average_value = $ old_average_value * $ smoothing_factor + $ hits_today * (1- $ smoothing_factor) - person caw; 06.05.2009
comment
Пункты 1 и 3 верны. См. Мою правку, где подробно обсуждается 2. - person David Berger; 06.05.2009
comment
Возможно, мне что-то не хватает, но я не понимаю, как можно разумно использовать скользящую среднюю для решения этой проблемы. После того, как вы рассчитали скользящую среднюю для своих трендов, как узнать, какой из них растет быстрее других по сравнению с другими? Не могли бы вы добавить дополнительную информацию о том, как это решает начальную проблему. Спасибо. - person Nixuz; 07.05.2009
comment
@Nixuz Это способствует решению исходной проблемы; это не решает. Вероятно, это не должно было быть таким длинным ответом, но было несколько ответов, которые указали на использование некоторого среднего тренда, и из исходного вопроса казалось, что намерение состояло только в использовании данных за предыдущий день. Назовите это оптимизацией подзадачи. - person David Berger; 07.05.2009
comment
В своем ответе я включил идею плавающего среднего в идею z-значения. Спасибо за идею, Дэвид. - person Nixuz; 07.05.2009
comment
@Nixuz, ты можешь мне сказать, как? - person Jesvin Jose; 28.10.2013
comment
Полный код находится в ответе, который у меня есть, с пояснением. - person Nixuz; 29.10.2013
comment
Стоит отметить, что вы определили свои коэффициенты распада (и 1-распада), противоположные тому, что указано в описании Википедии уравнения EMA, на которое вы ссылались в своем сообщении. Если вы используете уравнение, как описано в Википедии, более высокая константа распада будет быстрее обесценивать старые наблюдения. С вашей реализацией более низкая постоянная затухания будет быстрее обесценивать старые наблюдения. Это все то же самое, если вы понимаете, как постоянная затухания влияет на вашу функцию сглаживания в зависимости от вашей реализации. - person Javid Jamae; 02.10.2014

Обычно "шум" вычисляется с помощью некоторой формы механизма экспоненциального / логарифмического затухания. Для обзора того, как Hacker News, Reddit и другие просто справляются с этим, см. этот пост.

Это не совсем касается вещей, которые всегда популярны. Похоже, вы ищете что-то вроде "горячих тенденций "особенность. Для этого вы можете разделить текущее значение на историческое значение, а затем вычесть те, которые ниже некоторого порога шума.

person Jeff Moser    schedule 30.04.2009
comment
Да, Google's Hot Trends - это именно то, что я ищу. Какой должна быть историческая ценность? Например, среднее значение за последние 7 дней? - person caw; 02.05.2009
comment
Это зависит от того, насколько изменчивы ваши данные. Вы можете начать со средней 30-дневной. Если это цикличность (например, Дерби в Кентукки), то имеет смысл проводить ежегодные сравнения. Я бы поэкспериментировал и посмотрел, что лучше всего работает на практике. - person Jeff Moser; 04.05.2009

Я думаю, что ключевое слово, на которое вы должны обратить внимание, - «ненормально». Чтобы определить, что что-то «ненормально», вы должны знать, что является нормальным. То есть вам понадобятся исторические данные, которые вы можете усреднить, чтобы узнать нормальную скорость конкретного запроса. Вы можете исключить ненормальные дни из расчета усреднения, но опять же, для этого потребуется уже достаточно данных, чтобы вы знали, какие дни исключить.

Оттуда вам нужно будет установить порог (что, я уверен, потребует экспериментов), и если что-то выходит за пределы порога, скажем, на 50% больше запросов, чем обычно, вы можете считать это «тенденцией». Или, если вы хотите найти «Top X самых модных», как вы упомянули, вам просто нужно упорядочить вещи по тому, насколько (в процентном отношении) они отклоняются от своей нормальной скорости.

Например, предположим, что ваши исторические данные говорят вам, что Бритни Спирс обычно получает 100 000 запросов, а Пэрис Хилтон - 50 000. Если у вас есть день, когда они оба получают на 10 000 запросов больше, чем обычно, вы должны считать Пэрис более «горячей», чем Бритни, потому что ее запросы увеличились на 20% больше, чем обычно, в то время как количество запросов Бритни было только 10%.

Боже, не могу поверить, что я только что написал абзац, в котором сравнивает «сексуальность» Бритни Спирс и Пэрис Хилтон. Что ты со мной сделал?

person Chad Birch    schedule 05.05.2009
comment
Спасибо, но было бы слишком легко их заказывать только по их процентному увеличению, не так ли? - person caw; 06.05.2009

Мне было интересно, можно ли вообще в таком случае использовать обычную физическую формулу ускорения?

v2-v1/t or dv/dt

Мы можем рассматривать v1 как начальные лайки / голоса / количество комментариев в час, а v2 как текущую «скорость» в час за последние 24 часа?

Это больше похоже на вопрос, чем на ответ, но, похоже, это может сработать. Любой контент с максимальным ускорением будет актуальной темой ...

Я уверен, что это не решит проблему Бритни Спирс :-)

person Sap    schedule 27.05.2013
comment
Он будет работать, так как он просто рассчитывает увеличение количества голосов / лайков за раз, и это то, что нам нужно. Это могло бы частично решить проблему Бритни Спирс, потому что этот поисковый запрос всегда имеет высокий v1, и ему нужно очень высокое v2, чтобы его можно было считать трендовым. Однако для этого, вероятно, существуют более совершенные и более совершенные формулы и алгоритмы. Тем не менее, это базовый рабочий пример. - person caw; 27.05.2013
comment
В контексте, когда вам всегда нужно что-то в тренде, это идеально. Что-то вроде вкладки "Обзор", на которой вы перечисляете лучшее, что есть на платформе прямо сейчас. Используя другой алгоритм, вы можете получить пустой набор результатов. - person kilianc; 14.01.2015

вероятно, будет работать простой градиент частоты тем - большой положительный градиент = быстрый рост популярности.

Самый простой способ - собрать количество поисковых запросов каждый день, чтобы у вас было что-то вроде

searches = [ 10, 7, 14, 8, 9, 12, 55, 104, 100 ]

а потом узнайте, как сильно он менялся изо дня в день:

hot_factor = [ b-a for a, b in zip(searches[:-1], searches[1:]) ]
# hot_factor is [ -3, 7, -6, 1, 3, 43, 49, -4 ]

и просто примените какой-то порог, чтобы дни, когда рост был> 50, считались «горячими». Вы можете сделать это намного сложнее, если хотите. вместо абсолютной разницы вы можете взять относительную разницу, так что переход от 100 до 150 считается горячим, а от 1000 до 1050 - нет. или более сложный градиент, который учитывает тенденции на протяжении более чем одного дня.

person Autoplectic    schedule 24.04.2009
comment
Спасибо. Но я точно не знаю, что такое градиент и как с ним работать. Прости! - person caw; 26.04.2009
comment
Спасибо. Итак, я должен построить вектор, содержащий дневную частоту, верно? Я уверен, что относительные значения были бы лучше. Пример: я бы сказал, что рост от 100 до 110 не так хорош, как рост от 1 до 9. Но разве нет векторной функции, которую я могу использовать для поиска самых горячих тем? Недостаточно только оценки относительных значений, не так ли? Рост со 100 до 200 (100%) не так хорош, как рост с 20 000 до 39 000 !? - person caw; 27.04.2009
comment
На какой веб-сайт вы это добавляете? Предложение @ Autoplectic подсчитывать ежедневные изменения поисковых запросов не подходит для чего-то вроде популярного форума, где у вас есть тысячи тем, и каждый день определяются новые. - person Quantum7; 28.04.2009
comment
Вы правы, мне нужен алгоритм для огромных объемов данных, тысяч тем в час. - person caw; 02.05.2009
comment
это плохая стратегия. Таким образом, общее увеличение на 50 запросов о Бритни Спирс так же горячо, как и более 50 запросов о новом референдуме в Европе. - person Iman Akbari; 05.07.2016
comment
Я бы предложил b/a, а не b-a, поскольку даже небольшое изменение популярных тем приведет к огромной абсолютной разнице. - person DollarAkshay; 20.02.2019

Если вы просто посмотрите твиты или статусные сообщения, чтобы узнать о своих темах, вы столкнетесь с большим шумом. Даже если убрать все стоп-слова. Один из способов получить лучшее подмножество кандидатов в темы - сосредоточиться только на твитах / сообщениях, имеющих общий URL-адрес, и получить ключевые слова из заголовков этих веб-страниц. И убедитесь, что вы применяете POS-теги, чтобы получать существительные и словосочетания.

Заголовки веб-страниц обычно более информативны и содержат слова, описывающие содержание страницы. Кроме того, совместное использование веб-страницы обычно связано с публикацией последних новостей (например, если такая знаменитость, как Майкл Джексон, умерла, многие люди поделятся статьей о его смерти).

Я проводил эксперименты, в которых я беру только популярные ключевые слова из заголовков, а затем получаю общее количество этих ключевых слов во всех статусных сообщениях, и они определенно устраняют много шума. Если вы сделаете это таким образом, вам не понадобится сложный алгоритм, просто выполните простой порядок частот ключевых слов, и вы на полпути.

person Henley    schedule 14.08.2013

Вы можете использовать логарифмические отношения правдоподобия, чтобы сравнить текущую дату с прошлым месяцем или годом. Это статистически верно (учитывая, что ваши события не имеют нормального распределения, что следует из вашего вопроса).

Просто отсортируйте все свои термины по logLR и выберите первую десятку.

public static void main(String... args) {
    TermBag today = ...
    TermBag lastYear = ...
    for (String each: today.allTerms()) {
        System.out.println(logLikelihoodRatio(today, lastYear, each) + "\t" + each);
    }
} 

public static double logLikelihoodRatio(TermBag t1, TermBag t2, String term) {
    double k1 = t1.occurrences(term); 
    double k2 = t2.occurrences(term); 
    double n1 = t1.size(); 
    double n2 = t2.size(); 
    double p1 = k1 / n1;
    double p2 = k2 / n2;
    double p = (k1 + k2) / (n1 + n2);
    double logLR = 2*(logL(p1,k1,n1) + logL(p2,k2,n2) - logL(p,k1,n1) - logL(p,k2,n2));
    if (p1 < p2) logLR *= -1;
    return logLR;
}

private static double logL(double p, double k, double n) {
    return (k == 0 ? 0 : k * Math.log(p)) + ((n - k) == 0 ? 0 : (n - k) * Math.log(1 - p));
}

PS TermBag - это неупорядоченный набор слов. Для каждого документа вы создаете один пакет терминов. Просто посчитайте количество появлений слов. Затем метод occurrences возвращает количество вхождений данного слова, а метод size возвращает общее количество слов. Лучше всего как-то нормализовать слова, обычно достаточно toLowerCase. Конечно, в приведенных выше примерах вы создадите один документ со всеми запросами сегодняшнего дня и один со всеми запросами за последний год.

person akuhn    schedule 04.05.2009
comment
Извините, я не понимаю код. Что такое TermBags? Было бы здорово, если бы вы могли вкратце объяснить, что делает этот код. - person caw; 05.05.2009
comment
TermBag - это набор терминов, т. Е. Класс должен иметь возможность ответить на общее количество слов в тексте и количество вхождений каждого слова. - person akuhn; 12.05.2009

Идея состоит в том, чтобы отслеживать такие вещи и замечать, когда они значительно подскакивают по сравнению с их собственным базовым уровнем.

Итак, для запросов, которые имеют более определенного порога, отслеживайте каждый из них, и когда он изменяется на некоторое значение (скажем, почти вдвое) от своего исторического значения, это новая горячая тенденция.

person Joshua    schedule 05.05.2009