Учитывая список фрагментов, как мне разбить последовательность по ним? - PullRequest
11 голосов
/ 12 ноября 2009

Учитывая список фрагментов, как мне отделить последовательность на их основе?

У меня есть длинные аминокислотные строки, которые я хотел бы разделить на основе значений старт-стоп в списке. Пример, пожалуй, самый понятный способ объяснить это:

str = "MSEPAGDVRQNPCGSKAC"
split_points = [[1,3], [7,10], [12,13]]

output >> ['M', '(SEP)', 'AGD', '(VRQN)', 'P', '(CG)', 'SKAC']

Дополнительные скобки показывают, какие элементы были выбраны из списка split_points. Я не ожидаю, что точки начала-остановки когда-либо будут перекрываться.

У меня есть куча идей, которые сработают, но кажутся ужасно неэффективными (с точки зрения длины кода), и кажется, что должен быть хороший питонический способ сделать это.

Ответы [ 6 ]

9 голосов
/ 12 ноября 2009

Странный способ разбить строки у вас там:

def splitter( s, points ):
    c = 0
    for x,y in points:
        yield s[c:x]
        yield "(%s)" % s[x:y+1]
        c=y+1
    yield s[c:]

print list(splitter(str, split_points))
# => ['M', '(SEP)', 'AGD', '(VRQN)', 'P', '(CG)', 'SKAC']

# if some start and endpoints are the same remove empty strings.
print list(x for x in splitter(str, split_points) if x != '')
2 голосов
/ 12 ноября 2009

Вот простое решение для вас. захватить каждый из наборов, указанных точкой.

In[4]:  str[p[0]:p[1]+1] for p in split_points]
Out[4]: ['SEP', 'VRQN', 'CG']

Чтобы получить круглые скобки:

In[5]:  ['(' + str[p[0]:p[1]+1] + ')' for p in split_points]
Out[5]: ['(SEP)', '(VRQN)', '(CG)']

Вот более чистый способ сделать все это:

results = []

for i in range(len(split_points)):
    start, stop = split_points[i]
    stop += 1

    last_stop = split_points[i-1][1] + 1 if i > 0 else 0

    results.append(string[last_stop:start])        
    results.append('(' + string[start:stop] + ')')

results.append(string[split_points[-1][1]+1:])

Все приведенные ниже решения плохие, и больше для развлечения, чем что-либо еще, не используйте их!

Это скорее решение WTF, но я решил опубликовать его, поскольку об этом попросили в комментариях:

split_points = [(x, y+1) for x, y in split_points]
split_points = [((split_points[i-1][1] if i > 0 else 0, p[0]), p) for i, p in zip(range(len(split_points)), split_points)]
results = [string[n[0]:n[1]] + '\n(' + string[m[0]:m[1]] + ')' for n, m in split_points] + [string[split_points[-1][1][1]:]]
results = '\n'.join(results).split()

все еще пытается выяснить один вкладыш, вот два:

split_points = [((split_points[i-1][1]+1 if i > 0 else 0, p[0]), (p[0], p[1]+1)) for i, p in zip(range(len(split_points)), split_points)]
print '\n'.join([string[n[0]:n[1]] + '\n(' + string[m[0]:m[1]] + ')' for n, m in split_points] + [string[split_points[-1][1][1]:]]).split()

И тот лайнер, который никогда не должен использоваться:

print '\n'.join([string[n[0]:n[1]] + '\n(' + string[m[0]:m[1]] + ')' for n, m in (((split_points[i-1][1]+1 if i > 0 else 0, p[0]), (p[0], p[1]+1)) for i, p in zip(range(len(split_points)), split_points))] + [string[split_points[-1][1]:]]).split()
0 голосов
/ 12 ноября 2009
>>> str = "MSEPAGDVRQNPCGSKAC"
>>> split_points = [[1,3], [7,10], [12,13]]
>>>
>>> all_points = sum(split_points, [0]) + [len(str)-1]
>>> map(lambda i,j: str[i:j+1], all_points[:-1], all_points[1:])
['MS', 'SEP', 'PAGDV', 'VRQN', 'NPC', 'CG', 'GSKAC']
>>>
>>> str_out = map(lambda i,j: str[i:j+1], all_points[:-1:2], all_points[1::2])
>>> str_in = map(lambda i,j: str[i:j+1], all_points[1:-1:2], all_points[2::2])
>>> sum(map(list, zip(['(%s)' % s for s in str_in], str_out[1:])), [str_out[0]])
['MS', '(SEP)', 'PAGDV', '(VRQN)', 'NPC', '(CG)', 'GSKAC']
0 голосов
/ 12 ноября 2009

Вот решение, которое преобразует ваши split_points в обычные строковые фрагменты, а затем распечатывает соответствующие фрагменты:

str = "MSEPAGDVRQNPCGSKAC"
split_points = [[1, 3], [7, 10], [12, 13]]

adjust = [s for sp in [[x, y + 1] for x, y in split_points] for s in sp]
zipped = zip([None] + adjust, adjust + [None])

out = [('(%s)' if i % 2 else '%s') % str[x:y] for i, (x, y) in
       enumerate(zipped)]

print out

>>> ['M', '(SEP)', 'AGD', '(VRQN)', 'P', '(CG)', 'SKAC']
0 голосов
/ 12 ноября 2009

Наверное, не для элегантности, а только потому, что я могу сделать это в личном кабинете :) 1001 *

>>> reduce(lambda a,ij:a[:-1]+[str[a[-1]:ij[0]],'('+str[ij[0]:ij[1]+1]+')',
            ij[1]], split_points, [0])[:-1] + [str[split_points[-1][-1]+1:]]

['M', '(SEP)', 'PAGD', '(VRQN)', 'NP', '(CG)', 'SKAC']

Может быть, вам это нравится. Вот некоторые объяснения:

В вашем вопросе вы передаете один набор срезов, и неявно хотите, чтобы у вас также был набор дополнений срезов (чтобы генерировать не заключенные в скобки [это английский?] Срезы). Таким образом, в основном, каждому срезу [i, j] не хватает предыдущего j. например [7,10] не хватает 3 и [1,3] не хватает 0.

reduce обрабатывает списки и на каждом шаге передает вывод до сих пор (a) плюс следующий элемент ввода (ij). Хитрость в том, что помимо создания простого вывода, мы добавляем каждый раз дополнительную переменную - своего рода память - которая на следующем шаге извлекается в a[-1]. В этом конкретном примере мы сохраняем последнее значение j, и, следовательно, всегда имеем полную информацию для предоставления подстроки как в скобках, так и в скобках.

Наконец, память очищается с помощью [: -1] и заменяется остатком оригинальной строки в [str[split_points[-1][-1]+1:]].

0 голосов
/ 12 ноября 2009

Вот код, который будет работать.

result = []
last_end = 0
for sp in split_points:
  result.append(str[last_end:sp[0]])
  result.append('(' + str[sp[0]:sp[1]+1] + ')')
  last_end = sp[1]+1
result.append(str[last_end:])

print result

Если вам нужны только части в скобках, это становится немного проще:

result = [str[sp[0]:sp[1]+1] for sp in split_points]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...