บทนำ
การหา "Rank" หรืออันดับของแต่ละค่าในลิสต์ หมายถึงการกำหนดตัวเลขอันดับ โดยค่าน้อยสุด = อันดับ 1 ค่าที่เท่ากันจะได้อันดับเฉลี่ย (fractional ranking) โค้ดตัวอย่างใช้อัลกอริทึม O(n²) แบบ naive
โค้ดต้นฉบับ (O(n²))
def find_rank_elem(my_list):
result = [0] * len(my_list)
for i in range(len(my_list)):
r, s = 1, 1 # r = rank, s = count ของค่าเท่ากัน
for j in range(len(my_list)):
if j != i and my_list[j] < my_list[i]:
r += 1
if j != i and my_list[j] == my_list[i]:
s += 1
# fractional ranking: rank + (ties - 1) / 2
result[i] = r + (s - 1) / 2
return result
my_list = [1, 3, 5, 3, 1, 26, 99, 45, 67, 12]
print("ลิสต์:", my_list)
print("Rank:", find_rank_elem(my_list))
ผลลัพธ์
ลิสต์: [1, 3, 5, 3, 1, 26, 99, 45, 67, 12]
Rank: [1.0, 3.0, 5.0, 3.0, 1.0, 7.0, 10.0, 8.0, 9.0, 6.0]
คำอธิบายอัลกอริทึม
- r: นับจำนวนค่าที่น้อยกว่า (baseline rank)
- s: นับจำนวนค่าที่เท่ากัน (รวมตัวเอง)
- Fractional rank:
r + (s-1)/2→ ค่าเท่ากันได้อันดับเฉลี่ย
วิธีที่ดีกว่า: ใช้ Pandas / SciPy (O(n log n))
1. Pandas Series.rank() - แนะนำมากที่สุด
import pandas as pd
s = pd.Series([1, 3, 5, 3, 1, 26, 99, 45, 67, 12])
print(s.rank(method='average')) # default: average
# 0 1.5
# 1 3.5
# 2 5.0
# 3 3.5
# 4 1.5
# 5 7.0
# 6 10.0
# 7 8.0
# 8 9.0
# 9 6.0
2. SciPy stats.rankdata
from scipy.stats import rankdata
arr = [1, 3, 5, 3, 1, 26, 99, 45, 67, 12]
print(rankdata(arr, method='average'))
# [1.5 3.5 5. 3.5 1.5 7. 10. 8. 9. 6. ]
3. ใช้ sorted + dict mapping (O(n log n) ไม่ต้อง library)
def rank_fast(lst):
# เรียงค่าพร้อม index ต้นทาง
sorted_pairs = sorted(enumerate(lst), key=lambda x: x[1])
ranks = [0.0] * len(lst)
i = 0
while i < len(sorted_pairs):
j = i
# หากลุ่มค่าเท่ากัน
while j < len(sorted_pairs) and sorted_pairs[j][1] == sorted_pairs[i][1]:
j += 1
# อันดับเฉลี่ยของกลุ่มนี้
avg_rank = (i + 1 + j) / 2.0
for k in range(i, j):
orig_idx = sorted_pairs[k][0]
ranks[orig_idx] = avg_rank
i = j
return ranks
print(rank_fast([1, 3, 5, 3, 1, 26, 99, 45, 67, 12]))
# [1.5, 3.5, 5.0, 3.5, 1.5, 7.0, 10.0, 8.0, 9.0, 6.0]
วิธี Rank อื่นๆ (method parameter)
| Method | คำอธิบาย | ตัวอย่างค่าซ้ำ [10, 10, 20] |
|---|---|---|
| average | อันดับเฉลี่ย (default) | [1.5, 1.5, 3.0] |
| min | อันดับต่ำสุดของกลุ่ม | [1, 1, 3] |
| max | อันดับสูงสุดของกลุ่ม | [2, 2, 3] |
| first | ตามลำดับที่ปรากฏ | [1, 2, 3] |
| dense | อันดับติดกัน ไม่ข้าม | [1, 1, 2] |
สรุป
โค้ดต้นฉบับทำงานได้แต่ช้า (O(n²)) ควรใช้ pd.Series.rank() หรือ scipy.stats.rankdata() ซึ่งเร็ว รองรับหลายวิธี ranking และทดสอบผ่านแล้ว หากไม่ต้องการ library ให้ใช้วิธี sorted + mapping (O(n log n))