ลบและแยกองค์ประกอบที่ซ้ำกันออกจากรายการ (อาร์เรย์) ใน Python

ธุรกิจ

ส่วนนี้อธิบายวิธีสร้างรายการใหม่ใน Python โดยการลบหรือแยกองค์ประกอบที่ซ้ำกันออกจากรายการ (อาร์เรย์)

รายละเอียดต่อไปนี้อธิบายไว้ที่นี่

  • ลบองค์ประกอบที่ซ้ำกันและสร้างรายการใหม่
    • ไม่รักษาลำดับของรายการเดิม:set()
    • รักษาลำดับของรายการเดิม:dict.fromkeys(),sorted()
    • อาร์เรย์สองมิติ (รายการ)
  • แยกองค์ประกอบที่ซ้ำกันและสร้างรายการใหม่
    • ไม่รักษาลำดับของรายการเดิม
    • รักษาลำดับของรายการเดิม
    • อาร์เรย์สองมิติ (รายการ)

แนวคิดเดียวกันนี้สามารถนำไปใช้กับสิ่งอันดับแทนที่จะเป็นรายการ

ดูบทความต่อไปนี้สำหรับ

  • หากคุณต้องการตรวจสอบว่ารายการหรือทูเพิลมีองค์ประกอบที่ซ้ำกันหรือไม่
  • หากคุณต้องการแยกองค์ประกอบที่เหมือนกันหรือไม่ธรรมดาในหลายรายชื่อแทนที่จะเป็นรายการเดียว

โปรดทราบว่ารายการสามารถจัดเก็บข้อมูลประเภทต่างๆ และแตกต่างจากอาร์เรย์โดยสิ้นเชิง หากคุณต้องการจัดการอาร์เรย์ในกระบวนการที่ต้องการขนาดหน่วยความจำและที่อยู่หน่วยความจำ หรือการประมวลผลตัวเลขของข้อมูลขนาดใหญ่ ให้ใช้อาร์เรย์ (ไลบรารีมาตรฐาน) หรือ NumPy

ลบองค์ประกอบที่ซ้ำกันและสร้างรายการใหม่

ไม่รักษาลำดับของรายการเดิม:set()

หากไม่จำเป็นต้องรักษาลำดับของรายการดั้งเดิม ให้ใช้ set() ซึ่งจะสร้างชุดประเภทชุด

ประเภทชุดเป็นประเภทข้อมูลที่ไม่มีองค์ประกอบที่ซ้ำกัน เมื่อรายการหรือประเภทข้อมูลอื่นถูกส่งไปยัง set() ค่าที่ซ้ำกันจะถูกละเว้นและวัตถุของชุดประเภทจะถูกส่งคืนโดยมีเพียงค่าที่ไม่ซ้ำกันเท่านั้นที่เป็นองค์ประกอบ

หากคุณต้องการทำให้เป็น tuple ให้ใช้ tuple()

l = [3, 3, 2, 1, 5, 1, 4, 2, 3]

print(set(l))
# {1, 2, 3, 4, 5}

print(list(set(l)))
# [1, 2, 3, 4, 5]

แน่นอนมันยังสามารถปล่อยให้เป็นชุด ดูบทความต่อไปนี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับชุดประเภทชุด

รักษาลำดับของรายการเดิม:dict.fromkeys(),sorted()

หากคุณต้องการรักษาลำดับของรายการดั้งเดิม ให้ใช้วิธีคลาส fromkeys() ของประเภทพจนานุกรมหรือฟังก์ชัน sorted() ในตัว

dict.fromkeys() สร้างอ็อบเจ็กต์พจนานุกรมใหม่ซึ่งมีคีย์เป็นรายการ ทูเพิล ฯลฯ ที่ระบุในอาร์กิวเมนต์ หากละเว้นอาร์กิวเมนต์ที่สอง ค่าจะเป็นไม่มี

เนื่องจากคีย์พจนานุกรมไม่มีองค์ประกอบที่ซ้ำกัน ค่าที่ซ้ำกันจะถูกละเว้นเหมือนใน set() นอกจากนี้ วัตถุพจนานุกรมสามารถส่งผ่านเป็นอาร์กิวเมนต์ไปยัง list() เพื่อรับรายการที่มีองค์ประกอบเป็นคีย์พจนานุกรม

