25

我有两个清单说

List1 = ['a','c','c']
List2 = ['x','b','a','x','c','y','c']

现在我想知道 List1 的所有元素是否都在 List2 中。在这种情况下,一切都有。我不能使用子集函数,因为我可以在列表中有重复的元素。我可以使用 for 循环来计算 List1 中每个项目的出现次数,看看它是否小于或等于 List2 中的出现次数。有一个更好的方法吗?

谢谢。

4

6 回答 6

39

当出现次数无关紧要时,您仍然可以通过动态创建集合来使用子集功能:

>>> list1 = ['a', 'c', 'c']
>>> list2 = ['x', 'b', 'a', 'x', 'c', 'y', 'c']
>>> set(list1).issubset(list2)
True

如果您需要检查每个元素在第二个列表中出现的次数是否至少与在第一个列表中出现的次数一样多,您可以使用 Counter 类型并定义您自己的子集关系:

>>> from collections import Counter
>>> def counterSubset(list1, list2):
        c1, c2 = Counter(list1), Counter(list2)
        for k, n in c1.items():
            if n > c2[k]:
                return False
        return True
   
>>> counterSubset(list1, list2)
True
>>> counterSubset(list1 + ['a'], list2)
False
>>> counterSubset(list1 + ['z'], list2)
False

如果您已经有计数器(这可能是存储数据的有用替代方法),您也可以将其写为一行:

>>> all(n <= c2[k] for k, n in c1.items())
True
于 2013-02-28T23:40:29.870 回答
5

请注意以下事项:

>>>listA = ['a', 'a', 'b','b','b','c']
>>>listB = ['b', 'a','a','b','c','d']
>>>all(item in listB for item in listA)
True

如果您像用英语一样阅读“all”行,这并没有错,但可能会产生误导,因为 listA 有第三个 'b' 而 listB 没有。

这也有同样的问题:

def list1InList2(list1, list2):
    for item in list1:
        if item not in list2:
            return False
    return True

只是一个注释。以下不起作用:

>>>tupA = (1,2,3,4,5,6,7,8,9)
>>>tupB = (1,2,3,4,5,6,6,7,8,9)
>>>set(tupA) < set(TupB)
False

如果将元组转换为列表,它仍然不起作用。我不知道为什么字符串可以工作,但整数不行。

有效,但同样存在不计算元素出现次数的问题:

>>>set(tupA).issubset(set(tupB))
True

使用集合并不是多出现元素匹配的综合解决方案。

但这是一个单线解决方案/适应shantanoo的答案,无需尝试/例外:

all(True if sequenceA.count(item) <= sequenceB.count(item) else False for item in sequenceA)

使用三元条件运算符包装列表推导的内置函数。Python 太棒了!请注意,“<=”不应为“==”。

有了这个解决方案,序列 A 和 B 可以是类型元组和列表以及其他具有“计数”方法的“序列”。两个序列中的元素都可以是大多数类型。我不会像现在这样将它与dicts一起使用,因此使用“sequence”而不是“iterable”。

于 2015-06-25T08:59:29.023 回答
1

使用Counter和内置交集方法的解决方案(注意这-是正确的多重集差异,而不是元素减法):

from collections import Counter

def is_subset(l1, l2):
    c1, c2 = Counter(l1), Counter(l2)
    return not c1 - c2

测试:

>>> List1 = ['a','c','c']
>>> List2 = ['x','b','a','x','c','y','c']
>>> is_subset(List1, List2)
True
于 2018-05-04T17:56:50.957 回答
1

我不能使用子集函数,因为我可以在列表中有重复的元素。

这意味着您希望将列表视为multisets而不是sets。在 Python 中处理多重集的常用方法是collections.Counter

ACounter是用于计算可散列对象的 dict 子类。它是一个无序集合,其中元素存储为字典键,它们的计数存储为字典值。计数可以是任何整数值,包括零计数或负计数。该类Counter类似于其他语言中的bags 或multisets。

而且,虽然您可以Counter通过循环和比较计数来实现多集的子集(用 实现),就像在poke 的回答中那样,这是不必要的 - 就像您可以通过循环和测试实现集合的子集(用setor实现)一样,但它是不必要的。frozensetin

Counter类型已经实现了所有集合运算符,这些运算符以明显的方式扩展为多集。<1因此,您可以根据这些运算符编写子集,它适用于两者set并且Counter开箱即用。

与(多)集差异:2

def is_subset(c1, c2):
    return not c1 - c2

或与(多)集交集:

def is_subset(c1, c2):
    def c1 & c2 == c1

1. 你可能想知道为什么,如果Counter实现了集合运算符,它不仅仅实现了真子集<<=子集的and。虽然我找不到电子邮件线程,但我很确定这是讨论过的,答案是“集合运算符”被定义为初始版本中定义的特定运算符集collections.abc.Set(此后已扩展, IIRC…),并非所有set为了方便而碰巧包含的运算符,Counter就像没有命名方法那样intersection的命名方法对其他类型友好,而&不仅仅是因为set这样做。

2. 这取决于这样一个事实,即 Python 中的集合在为空时预期为假,否则为真。这在此处记录了内置类型,并且在此处解释了bool测试回退​​到的事实- 但它最终只是一种约定,因此如果有充分的理由,像 numpy 数组这样的“准集合”可能会违反它。它适用于“真正的集合”,如,等。如果你真的担心,你可以写,但请注意,这违反了PEP 8的精神。lenCounterOrderedDictlen(c1 - c2) == 0

于 2018-05-04T18:27:15.473 回答
0

如果 List1 中的所有项目都在 List2 中,这将返回 true

def list1InList2(list1, list2):
    for item in list1:
        if item not in list2:
            return False
    return True
于 2013-03-01T00:03:16.487 回答
-1
def check_subset(list1, list2):
    try:
        [list2.remove(x) for x in list1]
        return 'all elements in list1 are in list2'
    except:
        return 'some elements in list1 are not in list2'
于 2013-03-05T20:54:04.963 回答