Python 3:删除表格中的重叠部分

时间:2019-03-03 13:01:11

标签: python pandas sorting filtering

我有一张表(程序的简化输出),需要过滤:

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')

但是如何检查重叠部分(从fromto)并删除得分 更高的那一个?

这是我的预期输出:

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具有两个相等值的重叠命中,因此两个条目都将被踢出。

5 个答案:

答案 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)

并通过depth first search

计算连接的组件
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')])