从一系列值中获取一系列范围

Posted

技术标签:

【中文标题】从一系列值中获取一系列范围【英文标题】:Get series of ranges from series of values 【发布时间】:2021-06-14 06:58:13 【问题描述】:

我想知道如何。我的意思是假设我有一个数字列表:list_values = [4, 3, 4, 4],

我想把它转换成这样的一系列范围(暂时忽略数据的结构):

0: 0 - 4
1: 4 - 8
2: 8 - 13
3: 13 - 18

第 i 个范围的大小对应于数字列表中的第 i 个索引。

这是我迄今为止尝试过的(以及相同基本逻辑的其他变体)。为简单起见,并且因为我坚持这一点,我现在只计算了“开始”值,即范围的开始值(0、4、8、13 ...):

range_start = [0] 
for i, list_values in enumerate(list):
    range_start[i+1] = range_start[i] + list_values[i] + 1

这里的逻辑本质上是递归的,即开始范围的下一个值等于前一个值加上第 (i-1) 个 list_value 加上 1。注意循环的第一次迭代,range_start[1] = range_start[0] + list_value[0] + 1 = 4

但是,我不断收到错误消息,提示 TypeError: 'int' object is not subscriptable。我很困惑,因为我只做整数数学,而不是任何子集。

我希望输出是某种形式的范围,如类似于此的列表或元组:

[[0, 4], [4, 8], [8, 13], [13, 18]]

任何帮助都很好,谢谢!

【问题讨论】:

list是python中的保留关键字,请避免使用。 是的,是的。为了便于阅读,我更改了代码中的变量名称。 【参考方案1】:

如果你想得到答案而不导入任何东西。

list_values = [4, 3, 4, 4]
range_start = []
start = 0
for i, k in enumerate(list_values):
    if start == 0:
        new_range = [start, start + k]
    else:
        new_range = [range_start[i-1][1], start + k]
    range_start.append(new_range)
    start += k + 1

为您提供range_start 的输出:[[0, 4], [4, 8], [8, 13], [13, 18]]

【讨论】:

谢谢,这是一个优雅的解决方案。欣赏它。 我意识到我的问题弄错了!我已经编辑了我的问题以反映这一点。范围应该是 [[0, 4], [4, 8], [8, 13], [13, 18]]。我可以编辑子列表,但我真的很喜欢这个解决方案,知道修复它的逻辑吗? 生成一个列表:[[0, 4], [4, 7], [7, 11], [11, 15]]。还是差一点!我尝试了很多变体,但似乎都没有。 我误读了您的更改,编辑了代码以执行您想要的操作。如果您需要可用于不同范围开始的代码,您应该更新您的模式描述。 再次将代码编辑为我认为在逻辑上更一致的代码。【参考方案2】:

您可以使用zip 的列表推导:

l = [4, 3, 4, 4] 
k = [sum(l[:i])+a+i for i, a in enumerate(l)]
#as full ranges
result = [list(range(a, b+1)) for a, b in zip([0]+k[:-1], k)]
#start and end indices:
result = list(zip([0]+k[:-1], k))

输出

[[0, 1, 2, 3, 4], [4, 5, 6, 7, 8], [8, 9, 10, 11, 12, 13], [13, 14, 15, 16, 17, 18]]
[(0, 4), (4, 8), (8, 13), (13, 18)]

【讨论】:

【参考方案3】:

我想出了这个:

from collections import defaultdict

result = defaultdict(list)

prev = 0
for idx, n in enumerate(l):
  for i in range(prev, prev + n + 1):
    result[idx].append(i)
  prev = i + 1

print(result)

输出:

defaultdict(<class 'list'>, 0: [0, 1, 2, 3, 4], 1: [5, 6, 7, 8], 2: [9, 10, 11, 12, 13], 3: [14, 15, 16, 17, 18])

受@crcvd 回答启发的编辑:

from itertools import accumulate

l= [4, 3, 4, 4]

result = 
prev = 0
for idx, last in enumerate(accumulate(l)):
  result[idx] = list(range(idx + prev, last + idx + 1))
  prev = last

【讨论】:

【参考方案4】:

您可以使用itertools.accumulate 生成右端点列表,然后使用第二次迭代生成左端点。右端点随着您添加存储桶宽度而增长,并且任何给定点的左端点是最后一个右端点加一。

from itertools import accumulate

list_values = [4, 3, 4, 4]

ranges = []

endpoints = list(accumulate(list_values))

for idx, value in enumerate(endpoints):
    # These ranges are closed on the left and 
    # the right.
    if idx == 0:
        ranges.append((1, value))
    else:
        ranges.append((endpoints[idx-1] + 1, value))
print(ranges)
[(1, 4), (5, 7), (8, 11), (12, 15)]

IndexError 的原因是 list_values 是列表的一个元素(一个整数;您正在使用此名称隐藏您的外部列表)并且 list_values[i] 尝试访问它,就好像它是一个列表.

【讨论】:

太棒了!我认为它只是有点偏离,但我不知道如何修复它(第一次使用 itertools)。第一个之后的正确端点似乎是1。我在 for 循环范围内使用这些值。所以 range(5, 7) 只会运行 2 次,而它应该运行 3 次。 这些是左右封闭的范围,所以你应该可以写for rng in ranges: left, right = rng; for i in range(left, right + 1): ... 我从来没有用过累积,不错!

以上是关于从一系列值中获取一系列范围的主要内容,如果未能解决你的问题,请参考以下文章

如何从一系列数据属性的元素中更改 css?

从表中的一系列数字中获取范围并将所有范围存储在 PLSQL/Oracle Forms 中的字符串变量中

JavaScript 获取日期范围从一年中的周数

如何从一系列循环元素中获取点击元素?

从一系列日期中获取所有前 5 个 id(一个数组)

Cordova SQLite - 从一系列 SELECT 查询中获取结果