使用 Django ORM 进行快速移动平均计算
Posted
技术标签:
【中文标题】使用 Django ORM 进行快速移动平均计算【英文标题】:Fast moving average computation with Django ORM 【发布时间】:2018-07-25 05:04:10 【问题描述】:我们运行 Postgres 9.6.5 和 Django 2.0。我们有一个Model
字段created_at
和value
。我们需要计算某个date_range
的 90 天移动平均线。我们就是这样做的:
output = []
for i in range(len(date_range)):
output.append(
Model.objects.filter(
created_at__date__range=(date_range[i]-timezone.timedelta(days=90), date_range[i]),
).aggregate(Avg('value'))['value__avg'].days
)
这使用Avg
聚合函数,因此速度相当快,但是我们需要对date_range
中的每个日期进行一次查询。对于更长的范围,这意味着很多查询。
Postgres can do this in a single query。我的问题是 - 我们可以使用 Django ORM 在单个查询中以某种方式做到这一点吗?
(我知道我可以使用 Django ORM 执行原始 SQL,但我想尽可能避免这种情况,这就是我问的原因。)
【问题讨论】:
【参考方案1】:假设您每个日期有一个条目,您可以使用 Django 2.0 的新窗口表达式在单个查询中计算 90 周期移动平均值:
from django.db.models import Avg, F, RowRange, Window
items = Model.objects.annotate(
avg=Window(
expression=Avg('value'),
order_by=F('created_at').asc(),
frame=RowRange(start=-90,end=0)
)
)
如果您想改为使用特定字段值框住,另请参阅 ValueRange,例如,如果您每天都有多行,这可能会派上用场。
【讨论】:
【参考方案2】:再试一次。这更高效,因为它只使用一个查询,但从数据库中获取所有必需的模型实例以在 python 中而不是数据库级别中执行逻辑。仍然不是最佳的,但希望这次它做对了;)您必须比较它是否真的在您的情况下提高了性能。
import numpy as np
instances = Model.objects.filter(
created_at__gte=min(date_range)-timezone.timedelta(days=90),
created_at__lte=max(date_range)
).values('created_at', 'value')
instances = list(instances) # evaluate QuerySet and hit DB only once
output = []
for i in range(len(date_range)):
output.append(
np.mean(np.array([inst.value for inst in instances if \
inst.created_at >= date_range[i]-timezone.timedelta(days=90) and \
inst.created_at < date_range[i]
]))
)
【讨论】:
【参考方案3】:您可以使用annotation,而不是聚合。在我开始测试时考虑这一点,我不完全确定下面的代码。另请参阅有关 F()
对象的文档
Model.objects.annotate(
value_avg=Avg(
'value',
filter=Q(
created_at__date__range=(
F('created_at__date')-timezone.timedelta(days=90),
F('created_at__date')
)
)
)
)
your_date_field
取决于你
【讨论】:
谢谢。我看不到您的解决方案中的逻辑。我需要日期范围内每一天的移动平均线。如果我将它用于模型实例,为什么会有帮助? 我认为每个日期都与您的模型相关联。但如果它是独立的,我的方法确实行不通。 是的,但它是一对多的关系。一个日期可以有多个模型实例。很抱歉没有在问题中澄清这一点。以上是关于使用 Django ORM 进行快速移动平均计算的主要内容,如果未能解决你的问题,请参考以下文章