我想要 DF1 与 DF2 重叠的精确间隔。另外,我想要不与 DF2 重叠的间隔。这很棘手,因为您必须包括 A) 完全不重叠的 DF1 行 B) 部分重叠的 DF1 行。
可以看到DF1和DF2的第一行有重叠——重叠间隔(以DF1而言)是10-20。非重叠间隔(就 DF1 而言)为 1-9、21-100。当您考虑下一行时,另一个棘手的部分就出现了。非重叠区间为 1-59,如果我们只看这一行,这是正确的。但您必须确保所有间隔(无论是否重叠)在染色体水平上不会重叠。如果您还记得上面的行有一个从 10-20 的重叠区间,它与 1-59 重叠(因此 1-59 不是真正的非重叠区间)。 DF1 上的第二行与 DF2 没有重叠,因此您可以将整个间隔算作不重叠。
因此,对于1号染色体:重叠间隔为:10-20、60-100。
不重叠的间隔为1-9、21-59、150-200。
如您所见,要查看是否获得正确输出的真正测试是,如果将所有间隔(重叠和非重叠)相加,它应该等于 DF1 中区域的长度。
示例(为简单起见,忽略 2 号染色体):
DF1:1-100 + 150-200= 151 个碱基长(包括开始/结束)
1-9 = 9
10-20 = 11
21-59 = 39
60-100 = 41
150-200 = 51
9+11+39+41+51= 151 个碱基(匹配 DF1)
DF1
chr start end
1 1 100
1 150 200
2 5 10
DF2
chr start end
1 10 20
1 60 260
1 500 550
2 1 20
IIUC你可以做这样的事情:
df1["range"] = df1.apply(lambda row: range(row["start"], row["end"] + 1), axis=1)
df1 = df1[["chr", "range"]].explode("range")
df2["range"] = df2.apply(lambda row: range(row["start"], row["end"] + 1), axis=1)
df2 = df2[["chr", "range"]].explode("range")
out = df1.merge(df2, on=["chr", "range"], how="outer", indicator=True)
out = out.groupby("chr").apply(
lambda g: g.groupby((g["_merge"] != g["_merge"].shift()).cumsum()).agg(
{"range": ["first", "last"], "_merge": "first"}
),
include_groups=False,
)
print(out)
打印:
range _merge
first last first
chr _merge
1 1 1 9 left_only
2 10 20 both
3 21 59 left_only
4 60 100 both
5 101 149 right_only
6 150 200 both
7 201 550 right_only
2 1 1 4 right_only
2 5 10 both
3 11 20 right_only
结果包含所有区间,区间的“类型”位于
_merge
列中。例如1-9
仅在 df1
中找到,10-20
在 df1
中找到, 在 df2
中找到,101-149
仅在 df2
等中找到。