2012-02-14 15 views
8

Mam aplikację Django, w której potrzebuję zastosować prosty algorytm trendingu/rankingu. Jestem bardzo zagubiony jako:Decydowanie i implementacja algorytmu wyznaczania trendów w Django

Mam dwa modele, Book i Reader. Każdej nocy nowe książki są dodawane do mojej bazy danych. Liczba czytników każdej książki jest aktualizowana również każdej nocy, tzn. Jedna książka będzie miała wiele rekordów statystycznych czytnika (jeden rekord na każdy dzień).

W danym okresie (ostatni tydzień, ostatni miesiąc lub ostatni rok) chciałbym wymienić najpopularniejsze książki, jaki algorytm należy do tego użyć?

Popularność w żaden sposób nie musi być w czasie rzeczywistym, ponieważ liczba czytelników każdej książki jest aktualizowana tylko codziennie.

Znalazłem jeden artykuł, który został wymieniony w innym SO post that showed how they calculated trending Wikipedia articles, ale post tylko pokazał, jak obliczono bieżący trend.

Jak ktoś wskazał na SO, jest to bardzo prosty podstawowy algorytm trendów i oblicza tylko nachylenie między dwoma punktami danych, więc domyślam się, że pokazuje on trend między wczoraj i dziś.

ja nie szukam uber złożonego algorytmu trendów, takich jak te stosowane na Hacker News, Reddit, itp

mam tylko dwie osie danych, hrabia czytelnik i datą.

Wszelkie pomysły na temat tego, co i jak powinienem wdrożyć. Dla kogoś, kto nigdy nie pracował z jakąkolwiek statystyką/algorytmem, wydaje się to bardzo zniechęcającym przedsięwzięciem.

Z góry dziękuję wszystkim.

Odpowiedz

5

Prawdopodobnie najprostszą „algorytm” możliwe trendów mogę myśleć jest n-dniowa średnia ruchoma.Nie jestem pewien, w jaki sposób dane są skonstruowane, ale trzeba powiedzieć coś takiego:

books = {'Twilight': [500, 555, 580, 577, 523, 533, 556, 593], 
     'Harry Potter': [650, 647, 653, 642, 633, 621, 625, 613], 
     'Structure and Interpretation of Computer Programs': [1, 4, 15, 12, 7, 3, 8, 19] 
     } 

Prosta średnia ruchoma prostu zajmuje ostatnie n wartości i średnie im:

def moving_av(l, n): 
    """Take a list, l, and return the average of its last n elements. 
    """ 
    observations = len(l[-n:]) 
    return sum(l[-n:])/float(observations) 

Zapis plaster po prostu chwyta koniec końca listy, zaczynając od n-tej do ostatniej zmiennej. Średnia ruchoma to dość standardowy sposób na wygładzenie wszelkiego hałasu, który mógłby wprowadzić pojedynczy kolec lub dip. Funkcja może być używana tak:

book_scores = {} 
for book, reader_list in books.iteritems(): 
    book_scores[book] = moving_av(reader_list, 5) 

będziemy chcieli się bawić z liczbą dni, średnio ponad. A jeśli chcesz podkreślić najnowsze trendy, możesz również spojrzeć na użycie czegoś takiego jak weighted moving average.

Jeśli chciał skupić się na czymś, co wygląda mniej w absolutnej czytelnictwa i skupia się raczej na wzrost czytelnictwa, wystarczy znaleźć procentową zmianę w 30-dniowej średniej kroczącej i 5-dniowa średnia ruchoma:

d5_moving_av = moving_av(reader_list, 5) 
d30_moving_av = moving_av(reader_list, 30) 
book_score = (d5_moving_av - d30_moving_av)/d30_moving_av 

Dzięki tym prostym narzędziom masz dość elastyczności w zakresie podkreślania trendów z przeszłości i tego, jak bardzo chcesz wygładzić (lub nie wygładzić) skoki.

+0

HI Wilduck, sprawdziłem obliczenia EWMA, które przepisałeś. To wydaje się być dobrym rozwiązaniem dla mojego problemu. Jestem zdezorientowany, jak obliczyć wartość alfa "α". Czy masz jakieś pomysły, jak mogę to obliczyć? –

+0

@MridangAgarwalla Dobre wiadomości! Nie musisz tego obliczyć! Możesz wybrać dowolną liczbę od zera do jednej, gdzie liczba bliższa jednemu z rabatów starszych obserwacji jest szybsza. Twój wybór będzie zależał od tego, ile chcesz zdyskontować starszych wartości, abyś mógł bawić się nim, dopóki nie znajdziesz czegoś, co ci się podoba. – Wilduck

+0

Mimo to myślę, że prosta średnia krocząca (taka, która nie jest ważona wykładniczo) może działać równie dobrze dla twoich celów. Sugerowałbym najpierw wdrożenie prostszej wersji, a następnie zamianę wersji ważonej wykładniczo, jeśli uznasz, że nie jest zadowalająca. – Wilduck

0

Popularność jest łatwa; po prostu uruchomić liczyć na czytelników i aby przez to:

Book.objects.annotate(reader_count=Count('readers')).order_by('-reader_count') 

Trendy jest trudniejsze, gdyż jest to bardziej delta popularność, czyli które książki mają zyskuje większość czytelników niedawno. Jeśli chcesz czegoś takiego, będziesz potrzebować czegoś działającego za kulisami, aby zachować zapis liczby czytelników według daty.

0

Jako przykład można podać stackoverflow reputation ranking.

Użytkownik może zmienić widok: na miesiąc, za rok, ....

w Twoim przypadku: najbardziej czytać książki przez miesiąc, przez rok.

Aby to osiągnąć, należy codziennie oszczędzać liczbę czytelników każdej książki.

reader(date, book, total) 

Wtedy jest tak proste, jak:

Book.objects.filter( 
        boor__reader__date__gte = some_date 
        ).annotate(
          num_readers=Sum('book__reader__total') 
           ).order_by('-num_readers') 
+1

Nigdy tego nie robię. To najprostszy sposób na zabicie serwera sql. – iddqd

+0

@iddqd, Jesteś trochę apokaliptyczny. Połącz niektóre zasoby, które wyjaśniają twoje zdanie. – danihp

+1

Funkcje agregujące są bardzo powolne, pełne skanowanie jest bardzo powolne. Funkcje agregujące oraz pełne skanowanie są bardzo powolne. Aby utworzyć ranking w czasie, musisz przeczytać wszystkie dane. – iddqd

0

zrobiłbym to systemowo tak:

  1. Zrób listę najczęstszych pytań lub punktów danych użytkownik będzie zainteresowany, na przykład: 1,1 100 najbardziej popularnych książek w tym tygodniu 1.2 Top 100 Najpopularniejsze książki w tym miesiącu

  2. Po codziennej informacji o czytniku/książce. jest zaktualizowany, uruchomiłbym pracę (prawdopodobnie co noc), aby zaktualizować tabelę z tymi informacjami. Tabela będzie prawdopodobnie zawierać pola Book i ReaderDelta, gdzie ReadDelta jest zmianą w ReadCount na tydzień, miesiąc lub rok.

  3. Można również po prostu przechowywać codzienny czytnik ReaderDelta i przy wyszukiwaniu danych miesięcznych po prostu agregować dynamicznie ostatnie 30 dni według daty.

Powiązane problemy