我有以下数据帧:
id start end score
C1 2 592 157
C1 179 592 87
C1 113 553 82
C2 152 219 350
C2 13 70 319
C2 13 70 188
C2 15 70 156
C2 87 139 130
C2 92 140 102
C3 18 38 348
C3 20 35 320
C3 31 57 310
C4 347 51 514
数据按id和分数排序。
id表示DNA序列。
开始和结束表示id中的位置,我希望保留不重叠的切片,并且从重叠中只保留排名最高的:
id start end score
C1 2 592 157
C2 152 219 350
C2 13 70 319
C2 87 139 130
C3 18 38 348
C4 347 51 514
有什么想法吗?
感谢
以下是
- 添加一列来计算范围,并使用最大范围可以嵌套较小范围的任何内容这一事实
- 在范围列上排序以利用此属性
- 删除每个过程中嵌套的任何对象,这样它们就不会被多次比较
这只是为了让跑步变得简单。
import pandas as pd
import numpy as np
import StringIO as sio
data = """
id,start,end,score
C1,2,592,157
C1,179,592,87
C1,113,553,82
C2,152,219,350
C2,13,70,319
C2,13,70,188
C2,15,70,156
C2,87,139,130
C2,92,140,102
C3,18,38,348
C3,20,35,320
C3,31,57,310
C4,347,51,514"""
data = pd.read_csv(sio.StringIO(data))
下一个区块完成工作。
data['range'] = data.end - data.start
data.sort_values(['id','range'])
g = data.groupby('id')
def f(df):
keep = []
while df.shape[0] > 0:
widest = df.iloc[0]
nested = (df.start >= widest.start) & (df.end <= widest.end)
retain = df.loc[nested]
loc = retain.score.values.argmax()
keep.append(retain.iloc[[loc]])
df = df.loc[np.logical_not(nested)]
return pd.concat(keep,0)
out = g.apply(f).drop('range', 1)
out.index = np.arange(out.shape[0])
使用以上数据,输出
In[3]: out
Out[3]:
id start end score
0 C1 2 592 157
1 C2 152 219 350
2 C2 13 70 319
3 C2 87 139 130
4 C2 92 140 102
5 C3 18 38 348
6 C3 31 57 310
7 C4 347 51 514
这更短,满足所有要求。您需要:
- 检查重叠的方法
- 一种按ID对数据进行分组的方法
- 一种在检查重叠后从每组中获取最佳结果的方法
这完成了所有这些,通过使用逻辑和groupby
进行作弊
# from Ned Batchfelder
# http://nedbatchelder.com/blog/201310/range_overlap_in_two_compares.html
def overlap(start1, end1, start2, end2):
"""
Does the range (start1, end1) overlap with (start2, end2)?
"""
return end1 >= start2 and end2 >= start1
def compare_rows(group):
winners = []
skip = []
if len(group) == 1:
return group[['start', 'end', 'score']]
for i in group.index:
if i in skip:
continue
for j in group.index:
last = j == group.index[-1]
istart = group.loc[i, 'start']
iend = group.loc[i, 'end']
jstart = group.loc[j, 'start']
jend = group.loc[j, 'end']
if overlap(istart, iend, jstart, jend):
winner = group.loc[[i, j], 'score'].idxmax()
if winner == j:
winners.append(winner)
skip.append(i)
break
if last:
winners.append(i)
return group.loc[winners, ['start', 'end', 'score']].drop_duplicates()
grouped = df.groupby('id')
print grouped.apply(compare_rows)