python-3.x 如何将第一个元素与第二个元素的连续范围内的两个元素元组分组?

ctehm74n  于 2023-08-08  发布在  Python
关注(0)|答案(2)|浏览(85)

我有一个两个元素元组的列表,每个元组的第一个元素是一个整数,这些元组等价于键值对,实际上这些元组是扁平化的dict_items,使用list(d.items())生成。
第一个位置的元素保证是唯一的(它们是键),并且有很多键值对,其中值是相同的,并且键在连续范围内,这意味着有很多连续的键值对,其中一个键等于前一个键加1。
我想把这些对分成三个一组,其中前两个元素是整数,是这样的连续对的开始和结束,第三个元素是值。
逻辑很简单,如果输入是[(0, 0), (1, 0), (2, 0)],输出应该是[(0, 2, 0)],第一个数字是键范围的开始,第二个数字是键范围的结束,第三个是值。0、1、2是连续的整数。
给定[(0, 0), (1, 0), (2, 0), (3, 1), (4, 1)],输出应为[(0, 2, 0), (3, 4, 1)],具有相同值的连续键被分组。
给定[(0, 0), (1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (7, 2), (9, 2)],输出应该是[(0, 2, 0), (3, 4, 1), (5, 5, 2), (7, 7, 2), (9, 9, 2)],因为5,7,9不是连续的整数,5 + 1 != 7 and 7 + 1 != 9
输入:

[(3, 0),
 (4, 0),
 (5, 0),
 (6, 2),
 (7, 2),
 (8, 2),
 (9, 2),
 (10, 2),
 (11, 2),
 (12, 2),
 (13, 1),
 (14, 1),
 (15, 3),
 (16, 3),
 (17, 3),
 (18, 3),
 (19, 3),
 (20, 3),
 (21, 3),
 (22, 3),
 (23, 3),
 (24, 3),
 (25, 3),
 (26, 3),
 (27, 1),
 (28, 1)]

字符串
输出量:

[(3, 5, 0), (6, 12, 2), (13, 14, 1), (15, 26, 3), (27, 28, 1)]


我的代码给出了正确的输出,但效率不高:

def group_numbers(numbers):
    l = len(numbers)
    i = 0
    output = []
    while i < l:
        di = 0
        curn, curv = numbers[i]
        while i != l and curn + di == numbers[i][0] and curv == numbers[i][1]:
            i += 1
            di += 1
        output.append((curn, numbers[i - 1][0], curv))
    return output


生成测试用例的代码:

def make_test_case(num, lim, dat):
    numbers = {}

    for _ in range(num):
        start = random.randrange(lim)
        end = random.randrange(lim)
        if start > end:
            start, end = end, start
        x = random.randrange(dat)
        numbers |= {n: x for n in range(start, end + 1)}

    return sorted(numbers.items())


如何更有效地做到这一点,例如使用itertools.groupby
答案需要根据我的正确方法进行验证。
请注意,输入中可能存在没有值的空白,我想使问题简短,所以我没有包括这样的测试用例,但确实知道不应该填充这样的空白。更有效的方法应该产生与我相同的输出。
一个手动测试用例来证明这一点:

In [35]: group_numbers([(0, 0), (1, 0), (2, 0), (3, 0), (10, 1), (11, 1), (12, 1), (13, 1)])
Out[35]: [(0, 3, 0), (10, 13, 1)]


注解中建议的测试用例的说明,预期输出为:

In [61]: group_numbers([(3, 0),  (5, 0),  (7, 0)])
Out[61]: [(3, 3, 0), (5, 5, 0), (7, 7, 0)]


[(1, 0), (1, 1), (2, 0)]的输出应该是未定义的,如果遇到它应该会引发异常。这样的输入不是有效的输入。正如您在生成示例的代码中看到的,所有数字只能有一个值。
[(1, 0), (3, 0), (5, 0)]的输出是[(1, 1, 0), (3, 3, 0), (5, 5, 0)]

编辑

我不是一个以英语为母语的人,事实上,我一般不擅长语言(虽然希望不是编程语言),而且我没有人际交往能力(我真的没有人可以交谈),所以我的问题最初可能会令人困惑,我担心如果我把它写得很长,它肯定会包含语法错误。
我已经编辑了我的问题,以包括更多的细节,并更彻底地解释事情,希望使问题不那么混乱。

pod7payv

pod7payv1#

考虑到所有修改,为了通过考虑所有条件来保证正确的输出,您可以使用以下解决方案:

def group_numbers(numbers):
    output = []

    curr_i, curr_j = numbers[0]
    
    start = curr_i
    
    last_ij = numbers[-1]
    last_ij = (last_ij[0], last_ij[1] + 1)
    
    for i, j in numbers[1:] + [last_ij]:
        if i - curr_i == 1 and j == curr_j:
            curr_i = i
        else:
            output.append((start, curr_i, curr_j))
            
            curr_j = j
            curr_i = i
            start = curr_i
    
    return output

字符串
OP测试用例:

In [1]: group_numbers([(3, 0), (4, 0), (5, 0), (6, 2), (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), (12, 2), (13, 1), (14, 1), (15, 3), (16, 3), (17, 3), (18, 3), (19, 3), (20, 3), (21, 3), (22, 3), (23, 3), (24, 3), (25, 3), (26, 3), (27, 1), (28, 1)])
Out[1]: [(3, 5, 0), (6, 12, 2), (13, 14, 1), (15, 26, 3), (27, 28, 1)]

In [2]: group_numbers([(0, 0), (1, 0), (2, 0), (3, 0), (10, 1), (11, 1), (12, 1), (13, 1)])
Out[2]: [(0, 3, 0), (10, 13, 1)]

In [3]: group_numbers([(3, 0), (5, 0), (7, 0)])
Out[3]: [(3, 3, 0), (5, 5, 0), (7, 7, 0)]

旧答案(使用itertools,不考虑最后一种情况)

只需使用itertools.groupby,您就可以做到这一点:

from itertools import groupby
from operator import itemgetter

output = [((v := list(g))[0][0], v[-1][0], i) for i, g in groupby(numbers, itemgetter(1))]


如果没有operator.itemgetter,则是:

output = [((v := list(g))[0][0], v[-1][0], i) for i, g in groupby(numbers, lambda x: x[1])]


OP测试用例(假设解定义为函数group_numbers):

In [1]: group_numbers([(3, 0), (4, 0), (5, 0), (6, 2), (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), (12, 2), (13, 1), (14, 1), (15, 3), (16, 3), (17, 3), (18, 3), (19, 3), (20, 3), (21, 3), (22, 3), (23, 3), (24, 3), (25, 3), (26, 3), (27, 1), (28, 1)])
Out[1]: [(3, 5, 0), (6, 12, 2), (13, 14, 1), (15, 26, 3), (27, 28, 1)]

In [2]: group_numbers([(0, 0), (1, 0), (2, 0), (3, 0), (10, 1), (11, 1), (12, 1), (13, 1)])
Out[2]: [(0, 3, 0), (10, 13, 1)]

In [3]: group_numbers([(3, 0), (5, 0), (7, 0)])
Out[3]: [(3, 7, 0)]

m2xkgtsf

m2xkgtsf2#

使用生成器函数,该生成器函数基于算术和检查元组的第一元素的连续(升序)范围:

from itertools import groupby

def group_numbers(nums):
    for k, g in groupby(nums, key=lambda x: x[1]):
        g = list(i[0] for i in g)
        # check for consecutive ascending of numbers in group 
        if sum(g) == (len(g) * (g[0] + g[-1])) / 2:
            yield (g[0], g[-1], k)

print(list(group_numbers(nums)))

个字符

相关问题