

下記のデータに対してtag 'a'のstart-endのデータに対して 'b'のstart-
endで重複する部分を削除したいのですがどのような処理をしたら良いでしょうか。

[code]

    import pandas as pd
    
    data = [
        ['a', 1, 10],
        ['b', 2, 3],
        ['b', 4, 6],
        ['b', 9, 11]
    ]
    columns = ['tag', 'start', 'end']
    df = pd.DataFrame(data, columns=columns)
    
[/code]

**期待する結果:**

[code]

    result = [
        ['a', 1, 2]
        ['b', 2, 3]
        ['a', 3, 4]
        ['b', 4, 6]
        ['a', 6, 9]
        ['b', 9, 11]
    ]
    result_df = pd.DataFrame(result, columns=columns)
    
[/code]


CC BY-SA 4.0
2023-06-30T02:53:00.893
95421
2023-07-06T14:37:10.390
2023-06-30T05:19:51.350
3060
58920
question
python pandas
pythonでのデータ処理


以下のようにオーバーラップがあるとその後の処理が大変そうなので  
そのようなオーバーラップも隣り合うデータもないものとして回答します

| tag | start | end  
---|---|---|---  
0 | b | 2 | 5  
1 | b | 3 | 4  
2 | b | 3 | 6  
3 | b | 6 | 7  
  
* * *

Pythonで `zip`など用いる方法

[code]

    >>> lst = sorted([(2, 3), (4, 6), (9, 11)], key=lambda v: v[0])
    >>> lst
    [(2, 3), (4, 6), (9, 11)]
    >>> stop, start = zip(*lst)
    >>> start
    (3, 6, 11)
    >>> stop
    (2, 4, 9)
    >>> list(zip((1,) +start, stop))
    [(1, 2), (3, 4), (6, 9)]
    
[/code]

既存の `df`と合成

[code]

    >>> df[df.tag == 'b']
      tag  start  end
    1   b      2    3
    2   b      4    6
    3   b      9   11
    >>> tmp = pd.DataFrame(list(zip((1,) +start, stop)), columns=['start', 'end'])
    >>> tmp['tag'] = 'a'
    >>> ndf = pd.concat([df[df.tag == 'b'], tmp])
    >>> ndf.sort_values('start')
      tag  start  end
    0   a      1    2
    1   b      2    3
    1   a      3    4
    2   b      4    6
    2   a      6    9
    3   b      9   11
    
[/code]

上のコードでは `(1, 10)` 固定なので, こんな風に

[code]

    >>> r = range(1, 10)
    >>> if stop[0] > r.start:
    ...     start = (r.start,) +start
    ... else:
    ...     stop = stop[1:]
    ...
    >>> if start[-1] < r.stop:
    ...     stop += (r.stop,)
    ...
    >>> list(zip(start, stop))
    [(1, 2), (3, 4), (6, 9)]
    >>>
    
[/code]


CC BY-SA 4.0
2023-07-04T07:12:54.230
95459
2023-07-04T07:12:54.230
43025
95421
answer


a も b もオーバーラップがある場合

[code]

    import pandas as pd
    
    data = [
        ['a', 1, 4],
        ['a', 3, 5],
        ['a', 6, 10],
        ['b', 2, 5],
        ['b', 3, 4],
        ['b', 3, 6],
        ['b', 6, 7],
        ['b', 9, 11]
    ]
    columns = ['tag', 'start', 'end']
    df = pd.DataFrame(data, columns=columns)
    df = df.sort_values('start').reset_index(drop=True)
    
    def get_part_a(dft, start, end):
        ans = []
        for idx, row in dft.iterrows():
            if start < row['start']:
                if row['start'] <= end:
                    ans.append(['a', start, row['start']])
                elif start < end:
                    ans.append(['a', start, end])
            if start < row['end']:
                start = row['end']
        if start < end:
            ans.append(['a', start, end])
        return ans
    
    df_a = df[df['tag']=='a']
    lst_a = [[df_a['start'].iloc[0], df_a['end'].iloc[0]]]
    for i in range(len(df_a)):
        if i > 0:
            if df_a['start'].iloc[i] <= lst_a[-1][1]:
                if df_a['end'].iloc[i] > lst_a[-1][1]:
                    lst_a[-1][1] = df_a['end'].iloc[i]
            else:
                lst_a.append([df_a['start'].iloc[i], df_a['end'].iloc[i]])
    
    df_b = df[df['tag']=='b']
    lst = [get_part_a(df_b, lst_a[i][0], lst_a[i][1]) for i in range(len(lst_a))]
    result = df_b
    for df_ in lst:
        result = pd.concat([result, pd.DataFrame(df_, columns=columns)])
    result = result.sort_values('start').reset_index(drop=True)
    print(result)
    """
      tag  start  end
    0   a      1    2
    1   b      2    5
    2   b      3    4
    3   b      3    6
    4   b      6    7
    5   a      7    9
    6   b      9   11
    """
    
[/code]


CC BY-SA 4.0
2023-07-06T14:37:10.390
95504
2023-07-06T14:37:10.390
41756
95421
answer