计算和存储每日,每周,每月和每年的平均数据

时间:2012-02-15 20:36:18

标签: python django

我昨天在SO上问了一个题为Deciding and implementing a trending algorithm in 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

HTH。

为了给出任何一个镜头,我想构建一个存储数据的系统。我需要每天,每周和每月存储平均值。但是我很遗憾我应该实现什么样的表结构?如果可能的话,我不想重新发明轮子,所以如果你们中的任何人知道任何允许我完成这个任务的包裹,那就太棒了。

感谢。

2 个答案:

答案 0 :(得分:2)

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 :(得分:1)

我开始django-cube来处理这类问题(请参阅维基百科上的OLAP cube)。然而,由于时间不够,我没有设法得到一个合适,有效的版本...所以很遗憾,在你的情况下它不会做。

由于很多人一直在问我django-cube,我在一个新的存储库on github上重新开始了开发。

现在,除了2年前(当我第一次尝试时),我对这个问题有了更多的经验,我非常清楚我必须做什么,以及API应该是什么样的;当我有空闲时,我会慢慢发展它。所以请继续关注,当然,对该项目的任何帮助都会非常受欢迎。