Python Django 使用 annotate、aggregate 統計教學

更新日期: 2025 年 1 月 7 日

在 Django 專案中,經常需要對使用者的評論資料進行統計分析,例如計算各星等的分佈、總評論數以及平均評分等數據。

這篇文章將詳細解析如何透過 Django ORM 進行這些計算,並搭配圖表與範例數據,讓新手能夠更直觀地理解整個流程。


comments 是什麼?

comments = Comment.objects.filter(service=service, is_deleted=False)

解釋

  • comments 是一個 QuerySet 物件,代表從資料庫中查詢出來的所有符合條件的留言記錄。
  • 這段程式碼篩選了 與特定服務 (service) 相關,且 未被軟刪除 (is_deleted=False) 的評論資料。

範例數據

+----+---------+--------+
| ID | Rating  | User   |
+----+---------+--------+
| 1  |   5     | Alice  |
| 2  |   4     | Bob    |
| 3  |   5     | Carol  |
| 4  |   3     | David  |
| 5  |   5     | Eve    |
| 6  |   4     | Frank  |
| 7  |   5     | Grace  |
+----+---------+--------+

使用 .values('rating') 回傳指定欄位

comments.values('rating')

解釋

  • .values('rating') 會將 rating 欄位的數據轉換成 字典格式,方便後續進行統計計算。

範例輸出

[
    {'rating': 5},
    {'rating': 4},
    {'rating': 5},
    {'rating': 3},
    {'rating': 5},
    {'rating': 4},
    {'rating': 5}
]

使用 annotate(Count('rating')) 分組統計

from django.db.models import Count

rating_summary = comments.values('rating').annotate(count=Count('rating'))

使用 .annotate() 時,Django 會一次幫你完成以下 三件事

👉 分組 (Grouping)把相同的數據分類成一組

  • 使用 .values('欄位名稱') 來告訴 Django 依據哪些欄位來分組。
  • 例如:values('rating') 會根據 rating 欄位將相同星等的評論分成一組。

👉 計算 (Calculation)針對每組數據計算出一個數值

  • 透過 Count()Sum()Avg() 等 Django 提供的聚合函數來計算統計數據。
  • 例如:
    • Count('rating') → 計算每個星等的數量
    • Avg('rating') → 計算每組的平均評分
    • Sum('rating') → 計算每組的總分

👉 新增欄位 (Adding a Calculated Field)把計算結果放到一個新的欄位中

計算結果會自動成為查詢結果中的新欄位,名稱由你指定 (例如:counttotalaverage 等)。

.annotate()Count() 的實作步驟解析

以下以計算評論的星等分佈為例,逐步拆解這段程式碼的工作原理:

rating_summary = Comment.objects.values('rating').annotate(count=Count('rating'))

取得原始資料

+----+---------+--------+
| ID | Rating  | User   |
+----+---------+--------+
| 1  |   5     | Alice  |
| 2  |   4     | Bob    |
| 3  |   5     | Carol  |
| 4  |   3     | David  |
| 5  |   5     | Eve    |
| 6  |   4     | Frank  |
| 7  |   5     | Grace  |
+----+---------+--------+

.values('rating') 分組

使用 .values('rating')rating 欄位的資料分組:

[
  {'rating': 5},
  {'rating': 4},
  {'rating': 5},
  {'rating': 3},
  {'rating': 5},
  {'rating': 4},
  {'rating': 5}
]

.annotate(Count('rating')) 統計數量

針對每個分組計算 rating 的數量:

+---------+-------+
| Rating  | Count |
+---------+-------+
|    5    |   4   |
|    4    |   2   |
|    3    |   1   |
+---------+-------+

儲存到 rating_summary

結果儲存在變數 rating_summary 中,以便進一步使用。

rating_summary = [
    {'rating': 5, 'count': 4},
    {'rating': 4, 'count': 2},
    {'rating': 3, 'count': 1}
]

.annotate() 就像在 Excel 裡建立樞紐表

原始 Excel 表格:

學生分數
Alice90
Bob80
Carol90
David70
Eve80
Frank90
Grace90

