Расширение фрейма данных: генерация геномных позиций +/- 250 нуклеотидов

У меня есть df, который выглядит так (с еще 300 тысячами строк других геномных координат):

    chromosome  start     end   
    chr1      11859      11879

Я хочу расширить df так, чтобы для каждой строки она включала каждую позицию данной координаты с центром около 250 нуклеотидов с каждой стороны. Мне нужно сделать это эффективным способом, поскольку ожидается, что мой df будет содержать миллионы строк после этого процесса (вероятно, избегайте цикла for). Например, строка chr1:11859-11879 будет расширена до 21 строки.

как рассчитать:

    chromosome  start     end   
    chr1      11859-250   11859+250
    chr1      11860-250   11860+250
    ...
    chr1      11879-250   11879+250

окончательный результат:

    chromosome  start     end   
0   chr1       11609       12109
1   chr1       11610       12110
...
20  chr1        11629      12129

Это кажется таким простым, но мне кажется, что я использую слишком сложные методы, чтобы добраться до этой точки.

Вот общая формула:

chromosome     start            end 
chr1          START-250         START+250
chr1         (START+1)-250     (START+1)+250
...
chr1           END-250          END+250

🤔 А знаете ли вы, что...
С Python можно создавать ботов для социальных сетей и мессенджеров.


2
58
2

Ответы:

Вы можете использовать Index.repeat по разнице end и start и повторять строки с помощью DataFrame.loc , создавать счетчик с помощью GroupBy.cumcount для каждого значения индекса добавлять start столбец, последнее прибавлять и вычитать 250 и DataFrame.reset_index с параметром drop=True, чтобы избежать дублирования значений индекса:

out = df.loc[df.index.repeat(df['end'].sub(df['start']).add(1)), ['chromosome','start']]
counter = out['start'].add(out.groupby(level=0).cumcount())

out = (out.assign(start = counter.sub(250), end = counter.add(250))
          .reset_index(drop=True))
print (out)
   chromosome  start    end
0        chr1  11609  12109
1        chr1  11610  12110
2        chr1  11611  12111
3        chr1  11612  12112
4        chr1  11613  12113
5        chr1  11614  12114
6        chr1  11615  12115
7        chr1  11616  12116
8        chr1  11617  12117
9        chr1  11618  12118
10       chr1  11619  12119
11       chr1  11620  12120
12       chr1  11621  12121
13       chr1  11622  12122
14       chr1  11623  12123
15       chr1  11624  12124
16       chr1  11625  12125
17       chr1  11626  12126
18       chr1  11627  12127
19       chr1  11628  12128
20       chr1  11629  12129

Решено

Один из вариантов — использовать Conditional_join pyjanitor для выполнения соединения диапазона — обратите внимание, что это предполагает, что столбец chromosome не имеет значения — если это так, то это решение не подходит:

# pip install pyjanitor
import pandas as pd
import janitor

numbers = range(df.start.min(), df.end.max()+1)
numbers = pd.Series(numbers,name='numbers')

(df
.conditional_join(
    numbers, 
    ('start','numbers','<='), 
    ('end','numbers','>='), 
    df_columns='chromosome', 
    right_columns='numbers')
.assign(
    start=lambda df: df.numbers-250, 
    end=lambda df: df.numbers+250)
.drop(columns='numbers')
)

   chromosome  start    end
0        chr1  11609  12109
1        chr1  11610  12110
2        chr1  11611  12111
3        chr1  11612  12112
4        chr1  11613  12113
5        chr1  11614  12114
6        chr1  11615  12115
7        chr1  11616  12116
8        chr1  11617  12117
9        chr1  11618  12118
10       chr1  11619  12119
11       chr1  11620  12120
12       chr1  11621  12121
13       chr1  11622  12122
14       chr1  11623  12123
15       chr1  11624  12124
16       chr1  11625  12125
17       chr1  11626  12126
18       chr1  11627  12127
19       chr1  11628  12128
20       chr1  11629  12129