【问题标题】: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中的保留关键字,请避免使用。
  • 是的,是的。为了便于阅读,我更改了代码中的变量名称。

标签: python loops recursion


【解决方案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): ...
      • 我从来没有用过累积,不错!
      猜你喜欢
      • 1970-01-01
      • 2019-02-05
      • 2019-10-18
      • 2012-11-27
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多