我有一张表(程序的简化输出),需要过滤:
id hit from to value
A hit1 56 102 0.00085
B hit2 89 275 0.00034
B hit3 240 349 0.00034
C hit4 332 480 3.40E-15
D hit5 291 512 3.80E-24
D hit6 287 313 0.00098
D hit7 381 426 0.00098
D hit8 287 316 0.0029
D hit9 373 422 0.0029
D hit10 514 600 0.0021
对于每个ID,df
应该按from
进行排序,如果匹配重叠,则将匹配的value
放在较低的位置。
到目前为止,这是我的代码,它首先从from
开始,然后从value
开始:
import pandas
df = pandas.read_csv("table", sep='\s+', names=["id", "hit", "from", "to", "value"])
df.sort_values(['from', "value"]).groupby('id')
但是如何检查重叠部分(从from
到to
)并删除得分 更高的那一个?
这是我的预期输出:
id hit from to valu
A hit1 56 102 0.00085
C hit4 332 480 3.40E-15
D hit5 291 512 3.80E-24
D hit10 514 600 0.0021
请注意,id B
具有两个相等值的重叠命中,因此两个条目都将被踢出。
答案 0 :(得分:2)
如果您不介意代码中有多行,那么类似的事情应该起作用,我想...(这里是python newbie ...)source
df.sort_values(['from', "value"]).groupby('id')
df.drop_duplicates(subset=['id', 'value'], keep=False, inplace=True)
“ keep ”参数设置为false,因为您根本不希望复制行。
产生的结果:
id hit from to value
0 A hit1 56 102 0.00085
3 C hit4 332 480 3.40E-15
4 D hit5 291 512 3.80E-24
9 D hit10 514 600 0.0021
并摆脱混乱的索引列:
df.reset_index(drop=True, inplace=True)
得出的结果:
id hit from to value
0 A hit1 56 102 0.00085
1 C hit4 332 480 3.40E-15
2 D hit5 291 512 3.80E-24
3 D hit10 514 600 0.0021
PS:这是我第一次给出答案,请保持温柔。而且,我仍在学习英语。
答案 1 :(得分:1)
typeTolerance
所以我的解决方案使用遮罩检查重叠。通过对“ from”值进行排序,并检查下一个“ from”值是否小于上一个“ to”值。 np.inf只是确保分组中的第一个值始终为0。
然后,在df中将mask设为自己的列。然后,我们根据需要进行的分组,删除所有重复项,重置索引,最后删除掩码。
答案 2 :(得分:0)
如果您对id =='D'进行排序
id hit from to value
5 D hit6 287 313 9.800000e-04
7 D hit8 287 316 2.900000e-03
4 D hit5 291 512 3.800000e-24
8 D hit9 373 422 2.900000e-03
6 D hit7 381 426 9.800000e-04
9 D hit10 514 600 2.100000e-03
重叠将是:
击中6、8和5 =保持5 bc最低值
击中9和7 =击倒7
打10是独自保留吗?
答案 3 :(得分:0)
首先,我们引入唯一的ID
并使用pd.Interval
:
df['ID'] = range(df.shape[0])
df['Interval'] = df.apply(lambda x: pd.Interval(x['from'], x['to'], closed='both'), axis=1)
此后,我们将df自身加入并计算重叠部分:
columns = ['id', 'Interval', 'ID']
connected = df[columns].merge(df[columns], on='id')
connected['Overlap'] = connected.apply(lambda x: x['Interval_x'].overlaps(x['Interval_y']), axis=1)
connected = connected.loc[connected['Overlap'] == True, ['id', 'ID_x', 'ID_y']]
现在,我们知道哪些ID重叠,但是我们不知道其中的哪些ID建立了连接的组件。通常,这不能通过简单的算法(例如重新编排)来完成,但是有点graph theory会有所帮助。所以我们建立一个图
graph = connected.groupby(['id', 'ID_x']).agg(list)
def connections(graph, id):
def dict_to_df(d):
df = pd.DataFrame(data=[d.keys(), d.values()], index=['ID', 'Subgraph']).T
df['id'] = id
return df[['id', 'Subgraph', 'ID']]
def dfs(node, num):
visited[node] = num
for _node in graph.loc[node].iloc[0]:
if _node not in visited:
dfs(_node, num)
visited = {}
graph = graph.loc[id]
for (num, node) in enumerate(graph.index):
if node not in visited:
dfs(node, num)
return dict_to_df(visited)
dfs = []
for id in graph.index.get_level_values(0).unique():
dfs.append(connections(graph, id))
conns = pd.concat(dfs)
conns
保留了连接的组件,我们可以将它们放在一起:
data = df.merge(conns[['Subgraph', 'ID']], on=['ID'])
我们的最后一个任务是选择我们要保留的行:
def select_min(x):
m = x['value'].min()
if len(x) > 1 and (x['value'] == m).all():
return -1
else:
return x['value'].idxmin()
selected = data.groupby(['id', 'Subgraph'])['value', 'ID'].apply(select_min)
selected = selected[selected >= 0]
现在我们完成了:
print(df.loc[df.ID.isin(selected), :].drop(columns=['ID', 'Interval']))
id hit from to value
0 A hit1 56 102 8.500000e-04
3 C hit4 332 480 3.400000e-15
4 D hit5 291 512 3.800000e-24
9 D hit10 514 600 2.100000e-03
答案 4 :(得分:0)
似乎实现起来很简单,就是按组分组然后逐行进行.pandas中似乎没有一种方法可以有效地一次对多个行和多个列进行操作。
def strip(group):
non_overlapping=[]
overlapping = [list(group.itertuples())[0]]
end = list(group.itertuples())[0].to
for row in list(group.itertuples())[1:]:
if row[3]<=end:
overlapping.append(row)
if row.to > end:
end = row.to
else:
non_overlapping.append(reduce_overlap(overlapping))
overlapping=[row]
non_overlapping.append(reduce_overlap(overlapping))
return non_overlapping
搜索算法非常简单,因为您有一个排序的非空行组,它们都具有相同的ID。您转到第一个元素并结束。在此之前开始的所有后续行都是重叠的,如果在我们将其结尾设为新的结束值之后结束,则它们将结束。
def reduce_overlap(overlapping):
overlapping= sorted(overlapping,key=lambda x: x.value)
if len(overlapping)==1 or overlapping[0].value != overlapping[1].value:
return overlapping[0]
else:
return []
要找到要返回的值,我们将按值排序,如果它们是两个相同的值,则不返回任何值。
编辑: 这是将其应用于未经测试的整个数据框的函数。
def nonoverlapping(df):
return df.Dataframe.from_records([strip(group) for name,group in df.sort_values(['from', "value"]).groupby('id')])