如何在Django QuerySet中按月份计算平均值?
计算降雨量月度平均值的Django查询问题
我一直尝试计算降雨量的月度平均值,数据包含日期、小时和数值(小时不影响,需计算每月所有数值的平均值)。
模型定义
class TimeSeries(models.Model): station = models.ForeignKey(Station, on_delete=models.CASCADE) date = models.DateField() hour = models.IntegerField() value = models.FloatField()
我已尝试的代码
delta = relativedelta(months=1) dateStart = TimeSeries.objects.values('date').order_by('date').values_list('date', flat=True).first() dateEnd = TimeSeries.objects.values('date').order_by('date').values_list('date', flat=True).last() for i in range(len(stationNumbers)): while dateStart <= dateEnd: timeSeries = TimeSeries.objects.filter( station__number=stationNumbers[i], date__range=[dateStart, dateStart+delta] ).annotate( month=TruncMonth('date') ).values( 'station__number', 'month' ).annotate( value=Avg('value') ).order_by( 'station__number', 'month' )
我对查询语句做了一些修改,但始终无法生效。作为Django新手,想咨询是否需要使用原生SQL,并恳请各位提供解决方案,提前感谢!
补充说明1:
每天有24条记录(对应一天24小时),即数据按小时存储,重复的日期是否会影响月度平均值的计算?
补充说明2:
数据集示例(数据跨度3年):
| Name | Date | Time | Value |
|---|---|---|---|
| Station B | 01/01/2015 | 0:00 | 55 |
| Station B | 01/01/2015 | 1:00 | 51 |
| Station B | 01/01/2015 | 2:00 | 54 |
| Station B | 01/01/2015 | 3:00 | 58 |
解决方案
不需要使用原生SQL,Django ORM完全可以高效实现需求。
核心问题分析
你的代码存在几个明显问题:
while循环中未更新dateStart,导致无限循环,始终处理同一个月的数据date__range的范围逻辑错误,比如dateStart+delta会包含下一个月的起始日期记录,导致跨月统计- 无需手动遍历站点和日期范围,ORM的分组查询可以一次性完成所有统计
正确查询代码
1. 获取所有站点的月度平均值
from django.db.models import Avg from django.db.models.functions import TruncMonth monthly_avg = TimeSeries.objects.annotate( # 将date字段截断到月份,用于分组 month=TruncMonth('date') ).values( # 按站点编号和月份分组 'station__number', 'month' ).annotate( # 计算该站点该月的所有value的平均值 avg_value=Avg('value') ).order_by('station__number', 'month')
2. 仅获取指定站点的月度平均值
如果只需要统计stationNumbers列表中的站点,添加filter即可:
monthly_avg = TimeSeries.objects.filter( station__number__in=stationNumbers ).annotate( month=TruncMonth('date') ).values( 'station__number', 'month' ).annotate( avg_value=Avg('value') ).order_by('station__number', 'month')
补充说明解答
重复日期不会影响月度平均值的计算。Avg('value')会计算该分组(站点+月份)下所有记录的数值平均值,不管同一天有多少条小时记录,都会被纳入计算,这正好符合你“每月所有数值的平均值”的需求。
内容的提问来源于stack exchange,提问作者Muhammad Waqar Anwar
相关产品推荐
相关产品推荐

