Django:按日期求和,然后创建显示滚动平均值的额外字段

问题描述 投票:0回答:1

我想按日期在模型中创建一个总和,然后在一个查询中添加一个滚动平均值……。这可能吗?

假设我有一张这样的桌子,叫做“销售”:

|---------------------|------------------|--------------|
|     Date            |     Category     |     Value    |
|---------------------|------------------|--------------|
|      2020-04-01     |         1        |      55.0    |
|---------------------|------------------|--------------|
|      2020-04-01     |         2        |      30.0    |
|---------------------|------------------|--------------|
|      2020-04-02     |         1        |      25.0    |
|---------------------|------------------|--------------|
|      2020-04-02     |         2        |      85.0    |
|---------------------|------------------|--------------|
|      2020-04-03     |         1        |      60.0    |
|---------------------|------------------|--------------|
|      2020-04-03     |         2        |      30.0    |
|---------------------|------------------|--------------|

我想按日期对其进行分组(“类别”列不重要)并添加日期值的总和。然后我想添加过去 7 天的滚动平均值。

我试过这个:

days = (
        Sales.objects.values('date').annotate(sum_for_date=Sum('value'))
        ).annotate(
                rolling_avg=Window(
                         expression=Avg('sum_for_date'),
                         frame=RowRange(start=-7,end=0),
                         order_by=F('date').asc(),
                    )
                )
        .order_by('date')

这会引发此错误:

django.core.exceptions.FieldError: Cannot compute Avg('sum_for_date'): 'sum_for_date' is an aggregate

有什么想法吗?

django window-functions django-orm
1个回答
0
投票

你得到的错误是因为你试图计算同一查询中聚合字段('sum_for_date')的平均值。这对于 Django 的 ORM 是不可能的。

一种可能的解决方法是使用子查询来计算每日总和,然后使用另一个查询来计算滚动平均值。这是使用您的销售模型的示例:

from django.db.models import Avg, Sum, F, Window
from django.db.models.functions import Lag

# Subquery to calculate daily sums
daily_sums = (
    Sales.objects
    .values('date')
    .annotate(sum_for_date=Sum('value'))
)

# Query to calculate rolling average
rolling_avg = (
    daily_sums
    .annotate(
        rolling_sum=Window(
            expression=Sum('sum_for_date'),
            frame=Window(
                expression=Lag('sum_for_date', offset=6),
                start=0,
            ),
        ),
        rolling_count=Window(
            expression=Sum(1),
            frame=Window(
                expression=Lag('date', offset=6),
                start=0,
            ),
        ),
    )
    .annotate(
        rolling_avg=F('rolling_sum') / F('rolling_count')
    )
    .order_by('date')
)

这首先使用子查询计算每日总和,然后使用窗口函数计算滚动平均值。 rolling_sum 窗口函数计算前 7 天的总和,rolling_count 窗口函数计算前 7 天的日期计数。最后,我们将 rolling_sum 除以 rolling_count 得到滚动平均值。

请注意,此方法使用 Window 函数,这些函数仅在某些数据库(例如 PostgreSQL)中可用。如果您使用不同的数据库,您可能需要使用不同的方法来计算滚动平均值。

有关

Window
的更多信息:https://docs.djangoproject.com/en/dev/ref/models/expressions/#window-functions

© www.soinside.com 2019 - 2024. All rights reserved.