从一系列值中获取一系列范围
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): ...
我从来没有用过累积,不错!以上是关于从一系列值中获取一系列范围的主要内容,如果未能解决你的问题,请参考以下文章