print(dict.fromkeys(l))
# {3: None, 2: None, 1: None, 5: None, 4: None}

print(list(dict.fromkeys(l)))
# [3, 2, 1, 5, 4]

รับประกันตั้งแต่ Python 3.7 (CPython คือ 3.6) ที่ dict.fromkeys() รักษาลำดับของลำดับอาร์กิวเมนต์ เวอร์ชันก่อนหน้าใช้ฟังก์ชัน sorted() ในตัวดังนี้

ระบุรายการ tuple method index() สำหรับคีย์อาร์กิวเมนต์ sorted ซึ่งส่งคืนรายการที่เรียงลำดับขององค์ประกอบ

index() เป็นวิธีการที่ส่งกลับดัชนีของค่า (จำนวนขององค์ประกอบในรายการ) ซึ่งสามารถระบุเป็นคีย์ของ sorted() เพื่อจัดเรียงรายการตามลำดับของรายการดั้งเดิม คีย์อาร์กิวเมนต์ถูกระบุเป็นวัตถุที่เรียกได้ (เรียกได้) ดังนั้นอย่าเขียน ()

print(sorted(set(l), key=l.index))
# [3, 2, 1, 5, 4]

อาร์เรย์สองมิติ (รายการ)

สำหรับอาร์เรย์สองมิติ (รายการของรายการ) วิธีการที่ใช้ set() หรือ dict.fromkeys() ส่งผลให้เกิด TypeError

l_2d = [[1, 1], [0, 1], [0, 1], [0, 0], [1, 0], [1, 1], [1, 1]]

# l_2d_unique = list(set(l_2d))
# TypeError: unhashable type: 'list'

# l_2d_unique_order = dict.fromkeys(l_2d)
# TypeError: unhashable type: 'list'

ทั้งนี้เนื่องจากอ็อบเจ็กต์ที่ไม่สามารถแฮชได้ เช่น รายการ ไม่สามารถเป็นอิลิเมนต์ของชุดประเภทหรือคีย์ประเภท dict

กำหนดฟังก์ชันต่อไปนี้ ลำดับของรายการดั้งเดิมจะถูกรักษาไว้และใช้ได้กับรายการแบบหนึ่งมิติและทูเพิล

def get_unique_list(seq):
    seen = []
    return [x for x in seq if x not in seen and not seen.append(x)]

print(get_unique_list(l_2d))
# [[1, 1], [0, 1], [0, 0], [1, 0]]

print(get_unique_list(l))
# [3, 2, 1, 5, 4]

ใช้สัญกรณ์ความเข้าใจรายการ

ที่นี่เราใช้สิ่งต่อไปนี้

  • หาก X ใน “X และ Y” เป็นเท็จในการประเมินการลัดวงจรของตัวดำเนินการ and ดังนั้น Y จะไม่ถูกประเมิน (ไม่ได้ดำเนินการ)
  • append() วิธีการส่งกลับไม่มี

หากองค์ประกอบของลำดับรายการดั้งเดิมไม่มีอยู่ในสิ่งที่เห็น จากนั้นและหลังจะถูกประเมิน
seen.append(x) ถูกดำเนินการและเพิ่มองค์ประกอบในการเห็น
เนื่องจากเมธอด append() คืนค่า None และ None เป็นเท็จ not seen.append(x) จะประเมินเป็น True
นิพจน์เงื่อนไขในสัญกรณ์ความเข้าใจรายการจะกลายเป็น True และถูกเพิ่มเป็นองค์ประกอบของรายการที่สร้างขึ้นขั้นสุดท้าย

หากองค์ประกอบของลำดับรายการดั้งเดิมมีอยู่ในการเห็น ดังนั้น x ไม่เห็นจะเป็นเท็จ และนิพจน์เงื่อนไขสำหรับนิพจน์ความเข้าใจรายการจะเป็นเท็จ
ดังนั้นจึงไม่ได้เพิ่มเป็นองค์ประกอบของรายการที่สร้างขึ้นขั้นสุดท้าย

อีกวิธีหนึ่งคือการตั้งค่าแกนอาร์กิวเมนต์ในฟังก์ชันของ NumPy np.unique() แม้ว่าผลลัพธ์จะถูกจัดเรียง

แยกองค์ประกอบที่ซ้ำกันและสร้างรายการใหม่

