Remover e extrair elementos duplicados de uma lista (matriz) em Python

O negócio

Esta secção descreve como gerar uma nova lista em Python, removendo ou extraindo elementos duplicados de uma lista (array).

Os seguintes detalhes são descritos aqui.

  • Remover elementos duplicados e gerar novas listagens
    • Não preservar a ordem da listagem original:set()
    • Preserva a ordem da listagem original: dict.fromkeys(),sorted()
    • Matriz bidimensional (lista de listas)
  • Extrair elementos duplicados e gerar uma nova lista
    • Não preservar a ordem da listagem original
    • Preserva a ordem da listagem original
    • Matriz bidimensional (lista de listas)

O mesmo conceito pode ser aplicado aos tuplos em vez de listas.

Ver o seguinte artigo para

  • Se quiser determinar se uma lista ou um tuple tem elementos duplicados
  • Se quiser extrair elementos que são comuns ou não comuns entre múltiplas listagens em vez de uma única lista

Note-se que as listas podem armazenar diferentes tipos de dados e são estritamente diferentes das arrays. Se quiser tratar arrays em processos que requerem tamanho de memória e endereços de memória ou processamento numérico de dados grandes, use array (biblioteca padrão) ou NumPy.

Remover elementos duplicados e gerar novas listagens

Não preservar a ordem da listagem original: set()

Se não houver necessidade de preservar a ordem da lista original, utilizar set(), que gera um set de tipo set.

O tipo de conjunto é um tipo de dados que não tem elementos duplicados. Quando uma lista ou outro tipo de dados é passado para set(), os valores duplicados são ignorados e um objecto do tipo set é devolvido em que apenas valores únicos são elementos.

Se quiser fazer um tuple, use 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, também pode ser deixado como estabelecido. Ver o artigo seguinte para mais informações sobre o conjunto do tipo de conjunto.

Preserva a ordem da listagem original: dict.fromkeys(),sorted()

Se quiser preservar a ordem da lista original, utilize o método class fromkeys() do tipo dicionário ou a função integrada ordenada().

dict.fromkeys() cria um novo objecto de dicionário cujas chaves são listas, tuplos, etc. especificados nos argumentos. Se o segundo argumento for omitido, o valor é Nenhum.

Como as chaves do dicionário não têm elementos duplicados, os valores duplicados são ignorados como em set(). Além disso, um objecto de dicionário pode ser passado como um argumento para listar() para obter uma lista cujos elementos são chaves de dicionário.

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

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

Tem sido garantido desde Python 3.7 (CPython é 3.6) que o dit.fromkeys() preserva a ordem da sequência de argumentos. As versões anteriores utilizam a função integrada ordenada() da seguinte forma.

Especifique a lista tuple method index() para a chave de argumento de classificação, que retorna uma lista ordenada de elementos.

index() é um método que retorna o índice do valor (o número do elemento da lista), que pode ser especificado como a chave de classificação() para classificar a lista com base na ordem da lista original. A chave de argumento é especificada como um objecto de chamada (callable), por isso não escreva ().

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

Matriz bidimensional (lista de listas)

Para arrays bidimensionais (listas de listas), o método usando set() ou dict.fromkeys() resulta num 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'

Isto porque os objectos que não podem ser caixilhados, tais como listas, não podem ser elementos do tipo set ou chaves do tipo dict.

Definir as seguintes funções A ordem da lista original é preservada e funciona para listas unidimensionais e tuplos.

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]

É utilizada a notação de compreensão de lista.

Aqui, utilizamos o seguinte

  • Se X em “X e Y” for falso na avaliação do curto-circuito e do operador, então Y não é avaliado (não executado).
  • O método append() não devolve nenhum.

Se os elementos da lista original seq não existirem na vista, então e depois são avaliados.
seen.append(x) é executado e o elemento é adicionado à vista.
Porque o método append() devolve Nenhum e Nenhum é Falso, não visto.append(x) avalia para Verdadeiro.
A expressão condicional na notação de compreensão da lista torna-se verdadeira e é acrescentada como um elemento da lista final gerada.

Se os elementos da lista original seq estão presentes em visto, então x não em visto é Falso, e a expressão condicional para a expressão de compreensão da lista é Falso.
Por conseguinte, não são adicionados como elementos da lista final gerada.

Outro método é definir o eixo de argumentos na função NumPy np.unique(), embora o resultado seja ordenado.

Extrair elementos duplicados e gerar uma nova lista

Não preservar a ordem da listagem original

Para extrair apenas elementos duplicados da lista original, utilizar colecções.Contador().
Retorna uma colecção. Contador (uma subclasse de dicionário) com os elementos como chaves e o número de elementos como valores.

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

Uma vez que é uma subclasse do dicionário, os itens() podem ser utilizados para recuperar chaves e valores. É suficiente extrair chaves cujo número seja dois ou mais.

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

Preserva a ordem da listagem original

Como mostrado no exemplo acima, desde Python 3.7, as chaves das colecções.Counter mantêm a ordem da lista original e assim por diante.

Nas versões anteriores, a ordenação com sort() é suficiente, tal como a eliminação de elementos duplicados.

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

Se desejar extrair duplicados tal como estão, basta deixar elementos da lista original com um número de dois ou mais. A ordem também é preservada.

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

Matriz bidimensional (lista de listas)

Para arrays bidimensionais (listas de listas), as seguintes funções são possíveis quando a ordem da lista original não é mantida e quando esta é mantida, respectivamente. Funciona também para listas unidimensionais e tuplos.

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 quiser extrair com duplicados, deixe elementos da lista original com uma contagem de dois ou mais.

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

Note-se que uma vez que a complexidade computacional da contagem() é O(n), a função mostrada acima que repetidamente executa a contagem() é muito ineficiente. Pode haver uma forma mais inteligente.

Counter() é uma subclasse do dicionário, portanto, se passar uma lista ou tuple cujos elementos são listas ou outros objectos não hashable para colecções.Counter(), ocorrerá um erro e não será possível utilizá-lo.

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