2012-02-14 15 views
8

Tengo una aplicación Django en la que necesito implementar un algoritmo simple de tendencias/clasificación. Estoy muy perdido como:Decidir e implementar un algoritmo de tendencias en Django

Tengo dos modelos, Book y . Todas las noches, nuevos libros se agregan a mi base de datos. El número de lectores de cada libro también se actualiza todas las noches, es decir, un libro tendrá múltiples registros de estadísticas de lectores (un registro por cada día).

Durante un período determinado (la semana pasada, el mes pasado o el año pasado), me gustaría enumerar los libros más populares, ¿qué algoritmo debo usar para esto?

La popularidad no tiene que ser en tiempo real de ninguna manera porque el recuento de lectores de cada libro solo se actualiza diariamente.

Encontré un artículo al que se hizo referencia en otro SO post that showed how they calculated trending Wikipedia articles pero la publicación solo mostraba cómo se calculaba la tendencia actual.

Como alguien señaló en SO, es un algoritmo de línea de base muy simple y solo calcula la pendiente entre dos puntos de datos, así que supongo que muestra la tendencia entre ayer y hoy.

No estoy en busca de un algoritmo complejo de tendencias súper como los utilizados en Hacker News, Reddit, etc.

sólo tengo dos ejes, el recuento de datos lector y fecha.

Cualquier idea sobre qué y cómo debo implementar. Para alguien que nunca ha trabajado con nada relacionado con estadísticas/algoritmos, esto parece ser una tarea muy desalentadora.

Gracias de antemano a todos.

Respuesta

5

Probablemente el más simple posible tendencia "algoritmo" se me ocurre es el n días de media móvil.No estoy seguro de cómo se estructure sus datos, pero dice que tiene algo como esto:

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] 
     } 

una media móvil simple sólo toma los últimos n valores y promedios ellos:

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) 

La notación rebanada simplemente agarra el final de la lista, comenzando desde la enésima hasta la última variable. Un promedio móvil es una forma bastante estándar de suavizar cualquier ruido que pueda provocar un solo pico o una caída. La función podría usarse así:

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

Querrá jugar con la cantidad de días que promedia. Y si desea enfatizar tendencias recientes, también puede usar algo como un weighted moving average.

Si quería centrarse en algo que se parece menos al número de lectores absoluto y se centra en cambio en incrementos de lectores, sólo tiene que encontrar el porcentaje de cambio en los 30 días de media móvil y 5 días de media móvil:

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 

Con estas sencillas herramientas, tiene una buena cantidad de flexibilidad en la medida en que usted enfatiza las tendencias pasadas y cuánto quiere suavizar (o no) los picos.

+0

HI Wilduck, he estado buscando en el cálculo de EWMA que prescribió. Eso parece una buena opción para mi problema. Estoy confundido en cuanto a cómo calcular el valor de alfa 'α'. ¿Tienes alguna idea de cómo puedo calcular esto? –

+0

@MridangAgarwalla ¡Buenas noticias! ¡No tienes que calcularlo! Puede elegir cualquier número entre cero y uno, donde un número más cercano a uno descuenta las observaciones anteriores más rápido. Su elección dependerá de cuánto desee descontar los valores anteriores, para que pueda jugar con ellos hasta que encuentre algo que le guste. – Wilduck

+0

Dicho esto, creo que un promedio móvil simple (uno que no está ponderado exponencialmente) podría funcionar igual de bien para sus propósitos. Sugeriría implementar la versión más simple primero, y luego intercambiar en la versión ponderada exponencialmente si encuentra que no es satisfactoria. – Wilduck

0

La popularidad es fácil; que acaba de ejecutar un recuento de los lectores y por fin que:

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

de tendencias es más difícil, ya que esto es más un delta de popularidad, es decir, que los libros tienen ganancias de las mayoría de los lectores recientemente. Si quieres algo como esto, necesitarás algo que se ejecute detrás de escena para mantener un registro de recuentos de lectores por fecha.

0

Puede tomar stackoverflow reputation ranking como ejemplo.

El usuario puede cambiar de imagen: por mes, por año, ....

En su caso: El libro más leído por mes, por año.

Para lograr esto, debe ahorrar día a día la cantidad de lectores para cada libro.

reader(date, book, total) 

entonces es tan simple como:

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

Nunca haga esto. Es la forma más fácil de matar al servidor sql. – iddqd

+0

@iddqd, Eres un poco apocalíptico. Por favor, enlace algún recurso que explique su oración. – danihp

+1

Las funciones agregadas son muy lentas, las exploraciones completas son muy lentas. Las funciones de agregado más los análisis completos son muy, muy lentos. Para producir una clasificación de todos los tiempos, necesita leer todos los datos. – iddqd

0

lo haría de forma sistémica como esto:

  1. Haga una lista de las preguntas más comunes o los puntos de datos de un usuario se le interesa, por ejemplo: 1.1 Top 100 libros más populares esta semana 1.2 Los 100 mejores libros más populares este mes

  2. Después de su información diaria sobre el lector/libro. se actualiza, me gustaría ejecutar un trabajo (probablemente todas las noches) para actualizar una tabla de esta información. Table probablemente tenga campos Book y ReaderDelta donde ReaderDelta es el cambio en readerCount durante una semana, mes o año.

  3. También puede simplemente almacenar el ReaderDelta diario y al buscar datos de un mes, simplemente agregue dinámicamente los últimos 30 días por fecha.

Cuestiones relacionadas