我正在使用 pandas 框架读取两个文本文件,每个文件有 600K 行,并尝试找到两个文件之间具有匹配值的行。我目前的实施速度非常慢,我想知道是否有办法使用 numpy 来加快速度?
genes1 = pd.read_csv("gen1.txt", sep=" ", names=["id", "chromosome", "genotype"])
genes1 = genes1[~genes1.chromosome.isin(['X', 'Y'])] # filter out unwanted data
genes2 = pd.read_csv("gen2.txt", sep=" ", names=["id", "chromosome", "genotype"])
genes2 = genes2[~genes2.chromosome.isin(['X', 'Y'])] # filter out unwanted data
for ind in genes1.index: # loop through each row of the dataframe
if genes1["id"][ind] in genes2["id"].values: # if the given id of genes1 exist in genes2
gen1_string = str(genes1["genotype"][ind])
gen2_string = str(genes2["genotype"][genes2.index[genes2["id"] == genes1["id"][ind]].tolist()[0]])
if gen1_string == gen2_string or gen1_string == "".join(reversed(gen2_string)):
match += 1
提前感谢您的帮助
我尝试将文件解析为文本 1 和文本 2 共享的列表,以及仅存在于文本 1 和文本 2 中的列表,使用:
# genes1_and_2 = genes1[genes1["rsid"] == genes2["rsid"]]
# genes1_only = genes1[genes1["rsid"] != genes2["rsid"]]
# genes2_only = genes2[genes1["rsid"] != genes2["rsid"]]
然而,这不是很有效,我相信它没有显示出太大的性能提升
这里是文本文件的例子,用“ ”分隔
rs12564807 1 AA
rs3131972 1 GG
rs148828841 1 CT
rs12124819 Y AG
rs115093905 X GG
Numpy 没有dataframe 类型,所以不能用来直接替代pandas。使用其他数据框实现可能会提高性能,我建议尝试 Polars。根据我的经验,它比 pandas 提速 2-5 倍。
但是,在您的情况下,我认为性能问题出在算法中。当
genes2["id"] == genes1["id"][ind]
在每次迭代中被评估时,它创建了一个系列,因此该算法实际上具有二次复杂度。
您可以尝试join
genes1
和genes2
“id”列上的表:
genes1.set_index("id").join(genes2.set_index("id"), lsuffix="_1", rsuffix="_2")
然后遍历生成的数据框。
完整示例:
import pandas as pd
genes1 = pd.read_csv("gen1.txt", sep=" ", names=["id", "chromosome", "genotype"])
genes1 = genes1[~genes1.chromosome.isin(['X', 'Y'])] # filter out unwanted data
genes2 = pd.read_csv("gen2.txt", sep=" ", names=["id", "chromosome", "genotype"])
genes2 = genes2[~genes2.chromosome.isin(['X', 'Y'])] # filter out unwanted data
genes = genes1.set_index("id").join(genes2.set_index("id"), lsuffix="_1", rsuffix="_2")
match = 0
for (id, row) in genes.iterrows(): # loop through each row of the dataframe
gen1_string = str(row["genotype_1"])
gen2_string = str(row["genotype_2"])
if gen1_string == gen2_string or gen1_string == "".join(reversed(gen2_string)):
match += 1
print(match)
我试图用 join 方法回答 tla 非常好的方法,但我的编辑被拒绝了。我认为您可以进一步改进该答案,避免使用 for 循环。
import pandas as pd
genes1 = pd.read_csv("gen1.txt", sep=" ", names=["id", "chromosome", "genotype"])
genes1 = genes1[~genes1.chromosome.isin(['X', 'Y'])] # filter out unwanted data
genes2 = pd.read_csv("gen2.txt", sep=" ", names=["id", "chromosome", "genotype"])
genes2 = genes2[~genes2.chromosome.isin(['X', 'Y'])] # filter out unwanted data
genes = genes1.set_index("id").join(genes2.set_index("id"), lsuffix="_1", rsuffix="_2")
# credit to tla for above
filtering = (
genes["genotype_1"] == genes["genotype_2"] or
genes["genotype_1"] == genes["genotype_2"].apply(lambda x: "".join(reversed(x)))
)
match = filtering.values.sum()
print(match)
遍历数据帧被认为很慢,所以我会应用一些过滤器。
filtering
包含一系列布尔值。匹配时为 True,不匹配时为 False。 filtering.values.sum()
只是计算 True 的数量。