Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

Python: หา Rank (อันดับ) ของแต่ละองค์ประกอบในลิสต์

บทนำ

การหา "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))