【问题标题】:I want to group tuples based on similar attributes我想根据相似的属性对元组进行分组
【发布时间】:2012-12-10 20:34:55
【问题描述】:

我有一个元组列表。 [ (1, 2), (2, 3), (4, 3), (5, 6), (6, 7), (8, 2) ]

我想根据连接的元组(具有相关值)将它们分组到列表中。

所以最终结果是两个相关元组值的列表 = [ [1, 2, 3, 4, 8], [5, 6, 7] ]

如何编写函数来执行此操作?这是一个求职面试问题。我试图在 Python 中做到这一点,但我很沮丧,只想看看答案背后的逻辑,所以即使是伪代码也会帮助我,所以我可以看到我做错了什么。

我只有几分钟的时间来现场做这件事,但这是我尝试过的:

def find_partitions(connections):
 theBigList = []     # List of Lists
 list1 = []          # The initial list to store lists
 theBigList.append(list1)

 for list in theBigList:
 list.append(connection[1[0], 1[1]])
     for i in connections:
         if i[0] in list or i[1] in list:
             list.append(i[0], i[1])

         else:
             newList = []
             theBigList.append(newList)

本质上,这家伙想要一个相关值列表的列表。 我尝试使用for循环,但意识到它不起作用,然后时间用完了。

【问题讨论】:

  • 你累了什么? Stack Overflow 旨在帮助您解决确切的问题,而不是为您解决问题。
  • 如果输入列表是:[ (1, 2), (4, 3), (2, 3), (5, 6), (6, 7), (8, 2) ]——同样的事情,你会期待什么结果?
  • 如果输入列表是那个,那么是的,同样的返回列表。
  • 你可以看看源代码networkx是如何做到的:import networkx as nx; g = nx.Graph([ (1, 2), (2, 3), (4, 3), (5, 6), (6, 7), (8, 2) ]); nx.connected_components(g) [[8, 1, 2, 3, 4], [5, 6, 7]]
  • 听起来像是连接问题 (en.wikipedia.org/wiki/Connectivity_%28graph_theory%29)。每个数字都是图中的一个节点,元组对是边,您希望通过连接节点的“岛”对节点进行分类。 wiki 文章在“计算方面”下有一些伪代码,可能会对您有所帮助。

标签: python list tuples


【解决方案1】:

当我们填写组件时,在每个阶段都需要考虑三种情况(因为您必须匹配重叠的组):

  1. x 或 y 均不在已找到的任何组件中。
  2. 两者都已经在不同的集合中,x 在 set_i 中,y 在 set_j 中。
  3. 一个或两个都在一个组件中,x 在 set_i 中或 y 在 set_i 中。

我们可以使用内置的set 来提供帮助。 (请参阅 @jwpat 和 @DSM 的更棘手的示例)

def connected_components(lst):
    components = [] # list of sets
    for (x,y) in lst:
        i = j = set_i = set_j = None
        for k, c in enumerate(components):
            if x in c:
                i, set_i = k, c
            if y in c:
                j, set_j = k, c

        #case1 (or already in same set)
        if i == j:
             if i == None:
                 components.append(set([x,y]))
             continue

        #case2
        if i != None and j != None:
            components = [components[k] for k in range(len(components)) if k!=i and k!=j]
            components.append(set_i | set_j)
            continue

        #case3
        if j != None:
            components[j].add(x)
        if i != None:
            components[i].add(y)

    return components               

lst = [(1, 2), (2, 3), (4, 3), (5, 6), (6, 7), (8, 2)]
connected_components(lst)
# [set([8, 1, 2, 3, 4]), set([5, 6, 7])]
map(list, connected_components(lst))
# [[8, 1, 2, 3, 4], [5, 6, 7]]

connected_components([(1, 2), (4, 3), (2, 3), (5, 6), (6, 7), (8, 2)])
# [set([8, 1, 2, 3, 4]), set([5, 6, 7])] # @jwpat's example

