按公共元素对列表进行分组

时间:2017-07-16 02:45:59

标签: python python-2.7

假设我们有一个列表清单如下:

S1 = [{'A_1'}, {'B_1', 'B_3'}, {'C_1'}, {'A_3'}, {'C_2'},{'B_2'}, {'A_2'}]
S2 = []

我想查看此列表,并检查每组检查该集合与该列表的其他集合之间的属性是否为真。然后,如果该属性成立,则将这两个集合连接在一起,并将新集合与其他S1集合进行比较。最后,将这个新集添加到S2。

现在,作为示例,假设我们说如果这两个集合中的所有元素都以相同的字母开头,则属性保持在两个集合之间。 对于上面描述的列表S1,我希望S2为:

S2 = [{'A_1', 'A_3', 'A_2'}, {'B_1', 'B_3', 'B_2'}, {'C_1','C_2'}]

我们应该如何为此编写代码?

这是我的代码。它工作正常,但我认为它效率不高,因为它试图多次添加集([' A_3',' A_2',#A;'])。 假设给出了Checker函数,它检查两个列表之间的属性。我上面提到的那个属性只是一个例子。我们可能想稍后改变它。所以,我们应该将Checker作为一个函数。

 def Checker(list1, list2):

    flag = 1

    for item1 in list1:
        for item2 in list2:
            if item1[0] != item2[0]:
                flag =0

    if flag ==1:
        return 1
    else:
        return 0


S1 = [{'A_1'}, {'B_1', 'B_3'}, {'C_1'}, {'A_3'}, {'C_2'},{'B_2'}, {'A_2'}]
S2 = []

for i in range(0,len(S1)):

    Temp = S1[i]

    for j in range(0,i-1) + range(i+1,len(S1)):

        if Checker(Temp,S1[j]) == 1:

            Temp = Temp.union(S1[j])

    if Temp not in S2:
        S2.append(Temp)

print S2

输出:

[set(['A_3', 'A_2', 'A_1']), set(['B_1', 'B_2', 'B_3']), set(['C_1', 'C_2'])]

6 个答案:

答案 0 :(得分:1)

您可以使用it.chain(*nested_list)作为sorted,然后使用{{1}来展平(很多方法可以使用key}和it.groupby()列表。使用相同的key创建新列表:

In []:
import operator as op
import itertools as it
prop = op.itemgetter(0)
[set(v) for _, v in it.groupby(sorted(it.chain(*S1), key=prop), key=prop)]

Out[]:
[{'A_1', 'A_2', 'A_3'}, {'B_1', 'B_2', 'B_3'}, {'C_1', 'C_2'}]

答案 1 :(得分:1)

def Checker(list1, list2):
    flag = 1

    for item1 in list1:
        for item2 in list2:
            if item1[0] != item2[0]:
                return  0

    return 1

我试图降低Checker()功能的复杂性。

答案 2 :(得分:0)

S1 = [{'A_1'}, {'B_1', 'B_3'}, {'C_1'}, {'A_3'}, {'C_2'},{'B_2'}, {'A_2'}]
from itertools import chain
l = list( chain.from_iterable(S1) )
s = {i[0] for i in l}
t = []
for k in s:
    t.append([i for i in l if i[0]==k])
print (t)

输出:

[['B_1', 'B_3', 'B_2'], ['A_1', 'A_3', 'A_2'], ['C_1', 'C_2']]

答案 3 :(得分:0)

如果考虑性能,我建议使用python中的canoncical分组方法:使用defaultdict

>>> S1 = [{'A_1'}, {'B_1', 'B_3'}, {'C_1'}, {'A_3'}, {'C_2'},{'B_2'}, {'A_2'}]
>>> from collections import defaultdict
>>> grouper = defaultdict(set)
>>> from itertools import chain
>>> for item in chain.from_iterable(S1):
...     grouper[item[0]].add(item)
...
>>> grouper
defaultdict(<class 'set'>, {'C': {'C_1', 'C_2'}, 'B': {'B_1', 'B_2', 'B_3'}, 'A': {'A_1', 'A_2', 'A_3'}})

修改

注意,以下内容适用于Python 3.在Python 2中,.values返回一个列表。

注意,你可能实际上只是想要这个dict,它可能比你的组列表更有用。您还可以使用.values()方法,该方法返回值的视图:

>>> grouper.values()
dict_values([{'C_1', 'C_2'}, {'B_1', 'B_2', 'B_3'}, {'A_1', 'A_2', 'A_3'}])

如果你真的想要一个列表,你总是可以直接得到它:

>>> S2 = list(grouper.values())
>>> S2
[{'C_1', 'C_2'}, {'B_1', 'B_2', 'B_3'}, {'A_1', 'A_2', 'A_3'}]

假设N是所有嵌套集中的项数,则此解为O(N)。

答案 4 :(得分:0)

你的财产是1.对称的和2.传递的?即{。prop(a,b)当且仅当prop(b,a)和2. prop(a,b)prop(b,c)隐含prop(a,c)时?如果是这样,您可以编写一个带有集合的函数,并为相应的等价类提供一些代码。 E.g。

  1 S1 = [{'A_1'}, {'B_1', 'B_3'}, {'C_1'}, {'A_3'}, {'C_2'},{'B_2'}, {'A_2'}]
  2
  3 def eq_class(s):
  4     fs = set(w[0] for w in s)
  5     if len(fs) != 1:
  6         return None
  7     return fs.pop()
  8
  9 S2 = dict()
 10 for s in S1:
 11     cls = eq_class(s)
 12     S2[cls] = S2.get(cls,set()).union(s)
 13
 14 S2 = list(S2.values())

这具有分摊O(len(S1))的优势。另请注意,如果1或2失败,您的最终输出可能取决于S1的顺序。

答案 5 :(得分:0)

使用itertools.groupby

的更详细的版本
from itertools import groupby

S1 = [['A_1'], ['B_1', 'B_3'], ['C_1'], ['A_3'], ['C_2'],['B_2'], ['A_2']]

def group(data):
    # Flatten the data
    l = list((d for sub in data for d in sub))
    # Sort it
    l.sort()

    groups = []
    keys = []
    # Iterates for each group found only
    for k, g in groupby(l, lambda x: x[0]):
        groups.append(list(g))
        keys.append(k)

    # Return keys group data
    return keys, [set(x) for x in groups]

keys, S2 = group(S1)
print "Found the following keys", keys
print "S2 = ", S2

这里的主要想法是减少og append的数量,因为这真的削弱了性能。我们使用生成器对数据进行展平并对其进行排序。然后我们使用groupby对数据进行分组。循环仅每组迭代一次。此处仍有相当多的数据副本可能被删除。

奖励是该功能还会返回数据中检测到的组密钥。