【问题标题】:How to get every value after certain character in a list/array如何在列表/数组中的某个字符之后获取每个值
【发布时间】:2021-12-23 19:00:54
【问题描述】:

例如,如果我有一个列表

data = ['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I']

如何在 B(包括 B)之后获取每个索引,直到它遇到另一个 B 或 O? 例如

output => [[2,3,4],[5,6],[8,9]]

因为第一个 B、I、I 在索引 2、3、4 上 第二个是 B,我在索引 5 和 6 上 最后一个是 B,I 在索引 8 和 9 上

另一个例子

data = ['B', 'I', 'I', 'O', 'O', 'B', 'I', 'B', 'I', 'I', 'O']
output => [[0, 1, 2], [5, 6], [7, 8, 9]]

我正在考虑迭代列表并一一检查。但是有没有更清洁、更有效的方法呢?谢谢

【问题讨论】:

  • 不清楚您要做什么,请详细说明。
  • 但是元素的索引在 B 之后是[3,6,9]。不是 [[2,3,4],[5,6],[8,9]]
  • 对不起,我的意思是,B(包括 B)之后的每个索引,直到它遇到另一个 B 或 O
  • @MuhammadFhadli 堆栈溢出的目的是解决您遇到的问题并扩大人们对编程的理解,而不是为您工作,如果您想回答特定问题,请包括在你的问题中。

标签: python arrays list algorithm arraylist


【解决方案1】:

试试这个!

data = ['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I']

res = []

i = -1
is_first = False
o_found = False
for index, value in enumerate(data):
    if value == 'O':
        o_found=True
    if value != 'B' and o_found:
        continue
    if value == 'B':
        i+=1
        is_first = True
        o_found=False
        res.append([])
    if is_first and o_found is False:
        res[i].append(index)

print(res)

【讨论】:

    【解决方案2】:

    通常我会建议尝试使用来自模块(例如 itertoolsmore_itertools)的分组函数来制定解决方案。但是,由于您有两个不同的分隔符“B”和“O”,它们的行为不同,并且由于您想要生成诸如[[2,3,4],[5,6],[8,9]] 之类的索引列表而不是诸如['BII', 'BI', 'BI'] 之类的值组,因此它将有点麻烦。您必须使用enumerate 来获取索引和值,根据值拆分,做一些额外的工作来区分 B 和 O,然后丢弃值并只保留索引。

    使用more_itertools.split_before

    模块more_itertools有几个功能非常适合分组/切片/分割/开窗,比如more_itertools.split_before

    from more_itertools import split_before
    
    print(list(split_before('OOBIIBIOBI', lambda c: c in 'BO')))
    [['O'], ['O'], ['B', 'I', 'I'], ['B', 'I'], ['O'], ['B', 'I']]
    
    def split_B_O(seq):
        yield from (next(zip(*l)) for l in split_before(enumerate(seq), lambda p: p[1] in 'BO') if l[0][1] == 'B')
    
    print(list(split_B_O('OOBIIBIOBI')))
    # [(2, 3, 4), (5, 6), (8, 9)]
    
    print(list(split_B_O('BIIOOBIBIIO')))
    # [(0, 1, 2), (5, 6), (7, 8, 9)]
    
    print(list(split_B_O('OBI-WAN KENOBI')))
    # [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (12, 13)]
    
    

    使用itertools.groupby

    对相邻值进行分组是函数itertools.groupby 的目的。它根据“键”对值进行分组,“键”是作为参数传递的函数。在这里,我们将编写一个键,每次遇到“O”或“B”时返回不同的标识,如果遇到另一个字符则返回与之前相同的标识。

    from itertools import groupby
    
    def k(c):
        if c[1] in 'OB':
            k.idx += 1
        return k.idx
    
    k.idx = 0
    
    def split_B_O(seq):
        k.idx = 0
        for _,g in groupby(enumerate(seq), k):
            g = list(g)
            if g[0][1] == 'B':
                yield next(zip(*g))
    
    print(list(split_B_O('OOBIIBIOBI')))
    # [(2, 3, 4), (5, 6), (8, 9)]
    
    print(list(split_B_O('BIIOOBIBIIO')))
    # [(0, 1, 2), (5, 6), (7, 8, 9)]
    
    print(list(split_B_O('OBI-WAN KENOBI')))
    # [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (12, 13)]
    

    编写自己的生成器

    但是,由于'B''O'在拆分中扮演的角色不同,并且在通用python模块中可以找到的大多数拆分函数都无法解释拆分字符的两个不同角色,我认为更容易用for循环和一些变量自己编写函数。

    def split_B_O(seq):
        inside_group = False
        j = 0
        for i,c in enumerate(seq):
            if c == 'O' and inside_group:
                yield range(j,i)
                inside_group = False
            elif c == 'B' and inside_group:
                yield range(j, i)
                j = i
            elif c == 'B':
                j = i
                inside_group = True
        if inside_group:
            yield range(j, i+1)
    
    print(list(split_B_O(['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I'])))
    # [range(2, 5), range(5, 7), range(8, 10)]
    
    print(list(split_B_O(['B', 'I', 'I', 'O', 'O', 'B', 'I', 'B', 'I', 'I', 'O'])))
    # [range(0, 3), range(5, 7), range(7, 10)]
    
    print([list(r) for r in split_B_O('OBI-WAN KENOBI')])
    # [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13]]
    

    如果由于某种原因您绝对不喜欢 range 对象,则可以将 yield range(j,i+1) 替换为 yield list(range(j,i+1)) 以获取索引列表。但是range 对象很棒,所以我建议不要这样做。

    【讨论】:

      【解决方案3】:

      您可以找到'B' 的索引并按它对列表进行切片。每个子列表都可以通过'O' 的索引和简单循环返回的索引进行切片

      def sub_list(lst, index):
          lst = lst[:lst.index('O') if 'O' in lst else len(lst)]
          # or with itertools:
          # lst = list(itertools.takewhile(lambda x: x != 'O', lst))
          return [i + index for i in range(len(lst))]
      
      
      def get_indices(data):
          indices = [i for i, x in enumerate(data) if x == 'B'] + [len(data)]
          return [sub_list(data[indices[i]:indices[i + 1]], indices[i]) for i in range(len(indices) - 1)]
      
      
      data1 = ['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I']
      data2 = ['B', 'I', 'I', 'O', 'O', 'B', 'I', 'B', 'I', 'I', 'O']
      print(get_indices(data1)) # [[2, 3, 4], [5, 6], [8, 9]]
      print(get_indices(data2)) # [[0, 1, 2], [5, 6], [7, 8, 9]]
      

      【讨论】:

        猜你喜欢
        • 2019-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-10
        • 1970-01-01
        • 1970-01-01
        • 2019-06-02
        • 1970-01-01
        相关资源
        最近更新 更多