如何将 pandas dataframe 转换为 numpy 以提高性能

问题描述 投票:0回答:2

我正在使用 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
pandas numpy performance string-comparison
2个回答
1
投票

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)

0
投票

我试图用 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 的数量。

© www.soinside.com 2019 - 2024. All rights reserved.