在python中仅从集合集中选择子集

时间:2014-09-03 06:47:45

标签: python

我正在尝试删除超集(如果我的集合中有任何集合)并且只返回集合中的子集。我写了下面的代码,但由于处理大型数据集需要很长时间才能执行,有人可能会为此建议其他选项。

例如,如果我有一组这样的冻结

skt = {{D},{E,D,M},{E,M}}

我需要像

这样的输出
skt = {{D},{E,M}}

我的代码是,

for item in skt.copy():
    for other_item in skt.difference([item]):
        if item >= other_item:
            skt.remove(item)
            break

提前致谢。

3 个答案:

答案 0 :(得分:3)

至少可以进行一些小的优化:不要复制一套,而是创建一个新的:

newset = set()
for x in skt:
   if not any(y < x for y in skt):
      newset.add(x)

或者在一行中:

newset = set(x for x in skt if not any(y < x for y in skt))

<强>更新

您可以为每个元素预先计算包含该元素的集合集,然后仅针对包含至少一个元素的集合检查每个集合:

setsForElement = defaultdict(set);
for s in skt:
    for element in s:
        setsForElement[element].add(s);

newset = set(s for s in skt if not any (setForElement < s for element in s for setForElement in setsForElement[element]))

# last line is equal to:
newset = set();
for s in skt:
    good = True;
    for element in s:
        if any(setForElement < s for setForElement in setsForElement[element]):
            good = False;
            break;

    if good:
        newset.add(s);

根据您的数据集,它可能会节省您一些时间。当然,在最坏的情况下(例如,如果您的数据集是某个集合的power set),复杂性将再次为O(N ^ 2)集合比较。或者考虑一下,它可能比直接算法更糟糕,因为你可能多次检查同一组。

答案 1 :(得分:1)

理解

对于列表L的集合,返回L

中没有超集的集合
skt = {{D},{E,D,M},{E,M}}
out = {{D}, {E,M}}

skt = {{D}, {E,G}, {E,H}, {D,E,F}, {E,F,G}}
out = {{D}, {E,G}, {E,H}, {D,E,F}}

如果那是正确的(在我的脑海里,我可能是错的)最坏的情况总是迫使你检查所有对。您可以进行改进,例如不迭代已经删除的元素。或者只检查每一对,并在两个方向上进行,并相应地更新。 itertools.product可能很有用,但同样,它不会更新,所以当你删除一个元素时,我不确定什么是有效的。

更优化的代码可能是:

skt = {frozenset({1}), frozenset({1,2,3}), frozenset({2,3}), frozenset({4}), 
       frozenset({5,7}), frozenset({5,8}), frozenset({5,6,7}), 
       frozenset({6,7,8})}

newset = set()

def check(elem):
    to_delete = []
    ret = True
    for y in skt:
        if elem > y:
            to_delete.append(elem)
            ret = False
            break
        if y > elem:
            to_delete.append(y)
    for d in to_delete:
        skt.remove(d)
    return ret  

while skt:
    checking = skt.pop()
    if check(checking):
        newset.add(checking)

答案 2 :(得分:1)

这种方法与您的方法基本相同,但它按升序基数的顺序运行。优势可能很大,具体取决于您的数据(如果有一些小集可以在早期迭代中淘汰其他很多)。

from collections import defaultdict

def foo(skt):

    # Index the sets by cardinality
    index = defaultdict(lambda: set())
    for s in skt:
        index[len(s)].add(s)

    # For each cardinality i, starting with the lowest
    for i in range(max(index.keys()) + 1):

        # For each cardinality j > i (because supersets must be larger)
        for j in range(i + 1, max(index.keys()) + 1):

            # Remove j-sized supersets
            for y in [y for y in index[j] if any(y >= x for x in index[i])]:
                index[j].remove(y)

    # Flatten the index
    return set(x for xs in index.values() for x in xs)