У меня есть 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 можно создавать ботов для социальных сетей и мессенджеров.
Вы можете использовать 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