Выдаляйце і здабывайце паўтаральныя элементы са спісу (масіва) у Python

Бізнэс

У гэтым раздзеле апісваецца, як стварыць новы спіс у Python шляхам выдалення або здабывання паўтаральных элементаў са спісу (масіў).

Наступныя падрабязнасці апісаны тут.

  • Выдаліце ​​паўтаральныя элементы і стварайце новыя спісы
    • Не захоўвайце парадак першапачатковага спісу:set()
    • Захоўвае парадак зыходнага спісу:dict.fromkeys(),sorted()
    • Двухмерны масіў (спіс спісаў)
  • Вымайце паўтаральныя элементы і сфармуйце новы спіс
    • Не захоўвайце парадак першапачатковага спісу
    • Захоўвае парадак зыходнага спісу
    • Двухмерны масіў (спіс спісаў)

Тая ж канцэпцыя можа быць прыменена да картэжаў замест спісаў.

Глядзіце наступны артыкул для

  • Калі вы хочаце вызначыць, ці ёсць у спісе або картэжы паўтаральныя элементы
  • Калі вы хочаце атрымаць элементы, якія з’яўляюцца агульнымі або не распаўсюджанымі ў некалькіх спісах замест аднаго спісу

Звярніце ўвагу, што спісы могуць захоўваць розныя тыпы даных і строга адрозніваюцца ад масіваў. Калі вы хочаце апрацоўваць масівы ў працэсах, якія патрабуюць памеру памяці і адрасоў памяці або лікавай апрацоўкі вялікіх даных, выкарыстоўвайце масіў (стандартная бібліятэка) або NumPy.

Выдаліце ​​паўтаральныя элементы і стварайце новыя спісы

Не захоўвайце парадак першапачатковага спісу: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]

Вядома, яго таксама можна пакінуць як усталяваны. Глядзіце наступны артыкул для атрымання дадатковай інфармацыі аб наборы тыпу набору.

Захоўвае парадак зыходнага спісу: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() спісу для ключа аргументу 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» з’яўляецца ілжывым у ацэнцы кароткага замыкання аператара і, то Y не ацэньваецца (не выконваецца).
  • Метад append() вяртае None.

Калі элементы зыходнага спісу seq не існуюць у віданым, то і пасля ацэньваюцца.
seen.append(x) выконваецца, і элемент дадаецца да seen.
Паколькі метад append() вяртае None і None з’яўляецца False, not seen.append(x) ацэньвае як True.
Умоўны выраз у натацыі разумення спісу становіцца True і дадаецца як элемент канчатковага згенераванага спісу.

Калі элементы зыходнага спісу seq прысутнічаюць у seen, то x not in seen з’яўляецца False, а ўмоўны выраз для выразу разумення спісу – False.
Такім чынам, яны не дадаюцца ў якасці элементаў канчатковага згенераванага спісу.

Іншы метад – усталяваць вось аргументаў у функцыі NumPy np.unique(), хоць вынік будзе адсартаваны.

Вымайце паўтаральныя элементы і сфармуйце новы спіс

Не захоўвайце парадак першапачатковага спісу

Каб здабываць толькі паўтаральныя элементы з зыходнага спісу, выкарыстоўвайце 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]

Захоўвае парадак зыходнага спісу

Як паказана ў прыведзеным вышэй прыкладзе, пачынаючы з 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(), вельмі неэфектыўная. Можа быць больш разумны спосаб.

Лічыльнік з’яўляецца падкласам слоўніка, таму, калі вы перадаеце спіс або картэж, элементамі якіх з’яўляюцца спісы або іншыя нехэшаваныя аб’екты ў collections.Counter(), адбудзецца памылка, і вы не зможаце ім карыстацца.

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