#python #arrays #list #algorithm #arraylist
Вопрос:
Например, если у меня есть список
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, I, который находится в индексе 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]]
Я думаю повторить список и проверить один за другим. Но есть ли какой-нибудь более чистый и эффективный способ сделать это? Спасибо
Комментарии:
1. Неясно, что вы пытаетесь сделать, пожалуйста, уточните.
2. Но индекс элементов идет после того, как B есть
[3,6,9]
. не [[2,3,4],[5,6],[8,9]]3. извините, я имею в виду каждый индекс после B (включая B), пока он не встретится с другим B или O
4. @MuhammadFhadli цель переполнения стека-решить возникающие у вас проблемы и расширить понимание людьми программирования, чтобы оно не работало на вас, если вы хотите получить ответ на конкретную проблему, пожалуйста, включите это в свой вопрос.
Ответ №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:
Вы можете найти индексы '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]]
Ответ №3:
Обычно я бы рекомендовал попытаться разработать решение, используя функции группировки из таких модулей, как itertools
или more_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
объекты-это здорово, поэтому я не рекомендую этого делать.