Расчет и хранение средних данных на ежедневной, еженедельной, ежемесячной и годовой основе - PullRequest
2 голосов
/ 16 февраля 2012

Вчера я задал вопрос о SO под названием . Решение и реализация алгоритма трендов в Django . Многие люди предлагали простые вещи, такие как средние (экспоненциальные, взвешенные и т. Д.) У меня есть модель под названием Book, а другая - Readers:

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

class Reader(models.Model):
    date = models.DateField()
    book = models.ForeignKey(Book)
    reader_count = models.PostiveIntegerField()

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

Мне нужно рассчитать средние значения для Книг за текущую неделю, текущий месяц и текущий год. Помимо текущих данных, я бы хотел также сохранить исторические данные.

Если бы я попытался запросить данные такого рода из БД, это сильно ударило бы. Не так ли? Кроме того, я пытаюсь реализовать эту систему, используя простые средние значения для начала, но позже я хотел бы иметь гибкость в изменении моего вычислительного метода. У меня есть два варианта -

  • один, где я могу частично обновить данные в другой таблице, которая хранит вычисленные данные каждый раз, когда добавляется новая запись Reader.

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

Вот некоторые примеры данных и результатов.

Book  Date        Count
----  ----------  -----
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

Среднее количество читателей за неделю № 1: 23,5. Среднее количество читателей за неделю №2 (если бы это не было на текущей неделе): 12,5 ..и для текущего месяца и года будет 21,1

НТН.

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

Спасибо.

Ответы [ 2 ]

2 голосов
/ 17 февраля 2012

Postgres очень хорошо выполняет такие вычисления одновременно с вашим другим трафиком, поэтому не стоит слишком беспокоиться о нагрузке (если вы запускаете пакетное задание такого типа вне цикла запрос-ответ) .

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

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 for a day, rolling results!
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 for a day for rolling results
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) )

Используя композицию функций и кэшированные функции, вы генерируете только те данные, которые вам нужны, и только тогда, когда они вам нужны. Вы также можете хранить эту информацию в redis вместо кеша django и использовать атомарные приращения для счетчиков чтения, позволяя читать статистику в реальном времени.

1 голос
/ 16 февраля 2012

Я начал django-cube для решения именно такого рода проблем (см. OLAP cube в Википедии). Однако из-за нехватки времени мне не удалось получить правильную и эффективную версию ... так что, к сожалению, это не сработает в вашем случае.

Так как многие спрашивали меня о django-cube, я снова начал разработку, на новом репозитории на github .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...