Фильтрация списка целочисленных диапазонов по максимальному проценту и значению перекрытия - PullRequest
1 голос
/ 20 сентября 2019

У меня есть список диапазонов, таких как:

[12-48,40-80,60-105,110-130,75-400]

И мне нужно отфильтровать или удалить диапазоны, которые перекрывают более x цифр (например, перекрывают более 10) и / или перекрываютболее x% (допустим, 20%) наименьшего из сравниваемых диапазонов.

В настоящий момент я использую цикл for, чтобы проверить каждый диапазон за раз и сравнить его со следующим, чтобы увидеть, перекрываются ли они.за мои установленные пределы, и если это так, удалите его.Это не работает, как в примере, который я показываю, я получаю следующий результат:

[12-48,75-400]

Диапазон [40-80] не должен был быть удален, поскольку он не перекрывается с нашими двумя оставшимися диапазонами за пределамино поскольку он перекрывался [60-105] и был меньшим из 2, он был удален.Правильные оставшиеся диапазоны должны быть:

[12-48,40-80,75-400]

Я не думаю, что простой цикл for - это решение здесь, но я в растерянности.Пожалуйста, дайте мне знать, если что-то неясно.

Текущий код

Части с GeneA / GenePrev / GeneAND - это то, как я вычисляю перекрытие в%, и его можно игнорировать.

        start = int(key.split(',')[0])
        stop = int(key.split(',')[1])
        length = stop - start
        if First == True:
            Both_Frames[key] = value
            First = False
            GeneA[start:stop] = [1] * (stop - start)
            GenePrev = GeneA
            PrevStart = start
            PrevStop = stop
            prevlength = PrevStop - PrevStart
        else:
            GeneA[start:stop] = [1] * (stop - start)
            Gene_AND = GenePrev & GeneA

            if start == PrevStart:
                GenePrev = GeneA

                ######Need to delete item from dictionary which is overlapping
                Both_Frames.popitem(last=False)
                Both_Frames[key] = value
                PrevStart = start
                PrevStop = stop
                prevlength = PrevStop - PrevStart
            elif start >= PrevStart and stop <= PrevStop:

                continue
            elif  np.count_nonzero(Gene_AND) <= (length * OverLapPercentage) and np.count_nonzero(Gene_AND) <= OverLapNT:
                GenePrev = GeneA
                Both_Frames[key] = value
                PrevStart = start
                PrevStop = stop
                prevlength = PrevStop - PrevStart

            elif np.count_nonzero(Gene_AND) >= (length * OverLapPercentage) or np.count_nonzero(Gene_AND) >= OverLapNT:
                if length > prevlength:
                    GenePrev = GeneA

                    Both_Frames.popitem(last=False)
                    Both_Frames[key] = value
                    PrevStart = start
                    PrevStop = stop
                    prevlength = PrevStop - PrevStart

1 Ответ

1 голос
/ 20 сентября 2019

У меня может быть замысловатое решение для вас:

Сначала я конвертирую ваши диапазоны в list из tuples из int:

import pandas as pd


r = ["12-48", "40-80", "60-105", "110-130", "75-400"]
r = [tuple(map(int, z.split("-"))) for z in r]

# [(12, 48), (40, 80), (60, 105), (110, 130), (75, 400)]

Затем яПереберите все диапазоны и удалите все, которые полностью инкапсулированы другим диапазоном.Например: (110, 130) находится в пределах (75, 400):

hold = []
for idx1 in range(len(r)):
    start_1, stop_1 = r[idx1]
    for idx2, (start_2, stop_2) in enumerate(r):
        if idx1 == idx2:
            continue
        if start_2 < start_1 and stop_1 < stop_2:
            hold.append(idx1)

while hold:
    del r[hold.pop()]

# [(12, 48), (40, 80), (60, 105), (75, 400)]

Наконец, используя pandas.DataFrame, я вычисляю перекрытие и процентное перекрытие;пометьте строки, которые соответствуют критериям исключения (перекрытие> 10 и%> 0,2).Эти строки затем удаляются в обратном порядке, и перекрытия проверяются снова после каждого удаления до тех пор, пока не удастся удалить больше строк.

Затем DataFrame преобразуется обратно в список строк в том же формате, в котором они были предоставлены.дюймы

df = pd.DataFrame(r, columns=["start", "stop"]).sort_values("start")

df["length"] = df["stop"] - df["start"]
df["bool_1"], df["bool_2"] = True, True

while any(df["bool_1"].eq(True) & df["bool_2"].eq(True)):
    df["overlap"] = df["stop"] - df["start"].shift(-1)
    df["pc"] = df["overlap"] / df["length"]

    df["bool_1"] = df["overlap"] > 10
    df["bool_2"] = df["pc"] > 0.2
    for i, row in df.sort_index(ascending=False).iterrows():
        if row["bool_1"] == row["bool_2"] and row["bool_1"] is not False:
            df.drop(i, inplace=True)
            break

result = df["start"].astype("str").str.cat(df["stop"].astype("str"), sep="-").to_list()

# ['12-48', '40-80', '75-400']
...