
在生物信息学数据分析中,对DNA片段长度分布进行聚合和分析是常见的任务。原始实现方式往往涉及多层循环和数据框的反复转换,这不仅导致代码冗长、难以维护,而且在大规模数据集上表现出极低的效率。本教程将详细介绍如何利用Pandas库的矢量化能力,重构一个计算DNA片段纯度(captured/all)的聚合任务,从而实现更高效、更Pythonic的代码。
原始代码的目标是根据一系列length_cutoffs计算DNA片段的纯度。具体步骤包括:
原始实现中存在以下主要痛点:
这些问题使得代码难以阅读、维护,并且在面对数千万行的数据时,性能瓶颈尤为突出。
为了解决上述问题,我们将采用以下Pandas高级功能来重构代码:
首先,我们需要将连续的length值转换为离散的长度区间(bins),这些区间由length_cutoffs定义。pd.cut函数非常适合此任务,它可以根据预定义的边界将数据分配到不同的类别中。
import io
import pandas as pd
import numpy as np
# 示例数据
TESTDATA="""
length regions
1 all
49 all
200 all
20 captured
480 captured
2000 captured
"""
df = pd.read_csv(io.StringIO(TESTDATA), sep='\s+')
# 长度截止值
length_cutoffs = [10, 100, 1000]
# 使用pd.cut创建长度区间
# from_breaks用于定义区间边界,包括负无穷和正无穷以覆盖所有值
# closed="left" 表示区间左闭右开,即 [lower, upper)
# 为了匹配“大于等于cutoff”的逻辑,我们创建的是 [cutoff, next_cutoff) 这样的区间
# 注意:这里的bins是针对“大于等于某个值”进行聚合的,所以需要将cutoff作为区间的起始点
# 最终的bins将是 [-inf, 10), [10, 100), [100, 1000), [1000, inf)
# 这样,对于每个bin,我们可以计算其包含的长度总和,然后通过累加得到“大于等于cutoff”的总和
df["bins"] = pd.cut(
df["length"],
pd.IntervalIndex.from_breaks([-np.inf] + length_cutoffs + [np.inf], closed="left"),
right=False # 确保区间是左闭右开,即 [low, high)
)
print("带有分箱信息的DataFrame:")
print(df)解释:
接下来,我们将使用pivot_table对数据进行初步聚合,计算每个regions和bins组合下的length总和。这取代了原始代码中循环内部的groupby().sum()。
# 使用pivot_table进行聚合
# index=["regions", "bins"] 定义了行索引
# values="length" 指定要聚合的列
# aggfunc="sum" 指定聚合函数
out = df.pivot_table(index=["regions", "bins"], values="length", aggfunc="sum", fill_value=0)
print("\n初步聚合结果 (length总和):")
print(out)解释:
这是最关键的一步,用于计算每个区域中,长度大于等于某个截止值(即当前bin及其后续所有bin)的片段总长度占该区域总长度的比例。原始代码中的内层循环和手动除法在这里被groupby().transform()替代。
# 按'regions'分组,然后计算每个组内的'frc_tot_length'
g = out.groupby(level=0) # level=0 表示按第一个索引级别(regions)分组
# 使用transform和lambda函数计算“大于等于当前bin”的累积和
# 对于每个组x(即每个region),遍历其所有行(bins)
# x.iloc[i:].sum() 计算从当前行i到末尾所有bin的length总和
out["frc_tot_length"] = (
g["length"].transform(lambda x: [x.iloc[i:].sum() for i in range(len(x))])
) / g["length"].transform('sum') # 除以该region的总长度
print("\n带有frc_tot_length的聚合结果:")
print(out)解释:
最后,为了计算captured / all的纯度比率,我们可以将regions从行索引转换为列,然后直接进行矢量化除法。
# 将'regions'从行索引转换为列,方便进行跨区域计算
x = out.unstack(level=0)
# 计算纯度:captured的frc_tot_length / all的frc_tot_length
df_pur = pd.DataFrame({
'length_cutoff': [interval.left for interval in x.index if interval.left != -np.inf],
'purity': (x[("frc_tot_length", "captured")] / x[("frc_tot_length", "all")]).iloc[1:]
})
print("\n最终纯度DataFrame (df_pur):")
print(df_pur)解释:
import io
import pandas as pd
import numpy as np
# 示例数据
TESTDATA="""
length regions
1 all
49 all
200 all
20 captured
480 captured
2000 captured
"""
df = pd.read_csv(io.StringIO(TESTDATA), sep='\s+')
# 长度截止值
length_cutoffs = [10, 100, 1000]
# 1. 数据分箱
df["bins"] = pd.cut(
df["length"],
pd.IntervalIndex.from_breaks([-np.inf] + length_cutoffs + [np.inf], closed="left"),
right=False
)
# 2. 初步聚合
out = df.pivot_table(index=["regions", "bins"], values="length", aggfunc="sum", fill_value=0)
# 3. 计算分数 (frc_tot_length)
g = out.groupby(level=0)
out["frc_tot_length"] = (
g["length"].transform(lambda x: [x.iloc[i:].sum() for i in range(len(x))])
) / g["length"].transform('sum')
# 4. 计算纯度比率 (purity)
x = out.unstack(level=0)
df_pur = pd.DataFrame({
'length_cutoff': [interval.left for interval in x.index if interval.left != -np.inf],
'purity': (x[("frc_tot_length", "captured")] / x[("frc_tot_length", "all")]).iloc[1:]
})
print("\n--- 最终纯度结果 ---")
print(df_pur)
# 真实数据集的性能测试(可选)
# num_rows = int(1e7)
# df_large = pd.concat([
# pd.DataFrame({'length': np.random.choice(range(1, 201), size=num_rows, replace=True), 'regions': 'all'}),
# pd.DataFrame({'length': np.random.choice(range(20, 2001), size=num_rows, replace=True), 'regions': 'captured'}),
# ]).reset_index(drop=True)
# # 在这里应用上述优化后的代码块,并使用timeit进行性能测试
# # 例如:
# # import time
# # start_time = time.time()
# # # ... apply optimized code to df_large ...
# # end_time = time.time()
# # print(f"Optimized code took {end_time - start_time:.4f} seconds for {num_rows*2} rows.")通过本教程的优化,我们实现了:
在处理大规模数据时,优先考虑使用Pandas的矢量化操作而非Python循环。关键的Pandas函数如pd.cut、pivot_table、groupby().transform()和unstack是实现高效、简洁数据聚合的强大工具。理解它们的用法和底层原理,能够帮助开发者编写出更健壮、更高效的数据分析代码。
以上就是使用Pandas矢量化操作高效聚合DNA片段数据的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号