<jupyter_start><jupyter_text>IntroductionNow that all the reactions have proper names, we can add all the needed annotations to the reactions to make further searching easier, and from there start fixing stochiometric problems. Right now, each reaction has no annotation at all. For some of them we can lift it from the notes.For the reactions, we would want to have KEGG IDs, MetanetX IDs and E.C. numbers where ever possible.<jupyter_code>import cobra
import pandas as pd
import cameo
model = cobra.io.read_sbml_model('../model/p-thermo.xml')
matteo_model = cobra.io.read_sbml_model("../databases/g-thermo-Matteo.xml")<jupyter_output><empty_output><jupyter_text>KEGG IDsFirst we will add KEGG IDs. This can be done by lifting the Kegg reaction information from the reaction name or reaction notes.<jupyter_code>#first try to add the KEGG reaction IDs
unannotated_rct_kegg = []
for rct in model.reactions:
    try:
        rct.annotation["kegg.reaction"] = rct.notes["KEGG ID"]
    except:
        unannotated_rct_kegg.append(rct)
len(unannotated_rct_kegg)
#the majority of the unannotated reactions are exchange and transport, as expected. 
cobra.io.write_sbml_model(model,"../model/p-thermo.xml")<jupyter_output><empty_output><jupyter_text>MetaNetX IDsNow I will try to annotate each compound with MetaNetX IDs, where possible, based on the KEGG ID's found.<jupyter_code>#load metaNetX dataframe
rct_df = pd.read_csv("../../databases/reac_xref.tsv", sep="\t", skiprows=385)
#NOTE: an extra colum head was added to the file on line 386 called 'note' to fix the headings being aligned further
rct_df
unannotated_rct_meta = []
for rct in model.reactions:
    try:
        rct_id = "kegg:"+ rct.notes["KEGG ID"]
    except KeyError:
        unannotated_rct_meta.append(rct)
        continue
    #find metanetX ID for this compound
    try:
        rct_new_ann = rct_df.loc[rct_df["#XREF"] == rct_id,"MNX_ID"].values[0]
    except IndexError:
        unannotated_rct_meta.append(rct)
        continue
    rct.annotation["metanetx.reaction"] = rct_new_ann

len(unannotated_rct_meta)
cobra.io.write_sbml_model(model,"../model/p-thermo.xml")<jupyter_output><empty_output><jupyter_text>E.C. numbersFirst we will try to add E.C. numbers based on the E.C. numbers present in the iML1515 model. To do so, we first make a dictionary of these values and then use this to match our reactions to it.As a second attempt, we will annotate all reactions with a Rhea database annotation, based on the reaction KEGG ID. Then from here, for all metabolites without an E.C. number from the iML1515 model, we will add the E.C. annotation, on the basis of the Rhea annotation<jupyter_code>#load the iML1515 model
model_e_coli = cameo.load_model("iML1515")
#making the e. coli iML1515 based dictionary of EC numbers based on BiGG ID
EC_dict = {reaction.id : reaction.annotation["ec-code"] for reaction in model_e_coli.reactions if "ec-code" in reaction.annotation}
len(EC_dict)
#copy E.C. numbers from the E. coli model on the basis of the BIGG IDs.
no_ec_rct = []
for reaction in model.reactions:
    try:
        reaction.id in EC_dict
        reaction.annotation["ec-code"] = EC_dict[reaction.id]
    except:
        no_ec_rct.append(reaction)
len(no_ec_rct)<jupyter_output><empty_output><jupyter_text>So not many reactions have an E.C. number attached to them in this way. For the (non-exchange or -transport) reactions we can try to add them by using the Rhea database. For all reactions we first add a rhea number, based on the kegg reaction ID the reaction has.Then we can couple this to an E.C. number for the reactions without the E.C. already.Files used for this section can be found here: https://www.rhea-db.org/download<jupyter_code>#load the data base that converts the kegg ID to rhea ID
rhea2kegg_df = pd.read_csv("../../databases/rhea2kegg_reaction.tsv", sep="\t")
rhea2kegg_df
#add rhea annotation to all reactions
unannotated_rct_rhea = []
for reaction in model.reactions:
    try:
        rct_kegg = reaction.annotation["kegg.reaction"]
    except KeyError:
        unannotated_rct_rhea.append(reaction)
        continue
    #find rhea ID for this compound
    try:
        rhea_ann = rhea2kegg_df.loc[rhea2kegg_df["ID"]== rct_kegg,"MASTER_ID"].values[0]
    except IndexError:
        unannotated_rct_rhea.append(reaction)
        continue
    reaction.annotation["rhea"] = str(rhea_ann)

len(unannotated_rct_rhea)
#quite a lot of unannotated, the large majority of these are the Exchange and transport reactions, as they have no Kegg ID.<jupyter_output><empty_output><jupyter_text>now with the rhea annotation added, for the reactions in no_ec_rct, add the corresponding EC number from the Rhea2EC database provided on their website.<jupyter_code>#load the dataframe
rhea2ec_df = pd.read_csv("../../databases/rhea2ec.tsv", sep="\t")
rhea2ec_df
#add the E.C. numbers based on the rhea IDs given
unannotated_rct_ec = []
for reaction in model.reactions:
    if reaction in no_ec_rct:
        try:
            rct_rhea = reaction.annotation["rhea"]
        except KeyError:
            unannotated_rct_ec.append(reaction)
            continue
        try:
            ec_ann_1 = rhea2ec_df.loc[rhea2ec_df["MASTER_ID"]== int(rct_rhea),"ID"].values[0]
        except IndexError:
            unannotated_rct_ec.append(reaction)
            continue
        try:
            ec_ann_2 =  rhea2ec_df.loc[rhea2ec_df["MASTER_ID"]== int(rct_rhea),"ID"].values[1]
        except IndexError:
            reaction.annotation["ec-code"] = [ec_ann_1]
            continue
        try: 
            reaction.annotation["ec-code"] = str([ec_ann_1, ec_ann_2])
        except IndexError:
            continue
    else: 
        continue

len(unannotated_rct_ec)<jupyter_output><empty_output><jupyter_text>So we have now annotated a bit more than half of the reactions with an E.C. number. Not everything is completely annotated, but most reactions will have some annotation and otherwise additional information in notes that allows one to identify the reaction. Therefore I will not manually go through each and make sure they have correct Kegg, metaNetX and E.C. numbers. (also because for transport and exchange there are no numbers allocated, so there will always be a lrge fraction of unannotated numbers.<jupyter_code>cobra.io.write_sbml_model(model,"../model/p-thermo.xml")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0848731600375822, 'fraction_numerical': 0.007516442217350454, 'mean_word_length': 3.943498452012384, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 5}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '444260', 'license': 'Apache-2.0', 'n_tokens_mistral': 2150, 'n_tokens_neox': 2041, 'n_words': 666, 'path': 'notebooks/5. Giving each reaction proper annotations.ipynb', 'repo_name': 'biosustain/p-thermo', 'size': '6386'}