用0填充没有覆盖的箱子

4 投票
1 回答
60 浏览
提问于 2025-04-14 16:20

我需要生成一个热图,显示每个区间内的平均覆盖情况。这个区间是根据我设定的数量来划分的,而不管每个区间内的转录组有多少个碱基。换句话说,如果我想要10个区间,一个转录组可能有1000个碱基要分配到这10个区间,而另一个转录组可能有2445个碱基也要分配到这10个区间。

问题是,在我的覆盖文件中,有些地方没有落入任何一个区间。比如说,如果我想在10个位置上划分成5个区间,我会得到:(0,2], (2,4], (4,6], (6,8], (8,10]。如果我的覆盖位置是1, 5, 5, 5, 7, 7, 10,那么区间“(2,4]”就会被忽略,因此在热图中不会出现。我希望这些没有覆盖的区间能用0填充,这样它们就能在热图中显示出来。

我正在使用Python,配合pandas、seaborn和matplotlib.pyplot库。

下面的图片中,第一行是我划分的区间边界,数据框显示了哪些区间有覆盖:

点击这里查看图片

输入示例:

chr17   1   1
chr17   5   1
chr17   5   2
chr17   5   2
chr17   7   1
chr17   7   5
chr17   10  1

问题:

    chr                data_bin        avg
  chr17                   (0,2]          1
  chr17                   (4,6]       1.66
  chr17                   (4,6]       1.66
  chr17                   (4,6]       1.66
  chr17                   (6,8]          3
  chr17                   (6,8]          3
  chr17                  (8,10]          1

期望结果:

    chr                data_bin        avg
  chr17                   (0,2]          1
  **chr17                   (2,4]          0**
  chr17                   (4,6]       1.66
  chr17                   (4,6]       1.66
  chr17                   (4,6]       1.66
  chr17                   (6,8]          3
  chr17                   (6,8]          3
  chr17                  (8,10]          1

我使用的函数是:

def bins_calculator(path_txt:str, start:int,end:int):
    column_names =["chr", "pos", "cov"]
    data = pd.read_csv(path_txt, names = column_names, sep = '\t')
    step = int((end - start) / 10)
    n_bins = [start + i * step for i in range(11)]
    n_bins[-1] = end
    data["data_bin"] = pd.cut(data["pos"], bins = n_bins)
    data["avg"] = data.groupby("data_bin", observed = False)["cov"].transform("mean")
    filtered_data = data[["chr","data_bin","avg"]].drop_duplicates("data_bin")
    return filtered_data

如果对这个问题有任何疑问,请在评论中告诉我 :)

1 个回答

1

如果我理解得没错,你可以使用 .merge 来合并缺失的类别,然后用你想要的值来填充任何 NaNs(缺失值):

df["data_bin"] = pd.cut(df["pos"], range(0, 12, 2))

df = pd.merge(
    df,
    df["data_bin"].cat.categories.to_frame(),
    left_on="data_bin",
    right_on=0,
    how="outer",
)[["chr", "data_bin", "cov"]]

df["chr"] = df["chr"].ffill().bfill()
df["cov"] = df["cov"].fillna(0)

df["avg"] = df.groupby("data_bin")["cov"].transform("mean")
print(df)

输出结果:

     chr     data_bin  cov       avg
0  chr17   (0.0, 2.0]  1.0  1.000000
1  chr17   (2.0, 4.0]  0.0  0.000000
2  chr17   (4.0, 6.0]  1.0  1.666667
3  chr17   (4.0, 6.0]  2.0  1.666667
4  chr17   (4.0, 6.0]  2.0  1.666667
5  chr17   (6.0, 8.0]  1.0  3.000000
6  chr17   (6.0, 8.0]  5.0  3.000000
7  chr17  (8.0, 10.0]  1.0  1.000000

撰写回答