使用 Excel 樞紐表 (Django .annotate() 做的事情):

分數人數
904
802
701

使用 count() 計算總評論數

total_reviews = comments.count()

解釋

  • .count() 直接計算查詢集中的總筆數。

範例輸出

總評論數: 7

使用 aggregate(Avg('rating')) 計算平均評分

from django.db.models import Avg

average_rating = comments.aggregate(Avg('rating'))['rating__avg']

解釋

.aggregate() 是 Django ORM 中用來計算整個查詢集的總體統計數據的函數。

  • .aggregate() 用於計算整個查詢集的總體統計數據。
  • Avg('rating') 計算 rating 欄位的平均分數。

它與 .annotate() 類似,但主要用於整體統計,而不是針對每個分組分別計算。

簡單來說,.aggregate() 主要做兩件事:

  1. 計算:對整個查詢集的所有數據執行計算 (如平均數、總和、最大值等)。
  2. 回傳結果:將計算結果以 字典 形式回傳。
補充:['rating__avg'] 是什麼?
  • 當你使用 aggregate(Avg('rating')) 時,Django 會自動在結果字典中使用這個命名規則:
    • rating ➡️ 你查詢的欄位
    • __avg ➡️ Django 內建的 Avg() 聚合函數自動附加的後綴

範例

average_rating = comments.aggregate(Avg('rating'))
print(average_rating)  # {'rating__avg': 4.5}

因為 aggregate() 回傳的是一個字典,所以你需要透過 ['rating__avg'] 取得計算結果。

計算過程

rating__avg = (5 + 4 + 5 + 3 + 5 + 4 + 5) / 7
rating__avg = 4.43

回傳值

{'rating__avg': 4.43}

建立完整的星等分佈 (1-5星)

# 建立 1-5 星的分佈
rating_distribution = {i: 0 for i in range(1, 6)}

# 將統計結果填入分佈字典
for entry in rating_summary:
    rating_distribution[entry['rating']] = entry['count']

範例輸出

+---------+-------+
| Rating  | Count |
+---------+-------+
|    5    |   4   |
|    4    |   2   |
|    3    |   1   |
|    2    |   0   |
|    1    |   0   |
+---------+-------+

解釋

  • 建立一個包含 15 星的字典,並將計算結果填入。
  • 如果某些星等沒有出現,預設為 0

完整流程示意圖

Step 1: 原始資料 (comments)

+----+---------+--------+
| ID | Rating  | User   |
+----+---------+--------+
| 1  |   5     | Alice  |
| 2  |   4     | Bob    |
| 3  |   5     | Carol  |
| 4  |   3     | David  |
| 5  |   5     | Eve    |
| 6  |   4     | Frank  |
| 7  |   5     | Grace  |
+----+---------+--------+





Step 2: `.values('rating')`
[{'rating': 5}, {'rating': 4}, {'rating': 5}, {'rating': 3}, ...]





Step 3: `.annotate(Count('rating'))`
+---------+-------+
| Rating  | Count |
+---------+-------+
|    5    |   4   |
|    4    |   2   |
|    3    |   1   |
+---------+-------+






Step 4: `.aggregate(Avg('rating'))`
average_rating = (5 + 4 + 5 + 3 + 5 + 4 + 5) / 7 = **4.43**






Step 5: `rating_distribution`
+---------+-------+
| Rating  | Count |
+---------+-------+
|    5    |   4   |
|    4    |   2   |
|    3    |   1   |
|    2    |   0   |
|    1    |   0   |
+---------+-------+

總結

這段程式碼透過 Django ORM 完成了以下步驟:

  1. 使用 filter() 篩選出符合條件的評論資料。
  2. 使用 .values() 將資料轉換為字典格式,方便進一步操作。
  3. 使用 .annotate()Count() 計算各星等的評論數量。
  4. 使用 .count() 取得總評論數。
  5. 使用 .aggregate(Avg()) 計算平均評分。
  6. 建立完整的星等分佈 並確保 1 到 5 星都正確顯示,即使某些星等為 0。

希望這篇教學有幫助到你,讓你更理解如何使用 Django ORM 進行評論統計計算!

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *