使用字典替换超过1M的pandas值

kmbjn2e3  于 2023-06-04  发布在  其他
关注(0)|答案(2)|浏览(197)

现在,我正在研究STRING网络
当我想将STRING蛋白替换为Entrez ID时,我看到STRING数据有超过1 M的pandas行。
因此,我尝试将STRING蛋白质ID(如9606.ENSP00000~~)替换为Entrez ID(整数)。
所有的方法都很慢。
但是当我使用这段代码时,所用的时间< 5分钟。

这个代码没有问题吗?

这是密码。”

def replace_string_id(self, data: pd.DataFrame, target_col: str, rename_dict: dict) -> pd.DataFrame:
    tmp_list = []
    for target, tmp_df in data.groupby(target_col):
        if target in rename_dict.keys():
            tmp_df[target_col] = rename_dict[target]
            tmp_list.append(tmp_df)
        else:
            continue
    # merging

    return_df = pd.concat(tmp_list)
    return return_df

string_link_df是这样的:

protein1    protein2    combined_score 
0   381 9606.ENSP00000379496    155
1   381 9606.ENSP00000314067    197
2   381 9606.ENSP00000263116    222
...

replace_dict是:

{'9606.ENSP00000263100': 1,
 '9606.ENSP00000323929': 2,
 '9606.ENSP00000443194': 9,
 '9606.ENSP00000286479': 10,
 '9606.ENSP00000376793': 12,
 '9606.ENSP00000450540': 12,
 '9606.ENSP00000232892': 13,
...
}

string_renamed = replace_string_id(string_link_df, 'protein2', replace_dict)


这段代码比其他方法更快。
--编辑2023-06-023 KST --感谢所有评论我的问题的人。
这是我的全部数据。

**字符串数据:**可在此处下载:https://string-db.org/cgi/download?sessionId=bNSFUedQrsqe&species_text=Homo+sapiens
**链接数据文件名:**9606.protein.links.v11.5.txt.gz
**信息数据文件名:**9606.protein.info.v11.5.txt.gz

你可以使用下面的代码将信息数据转换为dict:

import pandas as pd
from tqdm import tqdm
from pathlib import Path

def main(args):
    # read string alias data
    string_alias_df = pd.read_csv(args.string_alias_path, sep="\t", dtype={"alias": str}, compression="gzip")
    
    # set target source which have Entrez ID
    target_source_list = [
        "BLAST_KEGG_GENEID",
        "BLAST_UniProt_DR_GeneID",
        "Ensembl_HGNC_Entrez_Gene_ID",
        "Ensembl_HGNC_Entrez_Gene_ID(supplied_by_NCBI)",
        "Ensembl_HGNC_UniProt_ID(supplied_by_UniProt)_DR_GeneID",
        "Ensembl_UniProt_DR_GeneID"
    ]
    
    # make string alias to Entrez
    alias_df = []
    for string_id, string_id_df in tqdm(string_alias_df.groupby('#string_protein_id')):
        target_rows = string_id_df[string_id_df['source'].isin(target_source_list)]
        if len(target_rows):
            target_row = target_rows.iloc[0]
            tmp_series = pd.Series(
                data=[string_id, target_row['alias']],
                index=["STRING ID", "Entrez"]
            )
        else:
            tmp_series = pd.Series(
                data=[string_id, ""],
                index=["STRING ID", "Entrez"]
            )
        alias_df.append(tmp_series)

    alias_df = pd.DataFrame(alias_df)
    
    # Need to STRIP
    # alias_df['Entrez'] = alias_df['Entrez'].str.strip()
    
    # save
    alias_df.to_csv(
        args.string_alias_path.parent.joinpath("STRING_Entrez_mapping_table.tsv"), 
        sep="\t", 
        header=True, 
        index=False,
    )
    pass

if __name__ == "__main__":
    import argparse
    ref_dir = Path(__file__).parent.parent.joinpath("references", "STRING")
    parser = argparse.ArgumentParser()
    parser.add_argument("--string_alias_path", type=Path, default=ref_dir.joinpath("9606.protein.aliases.v11.5.txt.gz"))
    args = parser.parse_args()
    main(args)

原始String数据如:

protein1    protein2    combined_score
0   9606.ENSP00000000233    9606.ENSP00000379496    155
1   9606.ENSP00000000233    9606.ENSP00000314067    197
2   9606.ENSP00000000233    9606.ENSP00000263116    222
3   9606.ENSP00000000233    9606.ENSP00000361263    181
4   9606.ENSP00000000233    9606.ENSP00000409666    270

我需要所有这些列。
我做了两个函数。
一个是西蒙·大卫提出的:

def use_replace(string, target_col, replace_dict) -> None:
    data = string.copy()
    data = data.replace({target_col: replace_dict})
    pass

另一个是我建议的:

def replace_string_id(data: pd.DataFrame, target_col: str, rename_dict: dict) -> pd.DataFrame:
    tmp_list = []
    for target, tmp_df in data.groupby(target_col):
        if target in rename_dict.keys():
            tmp_df[target_col] = rename_dict[target]
            tmp_list.append(tmp_df)
        else:
            continue

    return_df = pd.concat(tmp_list)

我试图检查use_replace函数的执行时间,但它需要超过1分钟。
use_replace(string,“protein1”,replace_dict)
并且,replace_string_id函数的执行时间为:

%timeit replace_string_id(string, "protein1", replace_dict)
2.43 s ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

所以,我问了这个问题。为什么pandas replace比在超过100万行中使用字典k,v慢,并且想知道这段代码(replace_string_id)是否有其他问题。
谢谢你的关心。

tvmytwxo

tvmytwxo1#

如果我理解你的权利,你是在使用dict替换后,你可以:

df.replace({"protein2": d})

其中d是你的dict,例如:

d = {'9606.ENSP00000263100': 1, '9606.ENSP00000323929': 2, '9606.ENSP00000443194': 9, '9606.ENSP00000286479': 10, '9606.ENSP00000376793': 12, '9606.ENSP00000450540': 12, '9606.ENSP00000232892': 13, ... }
nwnhqdif

nwnhqdif2#

在您的示例中,.replace()的替代方案是将.map().fillna()结合使用,这比.replace()快一点。我在下面添加了一个代码示例,它使用两种方法对1200万行执行此操作。在这个简单的实验中,.map()方法比.replace()快3倍。**然而,根据此分析,这两种方法对于您的数据集来说都应该足够快。**如果此代码在您的数据集上运行得非常慢,我建议您要么“chunk”您的数据,要么在加载过程中忽略任何您不需要的列,如本pandas用户指南所述。

import pandas as pd
from time import time

# Part 1: Generate DataFrame
factor = 3_000_000

data = {
    "protein1": [381, 381, 381, 381] * factor,
    "protein2": [
        '9606.ENSP00000263100', '9606.ENSP00000323929',
        '9606.ENSP00000443194', '9606.NOTFOUND'
        ] * factor,
    "combined_score": [155, 197, 222, 259] * factor
}

df = pd.DataFrame(data)

mapping_dict = {'9606.ENSP00000263100': 1,
 '9606.ENSP00000323929': 2,
 '9606.ENSP00000443194': 9,
 '9606.ENSP00000286479': 10,
 '9606.ENSP00000376793': 12,
 '9606.ENSP00000450540': 12,
 '9606.ENSP00000232892': 13,
}

def use_replace(df, tar_col, mapping_dict):

    df = df.replace({tar_col: mapping_dict})

    return df

def use_map_fillna(df, tar_col, mapping_dict):

    df["_"] = df[tar_col].map(mapping_dict)
    df[tar_col] = df["_"].fillna(df[tar_col])
    df.drop(columns=["_"], inplace=True)

    return df

# Part 2: execute functions
# use_replace()
tic = time()
output_df = use_replace(df=df, tar_col='protein2', mapping_dict=mapping_dict)
toc = time()
print(f"seconds passed: {toc - tic}")
# seconds passed: 7.794971704483032
print(output_df.head(n=5).to_markdown(index=False))
# |   protein1 | protein2      |   combined_score |
# |-----------:|:--------------|-----------------:|
# |        381 | 1.0           |              155 |
# |        381 | 2.0           |              197 |
# |        381 | 9.0           |              222 |
# |        381 | 9606.NOTFOUND |              259 |
# |        381 | 1.0           |              155 |

# use_map_fillna()
tic = time()
output_df = use_map_fillna(df=df, tar_col='protein2', mapping_dict=mapping_dict)
toc = time()
print(f"seconds passed: {toc - tic}")
# seconds passed: 2.3541603088378906
print(output_df.head(n=5).to_markdown(index=False))
# |   protein1 | protein2      |   combined_score |
# |-----------:|:--------------|-----------------:|
# |        381 | 1.0           |              155 |
# |        381 | 2.0           |              197 |
# |        381 | 9.0           |              222 |
# |        381 | 9606.NOTFOUND |              259 |
# |        381 | 1.0           |              155 |

相关问题