【问题标题】:group list elements by their difference with respect to each other根据彼此之间的差异对列表元素进行分组
【发布时间】:2026-02-10 19:45:01
【问题描述】:

给定一个列表

A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]

有没有一种简单的方法可以将连续元素之间的差异小于 3 的所有簇分组?

也就是说,获得类似的东西:

[13, 15, 17, 19, 21], [58, 59, 61, 63], [78, 79, 81]

我想知道是否存在任何内置函数,但我找不到类似的东西。我试图使用itertoolsgroupby 来解决这个问题,但我被卡住了。提前谢谢你。

【问题讨论】:

  • 那么您是否忽略了任何少于 3 个元素的组?
  • 好吧,我不会忽视他们。在这个数组中,没有两个独立元素的差异小于 3。如果有,也可以将它们分组。我正在寻找类似“直到”或“同时”之类的东西,区别是 X,然后将它们分组。
  • 那么,1 和 6 不应该被认为是单例集群吗?
  • 没必要。但是,如果您愿意,您可以将它们保留为单例集群,我可以根据它们的长度在第二时刻忽略它们。重要的是“存储元素直到”条件满足:)
  • 关于“存储元素直到”itertools.takewhile 正是这样做的。

标签: python list grouping


【解决方案1】:

你可以使用itertools.groupby:

import itertools
A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]
new_a = [(A[i+1]-A[i], A[i]) for i in range(len(A)-1)]
a = [[a, [c for _, c in b]] for a, b in itertools.groupby(new_a, key=lambda x:x[0] < 3)]
final_groups = [a[i][-1]+[a[i+1][-1][0]] if a[i+1][-1][0] - a[i][-1][-1] < 3 else a[i][-1] for i in range(len(a)-1) if a[i][0]]

输出:

[[13, 15, 17, 18, 19, 21], [58, 59, 61, 63], [78, 79, 81]]

【讨论】:

  • 嗨 Ajax,我使用 A = [25,26,53,54,55,56] 测试您的解决方案,但结果是 [[25, 26]]。我认为您的解决方案中存在错误
  • 我认为这是一个非常顺利的想法。但是,我认为 final_groups 调用是错误的。我的建议是让 new_a 的第二个索引具有元组 (A[i],A[i+1]) 而不仅仅是 A[i] 并将 final_groups 替换为 [[t[1][0][ 0]] + [q[-1] for q in t[1]] for t in a if t[0]]
【解决方案2】:

这是一种使用迭代的方法。

例如:

A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]
res = []
temp = []
l = len(A)-1

for i,v in enumerate(A):
    if i+1 > l:
        break

    if abs(v - A[i+1]) < 3:
        temp.append(v)
    else:
        if temp:
            temp.append(v)
            res.append(temp)
            temp = []
print(res)

输出:

[[13, 15, 17, 18, 19, 21], [58, 59, 61, 63], [78, 79, 81]]

【讨论】:

    【解决方案3】:

    根据您的评论

    重要的是“存储元素直到”条件满足

    您可以为此使用itertools.takewhile

    takewhile(predicate, iterable) --> takewhile 对象

    从一个可迭代对象中返回连续的条目,只要 每个条目的谓词计算结果为真。

    这个解决方案当然有改进的余地,但要点是takewhile的使用

    class Grouper:
        """simple class to perform comparison when called, storing last element given"""
        def __init__(self, diff):
            self.last = None
            self.diff = diff
        def predicate(self, item):
            if self.last is None:
                return True
            return abs(self.last - item) < self.diff
        def __call__(self, item):
            """called with each item by takewhile"""
            result = self.predicate(item)
            self.last = item
            return result
    
    
    def group_by_difference(items, diff=3):
        results = []
        start = 0
        remaining_items = items
        while remaining_items:
            g = Grouper(diff)
            group = [*itertools.takewhile(g, remaining_items)]
            results.append(group)
            start += len(group)
            remaining_items = items[start:]
        return results
    

    这为您提供了带有单例集群的分组项目。

    [[1],
     [6],
     [13, 15, 17, 18, 19, 21],
     [29],
     [36],
     [53],
     [58, 59, 61, 63],
     [78, 79, 81],
     [102],
     [114]]
    

    【讨论】:

      【解决方案4】:

      类似于this answer,它要求运行相同的数字,可以在这里使用numpy.split

      import numpy as np
      
      def plateaus(A, atol=3):
          runs = np.split(A, np.where(np.abs(np.diff(A)) >= atol)[0] + 1)
          return [list(x) for x in runs if len(x) > 1]
      
      A = [1, 6, 13, 15, 17, 18, 19, 21, 29, 36, 53, 58, 59, 61, 63, 78, 79, 81, 102, 114]
      print(plateaus(A))
      [[13, 15, 17, 18, 19, 21], [58, 59, 61, 63], [78, 79, 81]]
      

      如果不对长度进行过滤,这会为您提供像 the itertools.takehwile approach by @sytech 这样的单例集群。

      【讨论】:

        最近更新 更多