Rimuovere ed estrarre elementi duplicati da una lista (array) in Python

Attività commerciale

Questa sezione descrive come generare una nuova lista in Python rimuovendo o estraendo elementi duplicati da una lista (array).

I seguenti dettagli sono descritti qui.

  • Rimuovere gli elementi duplicati e generare nuovi elenchi
    • Non conservare l'ordine dell'elenco originale:set()
    • Conserva l'ordine dell'elenco originale: dict.fromkeys(),sorted()
    • Matrice bidimensionale (lista di liste)
  • Estrarre gli elementi duplicati e generare una nuova lista
    • Non conservare l'ordine dell'elenco originale
    • Conserva l'ordine dell'elenco originale
    • Matrice bidimensionale (lista di liste)

Lo stesso concetto può essere applicato alle tuple invece che alle liste.

Vedere il seguente articolo per

  • Se volete determinare se una lista o una tupla ha elementi duplicati
  • Se vuoi estrarre elementi che sono comuni o non comuni tra più elenchi invece di un singolo elenco

Notate che le liste possono memorizzare diversi tipi di dati e sono strettamente diverse dagli array. Se volete gestire gli array in processi che richiedono dimensioni e indirizzi di memoria o l'elaborazione numerica di grandi dati, usate array (libreria standard) o NumPy.

Rimuovere gli elementi duplicati e generare nuovi elenchi

Non conservare l'ordine dell'elenco originale: set()

Se non c'è bisogno di preservare l'ordine della lista originale, usate set(), che genera un insieme di tipo set.

Il tipo set è un tipo di dati che non ha elementi duplicati. Quando una lista o un altro tipo di dati viene passato a set(), i valori duplicati vengono ignorati e viene restituito un oggetto di tipo set in cui solo i valori unici sono elementi.

Se volete renderlo una tupla, usate 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]

Naturalmente, può anche essere lasciato come set. Vedere il seguente articolo per maggiori informazioni sul tipo set.

Conserva l'ordine dell'elenco originale: dict.fromkeys(),sorted()

Se volete conservare l'ordine della lista originale, usate il metodo di classe fromkeys() del tipo dizionario o la funzione integrata sorted().

dict.fromkeys() crea un nuovo oggetto dizionario le cui chiavi sono liste, tuple, ecc. specificate negli argomenti. Se il secondo argomento è omesso, il valore è None.

Poiché le chiavi del dizionario non hanno elementi duplicati, i valori duplicati sono ignorati come in set(). Inoltre, un oggetto dizionario può essere passato come argomento a list() per ottenere una lista i cui elementi sono chiavi di dizionario.

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

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

È stato garantito da Python 3.7 (CPython è 3.6) che dict.fromkeys() conserva l'ordine della sequenza degli argomenti. Le versioni precedenti usano la funzione built-in sorted() come segue.

Specifica il metodo della tupla della lista index() per l'argomento chiave di sorted, che restituisce una lista ordinata di elementi.

index() è un metodo che restituisce l'indice del valore (il numero dell'elemento nella lista), che può essere specificato come chiave di sorted() per ordinare la lista in base all'ordine della lista originale. L'argomento key è specificato come un oggetto callable (richiamabile), quindi non scrivere ().

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

Matrice bidimensionale (lista di liste)

Per gli array bidimensionali (liste di liste), il metodo che usa set() o dict.fromkeys() risulta in un 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'

Questo perché gli oggetti non-hashable come le liste non possono essere elementi di tipo set o chiavi di tipo dict.

Definire le seguenti funzioni L'ordine della lista originale è conservato e funziona per liste unidimensionali e tuple.

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]

Viene usata la notazione di comprensione della lista.

Qui, usiamo il seguente

  • Se X in “X e Y” è falso nella valutazione in cortocircuito dell'operatore and, allora Y non viene valutato (non viene eseguito).
  • Il metodo append() restituisce None.

Se gli elementi della lista originale seq non esistono nella vista, allora e dopo vengono valutati.
seen.append(x) viene eseguito e l'elemento viene aggiunto a seen.
Poiché il metodo append() restituisce None e None è False, not seen.append(x) valuta True.
L'espressione condizionale nella notazione di comprensione della lista diventa True e viene aggiunta come elemento della lista finale generata.

Se gli elementi della lista originale seq sono presenti in seen, allora x non in seen è False, e l'espressione condizionale per l'espressione di comprensione della lista è False.
Pertanto, non vengono aggiunti come elementi della lista finale generata.

Un altro metodo è quello di impostare l'asse degli argomenti nella funzione np.unique() di NumPy, anche se il risultato sarà ordinato.

Estrarre gli elementi duplicati e generare una nuova lista

Non conservare l'ordine dell'elenco originale

Per estrarre solo gli elementi duplicati dalla lista originale, usate collections.Counter().
Restituisce un collections.Counter (una sottoclasse di dizionario) con gli elementi come chiavi e il numero di elementi come valori.

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})

Poiché è una sottoclasse di dizionario, items() può essere usato per recuperare chiavi e valori. È sufficiente estrarre le chiavi il cui numero è due o più.

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

Conserva l'ordine dell'elenco originale

Come mostrato nell'esempio sopra, da Python 3.7, le chiavi di collections.Counter mantengono l'ordine della lista originale e così via.

Nelle versioni precedenti, l'ordinamento con sorted() è sufficiente, così come la cancellazione di elementi duplicati.

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

Se volete estrarre i duplicati così come sono, lasciate semplicemente gli elementi della lista originale con un numero di due o più. Anche l'ordine viene conservato.

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

Matrice bidimensionale (lista di liste)

Per gli array bidimensionali (liste di liste), le seguenti funzioni sono possibili quando l'ordine della lista originale non viene mantenuto e quando viene mantenuto, rispettivamente. Funziona anche per liste unidimensionali e tuple.

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]

Se volete estrarre con i duplicati, lasciate gli elementi della lista originale con un numero di due o più.

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

Si noti che poiché la complessità computazionale di count() è O(n), la funzione mostrata sopra che esegue ripetutamente count() è molto inefficiente. Potrebbe esserci un modo più intelligente.

Counter è una sottoclasse di dizionario, quindi se passate una lista o una tupla i cui elementi sono liste o altri oggetti non-hashable a collections.Counter(), si verificherà un errore e non sarete in grado di usarla.

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