Премахване и извличане на дублиращи се елементи от списък (масив) в Python

Бизнес

Този раздел описва как да генерирате нов списък в Python чрез премахване или извличане на дублиращи се елементи от списък (масив).

Тук са описани следните подробности.

  • Премахване на дублиращи се елементи и генериране на нови списъци
    • Не запазвайте реда на първоначалния списък:set()
    • Запазване на реда на първоначалния списък: dict.fromkeys(),sorted()
    • Двуизмерен масив (списък от списъци)
  • Извличане на дублиращи се елементи и генериране на нов списък
    • Не запазвайте реда на първоначалния списък
    • Запазване на реда на първоначалния списък
    • Двуизмерен масив (списък от списъци)

Същата концепция може да се приложи към кортежи вместо към списъци.

Вижте следната статия за

  • Ако искате да определите дали даден списък или кортеж има дублиращи се елементи
  • Ако искате да извлечете елементи, които са общи или не са общи за няколко списъка, вместо за един списък

Обърнете внимание, че списъците могат да съхраняват различни типове данни и се различават строго от масивите. Ако искате да работите с масиви в процеси, които изискват размер на паметта и адреси в паметта или числена обработка на големи данни, използвайте array (стандартна библиотека) или NumPy.

Премахване на дублиращи се елементи и генериране на нови списъци

Не запазвайте реда на първоначалния списък: set()

Ако не е необходимо да се запазва редът на оригиналния списък, използвайте функцията set(), която генерира набор от тип set.

Типът набор е тип данни, който няма дублиращи се елементи. Когато към set() се подаде списък или друг тип данни, дублиращите се стойности се игнорират и се връща обект от тип set, в който елементи са само уникални стойности.

Ако искате да го превърнете в кортеж, използвайте 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]

Разбира се, тя може да бъде оставена и така, както е зададена. Вижте следната статия за повече информация относно типа set.

Запазване на реда на първоначалния списък: dict.fromkeys(),sorted()

Ако искате да запазите реда на оригиналния списък, използвайте класовия метод fromkeys() на типа речник или вградената функция sorted().

dict.fromkeys() създава нов речников обект, чиито ключове са списъци, кортежи и т.н., посочени в аргументите. Ако вторият аргумент е пропуснат, стойността е None.

Тъй като ключовете на речника нямат дублиращи се елементи, дублиращите се стойности се пренебрегват, както при 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(), както следва.

Посочете метода index() на кортежите на списъци за аргумента key на sorted, който връща сортиран списък от елементи.

index() е метод, който връща индекса на стойността (номера на елемента в списъка), който може да бъде зададен като ключ на sorted(), за да се сортира списъкът въз основа на реда на оригиналния списък. Аргументът ключ се задава като обект, който може да бъде извикан (callable), така че не пишете ().

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'

Това е така, защото обектите, които не могат да се променят с помощта на кеш, като например списъци, не могат да бъдат елементи от тип set или ключове от тип 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() връща None.

Ако елементите на първоначалния списък seq не съществуват във видяното, се оценяват след и след.
Изпълнява се seen.append(x) и елементът се добавя към saw.
Тъй като методът append() връща None, а None е False, not seen.append(x) се оценява на True.
Условният израз в нотацията за разбиране на списък става True и се добавя като елемент на крайния генериран списък.

Ако елементите на оригиналния списък seq присъстват в списъка seen, тогава x, който не е в списъка seen, е False, а условният израз за израза за разбиране на списък е False.
Поради това те не се добавят като елементи на окончателния генериран списък.

Друг метод е да зададете оста на аргумента във функцията np.unique() на NumPy, въпреки че резултатът ще бъде сортиран.

Извличане на дублиращи се елементи и генериране на нов списък

Не запазвайте реда на първоначалния списък

За да извлечете само дублиращи се елементи от оригиналния списък, използвайте collections.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]

Запазване на реда на първоначалния списък

Както е показано в примера по-горе, от версия 3.7 на Python ключовете на 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 е подклас на dictionary, така че ако подадете списък или кортеж, чиито елементи са списъци или други обекти, които не могат да се променят с кеш, на collections.Counter(), ще възникне грешка и няма да можете да го използвате.

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