connected_components([[1, 3], [2, 4], [3, 4]]
# [set([1, 2, 3, 4])] # @DSM's example

这肯定不是最有效的方法,但可能与他们所期望的相似。 正如 Jon Clements 指出的那样,有一个用于此类计算的库:networkx,在那里它们将更加有效。

【讨论】:

  • 注意 connected_components([ (1, 2), (4, 3), (2, 3), (5, 6), (6, 7), (8, 2) ]) 的错误输出 -- [set([8, 1, 2, 3]), set([3, 4]), set([5, 6, 7]) ]
  • 我不认为排序会起作用:考虑[[1,3],[2,4],[3,4]]
  • @DSM ;( 好吧,这次面试我会失败的。
  • @hayden:除非你以前见过,否则当场进行集合合并很棘手,然后它更像是一个记忆测试......所以我不会担心。 :^)
  • @DSM 我很担心!现在至少我已经完成了练习(我认为......)
【解决方案2】:
l = [ (1, 2), (2, 3), (4, 3), (5, 6), (6, 7), (8, 2) ]

# map each value to the corresponding connected component
d = {}
for i, j in l:
  di = d.setdefault(i, {i})
  dj = d.setdefault(j, {j})
  if di is not dj:
    di |= dj
    for k in dj:
      d[k] = di

# print out the connected components
p = set()
for i in d.keys():
  if i not in p:
    print(d[i])
  p |= d[i]

【讨论】:

    【解决方案3】:

    这当然不优雅,但它确实有效:

    def _grouper(s,ll):
        for tup in ll[:]:
            if any(x in s for x in tup):
                for y in tup:
                    s.add(y)
                    ll.remove(tup)
    
    def grouper(ll,out=None):
        _ll = ll[:]
        s = set(ll.pop(0))
        if out is None:
            out = [s]
        else:
            out.append(s)
    
        l_old = 0
        while l_old != len(_ll):
            l_old = len(_ll)
            _grouper(s,_ll)
    
        if _ll:
            return grouper(_ll,out=out)
        else:
            return out
    
    ll = [ (1, 2), (2, 3), (4, 3), (5, 6), (6, 7), (8, 2) ]
    print grouper(ll)
    

    【讨论】:

    • @JonClements -- 谢谢。由于某种原因,aquamacs 今天的缩进效果不佳...
    【解决方案4】:

    使用sets:

    In [235]: def func(ls):
        new_lis=sorted(sorted(ls),key=min) 
        lis=[set(new_lis[0])]
        for x in new_lis[1:]:
                for y in lis:
                        if not set(x).isdisjoint(y):
                                y.update(x);break 
                else:lis.append(set(x))
        return lis
       .....: 
    
    In [236]: func([(3, 1), (9, 3), (6, 9)])
    Out[236]: [set([1, 3, 6, 9])]
    
    In [237]: func([[2,1],[3,0],[1,3]])
    Out[237]: [set([0, 1, 2, 3])]
    
    In [239]: func([(1, 2), (4, 3), (2, 3), (5, 6), (6, 7), (8, 2)])
    Out[239]: [set([8, 1, 2, 3, 4]), set([5, 6, 7])]
    

    【讨论】:

    • func([[2,1],[3,0],[1,3]]) 给了[set([0, 1, 3]), set([1, 2])],我想。
    • @DSM 你是对的,解决方案已编辑。我认为这个是正确的。
    • func([[8,5], [5,6], [1,2]]).. 我认为您正在朝着第一个解决方案 here 发展。
    • @DSM 我不明白?下面的列表给了我[set([1, 2]), set([8, 5, 6])],那么我的解决方案是正确还是不正确?
    • 糟糕,我的缩进错误。当我将您的else: 置于与for y in lis: 匹配的级别时,我得到了一次正确的答案,但func([(3, 1), (9, 3), (6, 9)]) 在应该将所有内容合并为一个时给出了[set([1, 3, 9]), set([9, 6])]
    【解决方案5】:

    怎么样

    ts = [(1, 2), (2, 3), (4, 3), (5, 6), (6, 7), (8, 2)]
    ss = []
    while len(ts) > 0:
        s = set(ts.pop())
        ol = 0
        nl = len(s)
        while ol < nl:
            for t in ts:
                if t[0] in s or t[1] in s: s = s.union(ts.pop(ts.index(t)))
            ol = nl
            nl = len(s)
        ss.append(s)
    
    print ss
    

    【讨论】:

      猜你喜欢
      • 2020-10-18
      • 1970-01-01
      • 1970-01-01
      • 2020-10-13
      • 2020-05-26
      • 1970-01-01
      • 2021-09-02
      • 1970-01-01
      • 2019-09-10
      相关资源
      最近更新 更多