Como posso verificar se uma lista é um subconjunto de outra?

185

Preciso verificar se uma lista é um subconjunto de outra - um retorno booleano é tudo que procuro.

Testar a igualdade na lista menor após uma interseção é a maneira mais rápida de fazer isso? O desempenho é de extrema importância, considerando o número de conjuntos de dados que precisam ser comparados.

Adicionando fatos adicionais com base em discussões:

  1. Qualquer uma das listas será a mesma para muitos testes? Faz como um deles é uma tabela de pesquisa estática.

  2. Precisa ser uma lista? Isso não acontece - a tabela de pesquisa estática pode ser qualquer coisa que tenha melhor desempenho. O dinâmico é um ditado a partir do qual extraímos as chaves para executar uma pesquisa estática.

Qual seria a solução ideal diante do cenário?

IDesconhecido
fonte
Você mencionou a velocidade, talvez o numpy seja útil, dependendo do seu uso.
NinMonkey 16/05
2
Os itens da lista são laváveis?
Wim
você precisa de um subconjunto adequado ou eles podem ser iguais?
törzsmókus 16/08
2
Por que não definir (list_a) .issubset (set (list_b))?
SeF

Respostas:

127

A função de desempenho que o Python fornece para isso é set.issubset. No entanto, existem algumas restrições que tornam claro se é a resposta para sua pergunta.

Uma lista pode conter itens várias vezes e tem um pedido específico. Um conjunto não. Além disso, os conjuntos funcionam apenas em objetos hashable .

Você está perguntando sobre subconjunto ou subsequência (o que significa que você desejará um algoritmo de pesquisa de cadeias)? Qualquer uma das listas será a mesma para muitos testes? Quais são os tipos de dados contidos na lista? E, nesse caso, precisa ser uma lista?

Sua outra postagem intercepta um ditado e a lista tornou os tipos mais claros e recebeu uma recomendação para usar as visualizações de chave do dicionário para a funcionalidade de conjunto. Nesse caso, sabia-se que funcionava porque as chaves de dicionário se comportam como um conjunto (tanto que, antes de termos conjuntos em Python, usamos dicionários). É de se perguntar como o problema ficou menos específico em três horas.

Yann Vernier
fonte
Estou me referindo apenas a um subconjunto e o issubset executa muito bem - Obrigado. No entanto, estou curioso sobre duas perguntas aqui. 1.As listas serão as mesmas para muitos testes? Faz como um deles uma tabela de pesquisa estática 2. Precisa ser uma lista? Isso não acontece - a tabela de pesquisa estática pode ser o que tiver melhor desempenho. O dinâmico é um ditado a partir do qual extraímos as chaves para executar uma pesquisa estática. Esse fato alterará a solução?
IUnknown
Não muito. As teclas de um dicionário são definidas e já estão organizadas em uma tabela de hash e, portanto, o uso de um conjunto para a parte estática não causará complicações adicionais. Basicamente, o fato de ser um dict significa que você pode não precisar converter a parte estática em um conjunto (você pode verificar todos (itertools.imap (dict.has_key, mylist)) com desempenho O (n)).
Yann Vernier
Não entendo como essa (ou qualquer outra solução baseada em aparelhos) pode ser a resposta aceita aqui. A questão é sobre listas e, francamente, acho que o subconjunto em "verificar se uma lista é um subconjunto da outra" não deve ser considerado literalmente. Após a conversão em conjuntos, qualquer informação sobre elementos duplicados é perdida. No entanto, se a lista inicial puder conter esses elementos, pode ser importante verificar se eles aparecem na segunda lista e também dizer realmente que todos os elementos de uma lista podem ser encontrados. dentro do outro. Conjuntos não fazem isso!
inVader 13/06/19
O contexto importa; isso foi aceito por ajudar o solicitante e explicou a distinção. Disseram-nos que os candidatos seriam representáveis ​​como conjuntos, por isso era uma tarefa definida. Seu caso pode ser diferente e a diferença mencionada seria resolvida usando vários conjuntos, como coleções.
Yann Vernier
141
>>> a = [1, 3, 5]
>>> b = [1, 3, 5, 8]
>>> c = [3, 5, 9]
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