ไม่รักษาลำดับของรายการเดิม

หากต้องการแยกเฉพาะองค์ประกอบที่ซ้ำกันจากรายการดั้งเดิม ให้ใช้ collection.Counter()
ส่งคืน collections.Counter (คลาสย่อยของพจนานุกรม) โดยมีองค์ประกอบเป็นคีย์และจำนวนองค์ประกอบเป็นค่า

import collections

l = [3, 3, 2, 1, 5, 1, 4, 2, 3]

print(collections.Counter(l))
# Counter({3: 3, 2: 2, 1: 2, 5: 1, 4: 1})

เนื่องจากเป็นคลาสย่อยของพจนานุกรม จึงสามารถใช้ items() เพื่อดึงคีย์และค่าต่างๆ การแยกคีย์ที่มีตัวเลขตั้งแต่สองตัวขึ้นไปก็เพียงพอแล้ว

print([k for k, v in collections.Counter(l).items() if v > 1])
# [3, 2, 1]

รักษาลำดับของรายการเดิม

ดังที่แสดงในตัวอย่างข้างต้น ตั้งแต่ Python 3.7 คีย์ของ collections.Counter จะรักษาลำดับของรายการเดิมเป็นต้น

ในเวอร์ชันก่อนหน้า การเรียงลำดับด้วย sorted() ก็เพียงพอแล้ว เช่นเดียวกับการลบองค์ประกอบที่ซ้ำกัน

print(sorted([k for k, v in collections.Counter(l).items() if v > 1], key=l.index))
# [3, 2, 1]

หากคุณต้องการแยกรายการที่ซ้ำกันตามที่เป็นอยู่ ให้ปล่อยองค์ประกอบจากรายการเดิมที่มีจำนวนตั้งแต่สองรายการขึ้นไป ยังรักษาลำดับไว้ได้

cc = collections.Counter(l)
print([x for x in l if cc[x] > 1])
# [3, 3, 2, 1, 1, 2, 3]

อาร์เรย์สองมิติ (รายการ)

สำหรับอาร์เรย์สองมิติ (รายการของรายการ) ฟังก์ชันต่อไปนี้เป็นไปได้เมื่อไม่มีการรักษาลำดับของรายการดั้งเดิมและเมื่อถูกเก็บรักษาตามลำดับ มันยังใช้ได้กับรายการหนึ่งมิติและทูเพิลด้วย

l_2d = [[1, 1], [0, 1], [0, 1], [0, 0], [1, 0], [1, 1], [1, 1]]
def get_duplicate_list(seq):
    seen = []
    return [x for x in seq if not seen.append(x) and seen.count(x) == 2]

def get_duplicate_list_order(seq):
    seen = []
    return [x for x in seq if seq.count(x) > 1 and not seen.append(x) and seen.count(x) == 1]

print(get_duplicate_list(l_2d))
# [[0, 1], [1, 1]]

print(get_duplicate_list_order(l_2d))
# [[1, 1], [0, 1]]

print(get_duplicate_list(l))
# [3, 1, 2]

print(get_duplicate_list_order(l))
# [3, 2, 1]

หากคุณต้องการแยกด้วยรายการที่ซ้ำกัน ให้ปล่อยองค์ประกอบจากรายการดั้งเดิมโดยนับตั้งแต่สองรายการขึ้นไป

print([x for x in l_2d if l_2d.count(x) > 1])
# [[1, 1], [0, 1], [0, 1], [1, 1], [1, 1]]

โปรดทราบว่าเนื่องจากความซับซ้อนในการคำนวณของ count() คือ O(n) ฟังก์ชันที่แสดงด้านบนที่ดำเนินการ count() ซ้ำๆ จึงไม่มีประสิทธิภาพมาก อาจมีวิธีที่ฉลาดกว่า

Counter เป็นคลาสย่อยของพจนานุกรม ดังนั้นหากคุณส่งรายการหรือทูเพิลที่มีองค์ประกอบเป็นรายการหรืออ็อบเจกต์อื่นที่ไม่สามารถแฮชได้ไปยังคอลเลกชัน ตัวนับ () ข้อผิดพลาดจะเกิดขึ้นและคุณจะไม่สามารถใช้งานได้

# print(collections.Counter(l_2d))
# TypeError: unhashable type: 'list'