【问题标题】:Django group by hourDjango 按小时分组
【发布时间】:2015-08-08 12:12:54
【问题描述】:

我在 Django 中有以下模型。

class StoreVideoEventSummary(models.Model):
    Customer = models.ForeignKey(GlobalCustomerDirectory, null=True, db_column='CustomerID', blank=True, db_index=True)
    Store = models.ForeignKey(Store, null=True, db_column='StoreID', blank=True, related_name="VideoEventSummary")
    Timestamp = models.DateTimeField(null=True, blank=True, db_index=True)
    PeopleCount = models.IntegerField(null=True, blank=True)

我想知道每小时进入商店的人数。

为了实现这一点,我尝试在 Timestamp 上按小时对行进行分组,并对 PeopleCount 列求和。

store_count_events = StoreVideoEventSummary.objects.filter(Timestamp__range=(start_time, end_time),
                                                       Customer__id=customer_id,
                                                       Store__StoreName=store)\
        .order_by("Timestamp")\
        .extra({
            "hour": "date_part(\'hour\', \"Timestamp\")"
        }).annotate(TotalPeople=Sum("PeopleCount"))

这似乎没有按小时对结果进行分组,它只是在查询集中的每一行中添加一个新列TotalPeople,其值与PeopleCount 相同。

【问题讨论】:

  • 我错过了一些东西..“StartTime”来自哪里? "hour": "date_part(\'hour\', \"StartTime\")"
  • 对不起,应该是Timestamp

标签: python django orm group-by


【解决方案1】:

把它分成两步

import itertools
from datetime import datetime


# ...

def date_hour(timestamp):
    return datetime.fromtimestamp(timestamp).strftime("%x %H")


objs = StoreVideoEventSummary.objects.filter(
    Timestamp__range=(start_time, end_time),
    Customer__id=customer_id,
    Store__StoreName=store
).order_by("Timestamp")

groups = itertools.groupby(objs, lambda x: date_hour(x.Timestamp))

# since groups is an iterator and not a list you have not yet traversed the list
for group, matches in groups:  # now you are traversing the list ...
    print(group, "TTL:", sum(1 for _ in matches))

这允许您按几个不同的标准进行分组

你只想要小时而不考虑日期,只需更改date_hour

def date_hour(timestamp):
   return datetime.fromtimestamp(timestamp).strftime("%H")

如果您想按星期几分组,您只需使用

def date_day_of_week(timestamp):
   return datetime.fromtimestamp(timestamp).strftime("%w %H")

并更新 itertools.groupby 的 lambda 以使用 date_day_of_week

【讨论】:

  • 完全忘记了itertools。在 Python 中进行分组是可行的,如果一切都失败了,我认为这就是我要采用的方法。如果可能的话,在数据库中这样做会很好,因为我想这会快得多。再说一次,我的数据集并没有那么大(著名的遗言!)。
  • 你会感到惊讶......它可能不会在数据库中更快:P
  • 这种方法似乎工作正常。我们没有遇到任何问题,可能是因为我们的数据集很小。
  • 因为你真的只迭代它一次它应该是 O(N) ......这应该很容易管理
  • @JoranBeasley +1 以获得最优雅的解决方案。它对我有用,但我想知道 sum(1 for _ in matches) 语法指的是什么?我想对此进行研究,但不知道该用谷歌搜索什么。
【解决方案2】:

构建你的原始代码,你能试试吗:

store_count_events = StoreVideoEventSummary.objects.filter(Timestamp__range=(start_time, end_time), Customer__id=customer_id, Store__StoreName=store)\
    .extra({
        "hour": "date_part(\'hour\', \"Timestamp\")"
    })\
    .values("hour")\
    .group_by("hour")\
    .annotate(TotalPeople=Sum("PeopleCount"))

【讨论】:

  • 似乎不起作用。它与前面的类似,唯一的区别似乎是我现在每行只得到 2 个字段 hourTotalPeople 而不是整行。
【解决方案3】:

我知道我在这里迟到了,但从文档 https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.extra 那里得到提示

下面的过滤器应该适合你。

store_count_events = StoreVideoEventSummary.objects.filter(
    Timestamp__range=(start_time, end_time),
    Customer__id=customer_id,
    Store__StoreName=store
).order_by(
    'Timestamp'
).extra(
    select={
        'hour': 'hour(Timestamp)'
    }
).values(
    'hour'
).annotate(
    TotalPeople=Sum('PeopleCount')
)

【讨论】:

  • 什么是 start_time 和 end_time 以及它们在哪里定义?
  • 小时在做什么(时间戳)?
猜你喜欢
  • 1970-01-01
  • 2020-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-27
  • 2019-11-24
相关资源
最近更新 更多