>>> a = ['yes', 'no', 'hmm']
>>> b = ['yes', 'no', 'hmm', 'well']
>>> c = ['sorry', 'no', 'hmm']
>>> 
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False
Yulan Liu
fonte
21
Parece mais bonito e escreve mais simples, mas o mais rápido deve ser set(a).issubset(b) porque, nesse caso, você só converte aem conjunto, mas não b, o que economiza tempo. Você pode usar timeitpara comparar o tempo consumido em dois comandos. Por exemplo, timeit.repeat('set(a)<set(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000) e timeit.repeat('set(a).issubset(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)
Yulan Liu
8
@YulanLiu: Odeio quebrá-lo para você, mas a primeira coisa a issubsetfazer é verificar se o argumento é um set/ frozensete, se não for, ele o converte em temporário setpara comparação, executa a verificação e joga fora o temporário set. Diferenças de tempo (se houver) seriam um fator de pequenas diferenças nos custos de pesquisa do LEGB (encontrar setuma segunda vez é mais caro do que a pesquisa de atributo em um existente set), mas é geralmente uma lavagem para entradas grandes o suficiente.
precisa saber é o seguinte
3
Se ambas as listas contiverem os mesmos valores, este retornará false, a condição deve ser configurada (a) <= set (b) em vez disso
ssi-anik
2
Como essa resposta pode estar correta? Ele pediu uma lista, não um conjunto. Eles são completamente diferentes. E se a = [1, 3, 3, 5, 5] eb = [1, 3, 3, 3, 5]. A teoria dos conjuntos é inadequada para duplicatas.
Eamonn Kenny
1
Eu também apontaria que se a = [1,3,5] eb = [1,3,5], o conjunto (a) <conjunto (b) retornará Falso. Você pode adicionar o operador equals para lidar com esses casos: ie set (a) <= set (b).
Jon
37
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

all(x in two for x in one)

Explicação: Gerador criando booleanos percorrendo a lista oneverificando se esse item está na lista two. all()retorna Truese cada item é verdadeiro, caso contrário False.

Há também uma vantagem que allretorna False na primeira instância de um elemento ausente, em vez de precisar processar cada item.

voidnologo
fonte
Eu acho que para facilitar a leitura e ser explícito do que você está tentando alcançar, set(one).issubset(set(two))é uma ótima solução. Com a solução que publiquei, você poderá usá-lo com qualquer objeto, se eles tiverem os operadores de comparação adequados definidos.
precisa saber é o seguinte
4
Use uma expressão geradora, não uma compreensão de lista; o primeiro permitirá um allcurto-circuito adequado, o segundo executará todas as verificações, mesmo que seja claro desde a primeira verificação que o teste falhará. Basta soltar os colchetes para obter all(x in two for x in one).
precisa saber é o seguinte
Estou errado ou você não pode usar esse método com os locais?
Homper
22

Supondo que os itens sejam laváveis

>>> from collections import Counter
>>> not Counter([1, 2]) - Counter([1])
False
>>> not Counter([1, 2]) - Counter([1, 2])
True
>>> not Counter([1, 2, 2]) - Counter([1, 2])
False

Se você não se importa com itens duplicados, por exemplo. [1, 2, 2]e [1, 2]então basta usar:

>>> set([1, 2, 2]).issubset([1, 2])
True

Testar a igualdade na lista menor após uma interseção é a maneira mais rápida de fazer isso?

.issubsetserá a maneira mais rápida de fazer isso. A verificação do comprimento antes do teste issubsetnão melhora a velocidade, porque você ainda possui itens O (N + M) para percorrer e verificar.

jamylak
fonte
6

Mais uma solução seria usar a intersection.

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one).intersection(set(two)) == set(one)

A interseção dos conjuntos conteria de set one

(OU)

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one) & (set(two)) == set(one)
Super Nova
fonte
2
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(x in two for x in one) == set([True])

Se a lista1 estiver na lista 2:

  • (x in two for x in one)gera uma lista de True.

  • quando fazemos a set(x in two for x in one)tem apenas um elemento (True).

Super Nova
fonte
2

A teoria dos conjuntos é inadequada para listas, pois duplicatas resultarão em respostas erradas usando a teoria dos conjuntos.

Por exemplo:

a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
set(b) > set(a)

não tem significado. Sim, fornece uma resposta falsa, mas isso não está correto, uma vez que a teoria dos conjuntos está apenas comparando: 1,3,5 versus 1,3,4,5. Você deve incluir todas as duplicatas.

Em vez disso, você deve contar cada ocorrência de cada item e fazer um valor maior que igual para verificar. Isso não é muito caro, porque não está usando operações O (N ^ 2) e não requer classificação rápida.

#!/usr/bin/env python

from collections import Counter

