首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django -用与事件的开始DateTime重叠的参与者和对每个事件进行注释

Django -用与事件的开始DateTime重叠的参与者和对每个事件进行注释
EN

Stack Overflow用户
提问于 2019-03-24 07:42:29
回答 1查看 112关注 0票数 1

我有一个带有start DateTime和end DateTime的事件模型,以及参与者的数量。

对于每个事件对象,我希望得到与start DateTime重叠的任何事件中所有参与者的注释和。这样我就可以确保在任何时候都不会有太多的参与者。

代码语言:javascript
复制
class Event(models.Model):
    start = models.DateTime()
    end = models.DateTime()
    participants = models.IntegerField()

我一直在读关于窗口函数的文章,也许这在这里是可行的,但是我不能正确地理解它。

我尝试过这样做,但它不起作用,因为它希望用相同的start DateTime将事件组合在一起,而不是将开始和结束的DateTimes与原始的事件启动DateTime重叠起来。

代码语言:javascript
复制
starts = Event.objects.annotate(
    participants_sum=Window(
    expression=Sum('participants'),
    partition_by=[F('start'),],
    order_by=ExtractDay('start').asc(),
    ),
).values('participants', 'participants_sum', 'start')

如有任何建议,将不胜感激!

非常感谢@endre--在他/她的帮助下,我能够解决更大的问题。

最终结果--我希望在Events表中得到每个开始和结束转换的值,这样我就可以确定有太多参与者的时间段。但我担心解释太复杂了。

这是我最后得到的

代码语言:javascript
复制
from django.contrib.gis.db import models
from django.db.models import F, Window, Sum
from django.utils import timezone

overlap_filter_start = Q(start__lte=OuterRef('start'), end__gte=OuterRef('start'))
overlap_filter_end = Q(start__lte=OuterRef('end'), end__gte=OuterRef('end'))

subquery_start = Subquery(Event.objects
    .filter(overlap_filter_start)
    .annotate(sum_participants=Window(expression=Sum('participants'),))
    .values('sum_participants')[:1],
    output_field=models.IntegerField()
)

subquery_end = Subquery(Event.objects
    .filter(overlap_filter_end)
    .annotate(sum_participants=Window(expression=Sum('participants'),))
    .values('sum_participants')[:1],
    output_field=models.IntegerField()
)

# Will eventually filter the dates I'm checking over specific date ranges rather than the entire Event table
# but for simplicity, filtering from yesterday to tomorrow
before = timezone.now().date() - timezone.timedelta(days=1)
after = timezone.now().date() + timezone.timedelta(days=1)

events_start = Event.objects.filter(start__date__lte=after, start__date__gte=before).annotate(simultaneous_participants=subquery_start)
events_end = Event.objects.filter(end__date__lte=after, end__date__gte=before).annotate(simultaneous_participants=subquery_end)

# Here I combine the queries for *start* transition moments and *end* transition moments, and rename the DateTime I'm looking at to *moment*, and make sure to only return distinct moments (since two equal moments will have the same number of participants)

events = events_start.annotate(moment=F('start')).values('moment', 'simultaneous_participants').union(
    events_end.annotate(moment=F('end')).values('moment', 'simultaneous_participants')).order_by('moment').distinct()

for event in events:
    print(event)

print(events.count())

现在,我可以使用Python中相对较小的结果查询集和进程来确定参与者数量过高的地方,以及当参与者数量下降到可接受的水平时。

也许有一种更有效的方法来解决这个问题,但我对此很满意。比用Python做所有繁重的工作要好得多。

结果的输出如下所示:

代码语言:javascript
复制
{'simultaneous_participants': 45, 'moment': datetime.datetime(2019, 3, 23, 7, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 45, 'moment': datetime.datetime(2019, 3, 23, 11, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 40, 'moment': datetime.datetime(2019, 3, 23, 14, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 40, 'moment': datetime.datetime(2019, 3, 23, 15, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 35, 'moment': datetime.datetime(2019, 3, 23, 16, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 85, 'moment': datetime.datetime(2019, 3, 24, 19, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 125, 'moment': datetime.datetime(2019, 3, 25, 12, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 90, 'moment': datetime.datetime(2019, 3, 25, 12, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 135, 'moment': datetime.datetime(2019, 3, 25, 13, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 110, 'moment': datetime.datetime(2019, 3, 25, 18, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 160, 'moment': datetime.datetime(2019, 3, 25, 19, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 160, 'moment': datetime.datetime(2019, 3, 25, 20, 30, tzinfo=<UTC>)}
{'simultaneous_participants': 115, 'moment': datetime.datetime(2019, 3, 25, 22, 0, tzinfo=<UTC>)}
{'simultaneous_participants': 80, 'moment': datetime.datetime(2019, 3, 25, 23, 30, tzinfo=<UTC>)}
14
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-24 13:02:30

若要使用基于单个事件的某些条件筛选的聚合对Events进行注释,您需要每个事件单独的子查询。

此筛选器应有助于查找与特定时间范围重叠的所有事件:

代码语言:javascript
复制
overlap_filter = Q(start__lte=OuterRef('end'), end__gte=OuterRef('start'))

这将使您在开始时间之前或结束时开始所有事件,并在开始时或之后结束。过滤器将在子查询中使用,对于OuterRef,我们引用外部查询中的字段。

接下来是子查询。它是从子查询中获取聚合的出乎意料的困难,因为聚合并不懒惰(=它们立即执行),而Subquery需要这样做。解决办法之一是使用Window

代码语言:javascript
复制
subquery = Subquery(Event.objects
        .filter(overlap_filter)
        .annotate(sum_participants=Window(Sum('participants'),))
        .values('sum_participants')[:1],
    output_field=IntegerField()
)

最后,使用带注释的Events进行查询

代码语言:javascript
复制
events = Event.objects.annotate(simultaneous_participants=subquery)

请注意,虽然参与此计数的参与者与我们正在查看的Event重叠,但它们并不一定重叠--它们在Event期间的某个时间都存在,但并非所有参与者都同时在场--有些人可能在其他人到达之前就离开了。要计算实际出勤高峰,您需要查看较小的时间增量(取决于开始时间和结束时间是如何交错的)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55321684

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档