[algorithm] 인기 주제 또는 태그를 계산하는 가장 좋은 방법은 무엇입니까?
많은 사이트에서 “지난 24 시간 동안 가장 인기있는 주제”와 같은 통계를 제공합니다. 예를 들어 Topix.com은 “뉴스 트렌드”섹션에이를 표시합니다. 여기에서 가장 많이 인용되는 주제를 볼 수 있습니다.
주제에 대한 “버즈”도 계산하고 싶습니다. 어떻게해야합니까? 알고리즘은 항상 뜨겁지 않은 주제에 가중치를 부여해야합니다. 일반적으로 (거의) 아무도 언급하지 않는 주제는 가장 인기있는 주제 여야합니다.
Google은 “Hot Trends”, topix.com은 “Hot Topics”를, fav.or.it는 “Keyword Trends”를 보여줍니다. 이러한 모든 서비스에는 공통점이 있습니다.
“브리트니 스피어스”, “날씨”또는 “파리 힐튼”과 같은 용어는 항상 뜨겁고 자주 있기 때문에이 목록에 나타나지 않습니다. 이 기사는 이것을 “브리트니 스피어스 문제”라고 부릅니다.
내 질문 : 어떻게 알고리즘을 코딩하거나 기존 알고리즘을 사용 하여이 문제를 해결할 수 있습니까? 지난 24 시간 동안 검색된 키워드 목록이 있으면 알고리즘은 가장 인기있는 10 개의 키워드를 표시해야합니다.
위의 기사에서 일종의 알고리즘이 언급되어 있음을 알고 있습니다. PHP로 코드를 작성하려고했지만 작동하지 않을 것이라고 생각합니다. 그것은 단지 대다수를 발견하지 않습니까?
나는 당신이 나를 도울 수 있기를 바랍니다 (코딩 예제가 좋을 것입니다).
답변
이 문제는 다른 사람들이 언급했듯이 이력 데이터의 표준 편차뿐만 아니라 이력 데이터의 표준 편차를 고려하여 평균을 사용하는 것보다 더 강력한 z 점수 또는 표준 점수를 요구합니다.
귀하의 경우 z- 점수는 다음 공식으로 계산되며 추세는 조회 / 일과 같은 비율입니다.
z-score = ([current trend] - [average historic trends]) / [standard deviation of historic trends]
z- 점수가 사용될 때 z- 점수가 높거나 낮을수록 추세가 비정상적으로 증가하므로, 예를 들어 z- 점수가 매우 긍정적이면 추세가 비정상적으로 상승하는 반면, 매우 음수이면 비정상적으로 하락합니다. . 따라서 모든 후보 트렌드에 대해 z- 점수를 계산하면 최고 10 개의 z- 점수가 가장 비정상적으로 증가하는 z- 점수와 관련됩니다.
z- 점수에 대한 자세한 내용 은 Wikipedia 를 참조하십시오 .
암호
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()
-
이 방법을 사용하면 작업 흐름은 다음과 같습니다. 각 주제, 태그 또는 페이지에 대해 데이터베이스에서 총 일 수,보기 합계 및보기 합계에 대해 부동 소수점 필드를 작성하십시오. 기록 데이터가있는 경우 해당 데이터를 사용하여 이러한 필드를 초기화하고 그렇지 않으면 0으로 초기화하십시오. 하루가 끝나면 세 개의 데이터베이스 필드에 저장된 히스토리 데이터에 대한 일 수를 사용하여 z 점수를 계산하십시오. Xz 점수가 가장 높은 주제, 태그 또는 페이지는 오늘의 X “호스트 트렌드”입니다. 마지막으로 3 개의 각 필드를 일 값으로 업데이트하고 내일 프로세스를 반복하십시오.
새로운 추가
위에서 논의 된 일반적인 z- 점수는 데이터의 순서를 고려하지 않으므로 ‘1’또는 ‘9’의 관측에 대한 z- 점수가 시퀀스 [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
최신 정보
David Kemp가 올바르게 지적했듯이 일련의 상수 값이 주어지면 다른 값과 다른 관측 값에 대한 zscore가 요청되면 결과는 아마도 0이 아니어야합니다. 실제로 반환 된 값은 무한대 여야합니다. 그래서 나는이 줄을 바꿨다.
if self.std() == 0: return 0
에:
if self.std() == 0: return (obs - self.avg) * float("infinity")
이 변경 사항은 fazscore 솔루션 코드에 반영됩니다. 무한 값을 처리하지 않으려는 경우 수용 가능한 해결책은 대신 행을 다음과 같이 변경하는 것입니다.
if self.std() == 0: return obs - self.avg
답변
주제의 속도를 측정하는 알고리즘이 필요합니다. 다시 말하면 그래프를 작성하면 놀라운 속도로 올라가는 것을 보여주고 싶습니다.
이것은 추세선의 첫 번째 파생물이며 전체 계산의 가중 요소로 통합하는 것은 어렵지 않습니다.
정규화
당신이해야 할 한 가지 기술은 모든 데이터를 정규화하는 것입니다. 관심있는 각 주제에 대해 해당 주제의 기준을 정의하는 매우 낮은 통과 필터를 유지하십시오. 이제 해당 주제에 관한 모든 데이터 포인트가 정규화되어야합니다. 기준선을 빼면 모든 주제가 0에 가까워지고 위와 아래에 급상승이 발생합니다. 대신 신호를 기준선 크기로 나누면 신호가 약 1.0이됩니다. 이렇게하면 모든 신호가 서로 일치 할뿐 아니라 (기준선을 정규화 함) 스파이크도 정규화됩니다. 브리트니 스파이크가 다른 사람의 스파이크보다 크기가 크지 만주의를 기울여야한다는 의미는 아닙니다. 스파이크가 기준선에 비해 매우 작을 수 있습니다.
파생
모든 것을 정규화 한 후에는 각 주제의 기울기를 파악하십시오. 연속 된 두 점을 취하여 차이를 측정합니다. 양의 차이는 추세가 증가하고 음의 차이는 추세가 감소합니다. 그런 다음 정규화 된 차이점을 비교하고 다른 주제와 비교하여 인기가 높아지고있는 주제를 확인할 수 있습니다. 각 주제는 다른 주제와는 다른 순서로 나타날 수있는 자체 ‘정상’에 맞게 조정됩니다.
이것은 실제로 문제의 첫 번째 단계입니다. 더 많은 고급 기술이 필요하지만 (주로 위 알고리즘과 다른 알고리즘을 조합하여 필요에 맞게 가중치를 부여 함) 시작하기에 충분해야합니다.
기사에 대해
이 기사는 주제 동향에 관한 것이지만 인기있는 것과 그렇지 않은 것을 계산하는 방법에 관한 것이 아니라 Lycos 및 Google과 같은 곳에서 그러한 알고리즘이 처리해야하는 방대한 양의 정보를 처리하는 방법에 관한 것입니다. 각 주제에 카운터를 제공하고 검색 할 때 각 주제의 카운터를 찾는 데 필요한 공간과 시간은 엄청납니다. 이 기사는 그러한 작업을 시도 할 때 직면하는 문제에 관한 것입니다. 브리트니 효과에 대해서는 언급하지만 그것을 극복하는 방법에 대해서는 이야기하지 않습니다.
답변
차드 버치 (Chad Birch)와 아담 데이비스 (Adam Davis)는 기준을 세우기 위해 뒤를 돌아보아야한다는 점에서 맞습니다. 문구에 따르면 귀하의 질문에 따르면 지난 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
평범한 것 같지 않습니까? 다음 입력이 5인데도 어떻게 값이 1에 가깝게 유지되었는지 확인하십시오. 수학을 확장하면 다음과 같은 결과를 얻습니다.
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이 상대적으로 작 으면, 상당한 양의 무게가 남습니다. 원래 입력에.
위의 공식을 연구하면이 사용법에 대해 몇 가지 사실을 알아야합니다.
- 모든 데이터는 평생 평균 에 무언가 기여 합니다 . 실제로, 기여가 실제로 아주 작은 지점이 있습니다.
- 최근 값은 이전 값보다 더 많이 기여합니다.
- b가 높을수록 새로운 값이 덜 중요하고 더 오래된 값이 중요합니다. 그러나 b가 높을수록 a의 초기 값을 낮추기 위해 더 많은 데이터가 필요합니다.
처음 두 가지 특성이 정확히 당신이 찾고있는 것이라고 생각합니다. 간단한 구현 아이디어를 제공하기 위해 다음은 파이썬 구현입니다 (모든 데이터베이스 상호 작용 제외).
>>> 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
답변
일반적으로 “버즈”는 지수 / 로그 붕괴 메커니즘의 일부 형식을 사용하여 파악됩니다. Hacker News, Reddit 및 기타 사용자가이를 간단한 방법으로 처리하는 방법에 대한 개요는 이 게시물을 참조하십시오 .
이것은 항상 인기있는 것들을 완전히 다루지는 않습니다. 찾고있는 것은 Google의 ” 핫 트렌드 “기능 과 같은 것 같습니다 . 이를 위해 현재 값을 기록 값으로 나눈 다음 노이즈 임계 값보다 낮은 값을 뺄 수 있습니다.
답변
나는 그들이 당신이 주목해야 할 핵심 단어는 “정상적으로”라고 생각합니다. 어떤 것이 “비정상”인지 확인하려면 무엇이 정상인지 알아야합니다. 즉, 특정 쿼리의 정상 비율을 찾기 위해 평균을 내릴 수있는 기록 데이터가 필요합니다. 평균 계산에서 비정상적인 일을 제외하고 싶을 수도 있지만, 아직 충분한 데이터가 필요하므로 제외 할 일을 알 수 있습니다.
여기에서 임계 값을 설정해야합니다 (실험이 필요합니다). 만약 임계 값을 벗어나는 것이 정상보다 검색이 50 % 더 많다면이를 “추세”라고 생각할 수 있습니다. 또는 위에서 언급 한 “최고 X 최신 유행”을 찾으려면 정상 속도에서 얼마나 떨어져 있는지 (백분율 기준) 사물을 주문하면됩니다.
예를 들어, 과거 데이터에 따르면 브리트니 스피어스는 일반적으로 10 만 건의 검색을, 파리 힐튼은 보통 5 만 건의 검색을 받았다고 가정합니다. 둘 다 평소보다 10,000 번 더 검색을받는 날이 있다면 브리트니보다 파리가 “호터”라고 생각해야합니다. 브리트니의 검색은 평소보다 20 % 더 증가한 반면 브리트니는 10 %에 불과했습니다.
하나님, 나는 브리트니 스피어스와 패리스 힐튼의 “뜨거움”을 비교 한 단락을 썼다는 것을 믿을 수 없습니다. 나 한테 무슨 짓을 한거야?
답변
그런 경우 규칙적인 물리 가속 공식을 사용할 수 있는지 궁금합니다.
v2-v1/t or dv/dt
v1을 시간당 초기 좋아요 / 투표 / 설명 횟수로 간주하고 v2를 지난 24 시간 동안 시간당 현재 “속도”로 간주 할 수 있습니까?
이것은 답변보다 질문과 비슷하지만 효과가있는 것 같습니다. 가장 빠른 가속을 가진 모든 컨텐츠가 인기 주제가 될 것입니다 …
이것이 브리트니 스피어스 문제를 해결할 수 없을 것이라고 확신합니다 🙂
답변
아마도 주제 빈도의 간단한 그라디언트가 작동 할 것입니다.
가장 쉬운 방법은 매일 검색된 수를 비우는 것입니다.
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은 그렇지 않습니다. 또는 하루 이상의 요즘 추세를 고려한보다 복잡한 기울기입니다.