【问题标题】:Bin a queryset using Django?使用 Django 对查询集进行分类?
【发布时间】:2018-05-09 03:15:08
【问题描述】:

假设我们有以下简单的模型:

class Category(models.Model):
    name = models.CharField(max_length=264)

   def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "categories"

class Status(models.Model):
    name = models.CharField(max_length=264)

   def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "status"

class Product(models.Model):
    title = models.CharField(max_length=264)
    description = models.CharField(max_length=264)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=10)
    status = models.ForeignKey(Status, on_delete=models.CASCADE)

我的目标是根据每种产品所属的价格区间获取一些统计数据,例如总产品、总销售额、平均销售额等。

因此,价格区间可能是 0-100、100-500、500-1000 等。

我知道如何使用 pandas 来做这样的事情: Binning column with python pandas

我正在寻找一种使用 Django ORM 的方法。

我的一个想法是将查询集转换为列表并应用函数来获取适当的价格箱,然后进行统计。

另一个我不确定如何实现的想法与上面的想法相同,但只需将 bin 函数应用于我感兴趣的查询集中的字段。

【问题讨论】:

  • 您可以在视图中做任何您想做的事情。这是python :)

标签: django


【解决方案1】:

我可以看到三种途径。

首先是编写您要直接使用的 SQL,然后通过修改模型管理器类将其放入数据库。 .objects.raw("[sql goes here]")This answer 展示了如何使用内容上的简单函数来定义组 - 类似的东西可以工作吗?

SELECT FLOOR(grade/5.00)*5 As Grade, 
       COUNT(*) AS [Grade Count]
FROM TableName
GROUP BY FLOOR(Grade/5.00)*5
ORDER BY 1

其次,正如您所提到的,您没有理由不能将查询集(使用 .values() 或 .values_list())移动到 pandas 数据框或类似的数据框中,然后将其装箱。将查询集放入数据帧然后处理它可能会降低效率,但我不确定它肯定或总是不好。如果它更容易编写和维护,那可能没问题。

我会尝试的第三种方法(我认为这是您真正想要的)是将 .annotate() 链接到它们所属的 bin 标记点,以及聚合计数函数来计算每个 bin 中有多少。这是比我做过的更高级的 ORM 工作,但我认为您会开始关注 docs section on conditional aggregation 之类的东西。我已经稍微调整了一下,首先创建了“price_class”列,并带有注释。

Product.objects.annotate(price_class=floor(F('price')/100).aggregate(
      class_zero=Count('pk', filter=Q(price_class=0)),
      class_one=Count('pk', filter=Q(price_class=1)),
      class_two=Count('pk', filter=Q(price_class=2)),    # etc etc
  )

我不确定“地板”是否会起作用,您可能需要“表达式包装器”来确保将 price_class 推送到 output_field 的写入类型中。一切顺利。

【讨论】:

  • 你能解释一下floor是什么吗?
  • Floor 将数字截断为最小的整数。它与整数除以 1 相同。Floor(1.2) = 1。Floor(2.9)=2。在这些示例中定义“箱”的快捷方式很有用。我直接从另一个答案中获取了 SQL,它可能有效;但我没有在“注释”语句中尝试它,我不能保证它在那里工作,虽然 floor 是 python 的一部分,但它可能不在 Django ORM 中,以我的建议有效的方式。跨度>
猜你喜欢
  • 1970-01-01
  • 2019-08-19
  • 1970-01-01
  • 2019-12-30
  • 2012-12-20
  • 2010-12-31
  • 2016-12-26
  • 2018-01-31
  • 1970-01-01
相关资源
最近更新 更多