3 Stimmen

Berechnung und Speicherung von Durchschnittsdaten auf täglicher, wöchentlicher, monatlicher und jährlicher Basis

Ich habe gestern eine Frage auf SO gestellt mit dem Titel Entscheidung und Implementierung eines Trendalgorithmus in Django. Viele Leute schlugen einfache Dinge wie Durchschnitte (exponentiell, gewichtet, usw.) vor. Ich habe ein Modell namens Buch und ein anderes namens Leser:

class Buch(models.Model):
    name = models.charField()

class Leser(models.Model):
    datum = models.DateField()
    buch = models.ForeignKey(Buch)
    leser_count = models.PostiveIntegerField()

Eine einfache Struktur. Neue Bücher werden jeden Tag hinzugefügt und die Leserzahl für jedes Buch wird ebenfalls jeden Tag hinzugefügt. d.h. ein Buch wird täglich eine Leserzahl haben, mehrere Datensätze.

Ich muss die Durchschnitte für die Bücher für die aktuelle Woche, den aktuellen Monat und das aktuelle Jahr berechnen. Neben den aktuellen Daten möchte ich auch die historischen Daten beibehalten.

Wenn ich versuchen würde, solche Daten aus der Datenbank abzurufen, wäre das sehr aufwendig. Nicht wahr. Außerdem versuche ich, dieses System zu implementieren, indem ich zunächst einfache Durchschnittswerte verwende, später jedoch die Flexibilität haben möchte, meine Berechnungsmethode zu ändern. Ich habe zwei Optionen -

  • eine, bei der ich die Daten teilweise in einer anderen Tabelle aktualisieren kann, die die berechneten Daten jedes Mal speichert, wenn ein neuer Leser-Datensatz hinzugefügt wird.

  • zwei, bei der ich die aggregierten Daten jeden Abend durch ein Skript für den aktuellen Tag/Woche/Monat neu erstellen könnte.

Hier sind einige Beispieldaten und Ergebnisse.

Buch  Datum        Zählen
----  ----------  -----
AAAA  01.01.2012    10
AAAA  02.01.2012    20
AAAA  03.01.2012    30
AAAA  04.01.2012    30
AAAA  05.01.2012    40
AAAA  06.01.2012    10
AAAA  07.01.2012    25
AAAA  08.01.2012    15
AAAA  09.01.2012    10

Der Durchschnitt der Leserzahl für Woche #1 beträgt: 23,5. Der Leserzähldurchschnitt für Woche #2 (was in diesem Fall die aktuelle Woche wäre) beträgt: 12,5 ..und für den aktuellen Monat und das Jahr würde 21,1 betragen

HTH.

Um eine dieser Möglichkeiten auszuprobieren, möchte ich ein System zum Speichern der Daten entwickeln. Ich muss die Durchschnittswerte auf täglicher, wöchentlicher und monatlicher Basis speichern. Ich bin jedoch sehr verloren, was für eine Tabellenstruktur ich implementieren sollte? Ich würde gerne das Rad nicht neu erfinden, wenn möglich, also wenn jemand von Ihnen über Pakete Bescheid weiß, die mir dabei helfen können, wäre das großartig.

Danke.

3voto

Thomas Punkte 11298

Postgres ist sehr gut darin, diese Art von Berechnungen gleichzeitig mit Ihrem anderen Datenverkehr durchzuführen, daher machen Sie sich keine allzu großen Sorgen über die Last (solange Sie diese Art von Stapelverarbeitung außerhalb des Anfrage-Antwort-Zyklus ausführen).

Eine Sache, die Sie tun könnten, ist, diese Art von Arbeit in kleine, zwischenspeicherbare Einheiten aufzuteilen. Zum Beispiel ist der Durchschnitt eines Monats wirklich ein Durchschnitt der letzten 4 Wochen, der Durchschnitt eines Jahres ist ein Durchschnitt der letzten 12 Monate, und dies wird ohnehin nur auf Buchbasis durchgeführt, warum also nicht kleine Teile der Arbeit in der Anfrage erledigen.

from django.core.cache import cache
from datetime import timedelta

def cached(key, expire)
    def wrapped(f):
        def func(*args, **kwargs):
            result = cache.get(key%args%kwargs)
            if result is None:
                result = f(*args, **kwargs)
                cache.set(key%args%kwargs, result, expire)
            return result
        return func
    return wrapped

@cached("book:%s:avg:week:%s", 3600*24) #Cache für einen Tag, laufende Ergebnisse!
def book_read_week_average(book_id, week_start):
    week_end = week_start + timedelta(days=7)
    return Reader.objects.filter(book_id=book_id, date_gte=week_start, date_lt=week_end) \
                         .aggregate(Avg('count'))['count_avg']

@cached("book:%s:avg:month:%s", 3600*24) #Cache für einen Tag für laufende Ergebnisse
def book_read_month_average(book_id, month_start):
    month_end = month_start + timedelta(days=31)
    return Reader.objects.filter(book_id=book_id, date_gte=month_start, date_lt=month_end) \
                         .aggregate(Avg('count'))['count_avg']

@cached("author:%s:avg:month:%s", 3600*24)
def author_read_month_average(author_id, month_start):
    return sum(book_read_month_average( book.id )
               for book in Book.objects.filter(author_id=author_id) )

Verwenden von Funktionskomposition und gecachten Funktionen, werden nur die Daten generiert, die Sie benötigen, und nur wenn Sie sie benötigen. Sie können diese Informationen auch in Redis statt im Django-Cache speichern und die Vorteile von atomaren Inkrementen bei den Lesezählungen nutzen, um Echtzeit-Lesestatistiken zu ermöglichen.

1voto

sebpiq Punkte 7172

Ich habe django-cube gestartet, um genau diese Art von Problemen zu behandeln ([Siehe OLAP cube auf Wikipedia). Allerdings habe ich aufgrund von Zeitmangel keine ordentliche, effiziente Version davon erstellt ... also wird es leider in Ihrem Fall nicht funktionieren.

Da mich viele Leute nach django-cube gefragt haben, habe ich die Entwicklung erneut begonnen, in einem frischen Repository auf github.

Jetzt, mit viel mehr Erfahrung zu dem Problem als vor 2 Jahren (als ich meinen ersten Versuch startete), habe ich eine recht gute Vorstellung davon, was ich tun muss, und wie die API aussehen sollte; und ich werde sie langsam entwickeln, wenn ich Freizeit habe. Also bleiben Sie dran, und natürlich wäre jede Hilfe bei diesem Projekt sehr willkommen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X