def containedInFirst(a, b):
  a_count = Counter(a)
  b_count = Counter(b)
  for key in b_count:
    if a_count.has_key(key) == False:
      return False
    if b_count[key] > a_count[key]:
      return False
  return True


a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

a = [1, 3, 3, 3, 4, 4, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

Ao executar isso, você obtém:

$ python contained.py 
b in a:  False
b in a:  True
Eamonn Kenny
fonte
1

Como ninguém considerou comparar duas strings, aqui está minha proposta.

Obviamente, você pode querer verificar se o canal ("|") não faz parte de nenhuma das listas e talvez escolheu automaticamente outro caractere, mas você entendeu.

Usar uma string vazia como separador não é uma solução, pois os números podem ter vários dígitos ([12,3]! = [1,23])

def issublist(l1,l2):
    return '|'.join([str(i) for i in l1]) in '|'.join([str(i) for i in l2])
y4cine
fonte
0

Perdoe-me se eu estiver atrasado para a festa. ;)

Para verificar se um set Aé um subconjunto de set B, Pythonpossui A.issubset(B)e A <= B. Ele funciona setapenas e funciona muito bem, mas a complexidade da implementação interna é desconhecida. Referência: https://docs.python.org/2/library/sets.html#set-objects

Eu vim com um algoritmo para verificar se list Aé um subconjunto das list Bseguintes observações.

  • Para reduzir a complexidade da localização de um subconjunto, considero apropriado as sortduas listas antes de comparar os elementos para qualificar para o subconjunto.
  • Ajudou-me breaka loopquando o valor do elemento da segunda lista B[j]é maior que o valor do elemento da primeira lista A[i].
  • last_index_jé usado para iniciar loopsobre list Bonde último saiu fora. Ela ajuda a evitar começar comparações a partir do início list B(o que é, como você pode imaginar desnecessário, para começar list Ba partir index 0em posterior iterations.)
  • O(n ln n)Cada complexidade será cada uma para classificar as duas listas e O(n)verificar o subconjunto.
    O(n ln n) + O(n ln n) + O(n) = O(n ln n).

  • O código possui várias printinstruções para ver o que está acontecendo em cada uma iterationdas loop. Estes destinam-se apenas à compreensão.

Verifique se uma lista é subconjunto de outra lista

is_subset = True;

A = [9, 3, 11, 1, 7, 2];
B = [11, 4, 6, 2, 15, 1, 9, 8, 5, 3];

print(A, B);

# skip checking if list A has elements more than list B
if len(A) > len(B):
    is_subset = False;
else:
    # complexity of sorting using quicksort or merge sort: O(n ln n)
    # use best sorting algorithm available to minimize complexity
    A.sort();
    B.sort();

    print(A, B);

    # complexity: O(n^2)
    # for a in A:
    #   if a not in B:
    #       is_subset = False;
    #       break;

    # complexity: O(n)
    is_found = False;
    last_index_j = 0;

    for i in range(len(A)):
        for j in range(last_index_j, len(B)):
            is_found = False;

            print("i=" + str(i) + ", j=" + str(j) + ", " + str(A[i]) + "==" + str(B[j]) + "?");

            if B[j] <= A[i]:
                if A[i] == B[j]:
                    is_found = True;
                last_index_j = j;
            else:
                is_found = False;
                break;

            if is_found:
                print("Found: " + str(A[i]));
                last_index_j = last_index_j + 1;
                break;
            else:
                print("Not found: " + str(A[i]));

        if is_found == False:
            is_subset = False;
            break;

print("subset") if is_subset else print("not subset");

Resultado

[9, 3, 11, 1, 7, 2] [11, 4, 6, 2, 15, 1, 9, 8, 5, 3]
[1, 2, 3, 7, 9, 11] [1, 2, 3, 4, 5, 6, 8, 9, 11, 15]
i=0, j=0, 1==1?
Found: 1
i=1, j=1, 2==1?
Not found: 2
i=1, j=2, 2==2?
Found: 2
i=2, j=3, 3==3?
Found: 3
i=3, j=4, 7==4?
Not found: 7
i=3, j=5, 7==5?
Not found: 7
i=3, j=6, 7==6?
Not found: 7
i=3, j=7, 7==8?
not subset
Hamza Rashid
fonte
Se você classificá-los, já não há qualquer razão para usar uma lista em vez de um conjunto ...
LtWorf
0

O código abaixo verifica se um determinado conjunto é um "subconjunto adequado" de outro conjunto

 def is_proper_subset(set, superset):
     return all(x in superset for x in set) and len(set)<len(superset)
Leo Bastin
fonte
Obrigado @YannVernier Eu modifiquei para incluir verificações vazias para o subconjunto e o superconjunto, para que retorne false quando ambos estiverem vazios.
Leo Bastin
Mas por que você faz isso? Para A ser um subconjunto de B, simplesmente significa A não contém itens que não estejam em B ou, equivalentemente, todos os itens em A também estão em B. O conjunto vazio é, portanto, um subconjunto de todos os conjuntos, inclusive ele próprio. Suas verificações extras afirmam que não são e você afirma que isso é ideal, mas é contrário à terminologia estabelecida. Qual a vantagem?
Yann Vernier
Obrigado @YannVernier Agora, o código verifica se um determinado conjunto é um "subconjunto adequado" de outro conjunto.
Leo Bastin
Isso é tão ruim quanto as respostas que dependem do uso de conjuntos . Enquanto matematicamente falando, um conjunto é uma coleção de elementos distintos, podemos e não devemos confiar nessa suposição ao verificar se uma lista faz parte de outra. Se a lista inicial contiver duplicado, sua função ainda poderá retornar True , mesmo se o elemento em questão estiver presente na segunda lista apenas uma vez. Eu não acho que esse seja o comportamento correto ao tentar comparar listas.
inVader 13/06/19
0

No python 3.5, você pode fazer um [*set()][index]para obter o elemento É uma solução muito mais lenta que outros métodos.

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

result = set(x in two for x in one)

[*result][0] == True

ou apenas com len e definir

len(set(a+b)) == len(set(a))
Super Nova
fonte
0

Aqui está como eu sei se uma lista é um subconjunto de outra, a sequência é importante para mim no meu caso.

def is_subset(list_long,list_short):
    short_length = len(list_short)
    subset_list = []
    for i in range(len(list_long)-short_length+1):
        subset_list.append(list_long[i:i+short_length])
    if list_short in subset_list:
        return True
    else: return False
Mindee
fonte
0

A maioria das soluções considera que as listas não possuem duplicatas. Caso suas listas tenham duplicatas, você pode tentar o seguinte:

def isSubList(subList,mlist):
    uniqueElements=set(subList)
    for e in uniqueElements:
        if subList.count(e) > mlist.count(e):
            return False     
    # It is sublist
    return True

Ele garante que a sublista nunca tenha elementos diferentes da lista ou uma quantidade maior de um elemento comum.

lst=[1,2,2,3,4]
sl1=[2,2,3]
sl2=[2,2,2]
sl3=[2,5]

print(isSubList(sl1,lst)) # True
print(isSubList(sl2,lst)) # False
print(isSubList(sl3,lst)) # False
Ignacio Alorre
fonte
-1

Se você está perguntando se uma lista está "contida" em outra lista, então:

>>>if listA in listB: return True

Se você está perguntando se cada elemento da lista A tem um número igual de elementos correspondentes na lista B, tente:

all(True if listA.count(item) <= listB.count(item) else False for item in listA)
DevPlayer
fonte
Isso não funciona para mim. Retorna false, mesmo se listA == listB
cass
@ cass Eu só testei com cordas. Tente isso em sua máquina. pastebin.com/9whnDYq4
DevPlayer
Eu estava me referindo à parte "if listA in listB: return True", não à segunda parte.
cass
@ cass Considere: ['um', 'dois'] em ['um', 'dois'] produz Falso. ['um', 'dois'] em ['um', 'dois', 'três'] produz Falso. ['um', 'dois'] em [['um', 'dois'], 'três'] produz True. Portanto, sim, se listA == ListB, em seguida, listA na listaB sempre retornará False porque listA precisaria ser um elemento da lista na listaB. Talvez você esteja pensando: Lista de meios listaB "são itens em listA listados como itens em listaB Isso não é o significado de listA na listaB.
DevPlayer
@ Cass Ah, eu vejo como meu post confunde. A postagem original solicitou que a lista A fosse um subconjunto da lista B. Tecnicamente, minha postagem está errada com base na pergunta da postagem original. Para que isso estivesse correto, a pergunta teria que ser solicitada "se listA em [item0, item2, listaA, item3, listaA,]". Não "itens em ['a', 'b', 'c'] em ['d', 'c', 'f', 'a', 'b', 'a']".
DevPlayer
-2

Se a2 is subset of a1entãoLength of set(a1 + a2) == Length of set(a1)

a1 = [1, 2, 3, 4, 5]
a2 = [1, 2, 3]

len(set(a1)) == len(set(a1 + a2))
Super Nova
fonte