Source code for gsmodutils.model_diff

from __future__ import print_function
from cobra import Model
from gsmodutils.utils import check_obj_sim, convert_stoich, equal_stoich


[docs]class ModelDiff(dict): def __init__(self, model_a=None, model_b=None, *args, **kwargs): """ A subclass of dict that has a nice, friendly html representation for use with jupyter :param cobra.Model model_a: left model (base model) :param cobra.Model model_b: right model (new model) """ self.model_a = model_a self.model_b = model_b self.update(*args, **kwargs) required_attr = ["metabolites", "reactions", "genes", "removed_reactions", "removed_metabolites", "removed_genes"] for k in required_attr: self.setdefault(k, []) if self.model_b is not None and self.model_a is not None: self._diff_models(self.model_a, self.model_b) super(ModelDiff, self).__init__(*args, **kwargs) def _table_header(self): title = "" if self.model_b is not None and self.model_a is not None: title = "Model diff between {} and {}".format(self.model_a.id, self.model_b.id) info_str = """ <b> {} </b> <table> <tr> <td> Removed reactions: </td> <td> {} </td> </tr> <tr> <td> Removed metabolites: </td> <td> {} </td> </tr> <tr> <td> Removed genes: </td> <td> {} </td> </tr> <tr> <td> Added or removed reactions: </td> <td> {} </td> </tr> <tr> <td> Added or removed metabolites: </td> <td> {} </td> </tr> <tr> <td> Added or removed genes: </td> <td> {} </td> </tr> """.format( title, len(self["removed_reactions"]), len(self["removed_metabolites"]), len(self["removed_genes"]), len(self["reactions"]), len(self["metabolites"]), len(self["genes"]), ) return info_str def _reaction_table(self): info_str = "" if len(self["reactions"]): info_str += "<strong> Added/Changed reactions: </strong>" for reaction in self["reactions"]: info_str += """ <table> <th> Reaction id</th> <th>name</th> <th>gene_reaction_rule</th> <th>subsystem</th> <th>upper_bound</th> <th>lower_bound</th> <th>objective_coefficient</th> <tr> <td>{id}</td> <td>{name}</td> <td>{gene_reaction_rule}</td> <td>{subsystem}</td> <td>{upper_bound}</td> <td>{lower_bound}</td> <td>{objective_coefficient}</td> </tr> <tr> <td colspan=7 > {rstr} </td> </tr> <tr> <td colspan=7> {rstr_names} </td> </tr> """.format(**reaction) info_str += "</table>" return info_str def _metabolite_table(self): """ :return: """ info_str = "" if len(self["metabolites"]): info_str += """ <strong> Added/Changed Metabolites </strong> <table> <th> Metabolite id </th> <th> name </th> <th> compartment </th> <th> charge </th> <th> notes </th> <th> annotation </th> """ for mb in self["metabolites"]: info_str += """ <tr> <td>{id}</td> <td>{name}</td> <td>{compartment}</td> <td>{charge}</td> <td>{notes}</td> <td>{annotation}</td> </tr> """.format(**mb) info_str += "</table>" return info_str def _genes_table(self): """ :return: """ info_str = "" if len(self["genes"]): info_str += """ <strong> Added/Changed Metabolites </strong> <table> <th> Gene id </th> <th> Gene name </th> <th> notes </th> <th> functional </th> <th> annotation </th> """ for gene in self["genes"]: info_str += """ <tr> <td>{id}</td> <td>{name}</td> <td>{notes}</td> <td>{functional}</td> <td>{annotation}</td> </tr> """.format(**gene) info_str += "</table>" return info_str def _repr_html_(self): """Method intended for jupyter notebooks""" info_str = self._table_header() def _rem_tpl(attr, title): istr = "" if len(self.get(attr)): istr = """ <table> <th> {0} </th> """.format(title) for remid in self.get(attr): istr += """ <tr> <td> {} </td> </tr> """.format(remid) istr += "</table>" return istr info_str += _rem_tpl("removed_metabolites", "Removed Metabolite") info_str += "<br />" info_str += _rem_tpl("removed_reactions", "Removed Reactions") info_str += "<br />" info_str += _rem_tpl("removed_genes", "Removed genes") info_str += "<br />" info_str += self._reaction_table() info_str += "<br />" info_str += self._metabolite_table() info_str += "<br />" info_str += self._genes_table() info_str += "<br />" return info_str
[docs] @staticmethod def model_diff(model_a, model_b): """ Returns a dictionary that contains all of the changed reactions between model a and model b This includes any reactions or metabolites removed, or any reactions or metabolites added/changed This does not say HOW a model has changed if reactions or metabolites are changed they are just included with their new values Diff assumes l -> r (i.e. model_a is the base model) :param cobra.Model model_a: :param cobra.Model model_b: :return: """ if not (isinstance(model_a, Model) and isinstance(model_b, Model)): raise TypeError('Can only compare cobra models') return ModelDiff(model_a, model_b)
def _diff_models(self, model_a, model_b): """ :param cobra.Model model_a: :param cobra.Model model_b: :return: """ metfields = ['formula', 'charge', 'compartment', 'name'] for ma in model_a.metabolites: # Find removed metabolites try: model_b.metabolites.get_by_id(ma.id) except KeyError: self['removed_metabolites'].append(ma.id) def parse_compartment(compartment): if compartment == "": return None return compartment for mb in model_b.metabolites: # find added metabolites # find if metabolite has changed at all try: ma = model_a.metabolites.get_by_id(mb.id) ma.compartment = parse_compartment(ma.compartment) except KeyError: ma = None if ma is None or not check_obj_sim(ma, mb, metfields): self['metabolites'].append( dict( id=mb.id, notes=mb.notes, compartment=parse_compartment(mb.compartment), formula=mb.formula, name=mb.name, charge=mb.charge, annotation=mb.annotation, ) ) reacfields = [ 'lower_bound', 'upper_bound', 'gene_reaction_rule', 'subsystem', 'name', 'variable_kind', ] for ra in model_a.reactions: # reaction has been removed try: model_b.reactions.get_by_id(ra.id) except KeyError: self['removed_reactions'].append(ra.id) for rb in model_b.reactions: # reaction is new try: ra = model_a.reactions.get_by_id(rb.id) except KeyError: ra = None # reaction has changed or is new if ra is None or not check_obj_sim(ra, rb, reacfields) or not equal_stoich(ra, rb): self['reactions'].append( dict( id=rb.id, lower_bound=rb.lower_bound, upper_bound=rb.upper_bound, gene_reaction_rule=rb.gene_reaction_rule, subsystem=rb.subsystem, objective_coefficient=rb.objective_coefficient, name=rb.name, variable_kind=rb.variable_kind, metabolites=dict(convert_stoich(rb.metabolites)), rstr=rb.build_reaction_string(use_metabolite_names=False), rstr_names=rb.build_reaction_string(use_metabolite_names=True) ) ) # Gene reaction rules are stored in reactions, however models also contains metadata for genes genefields = ["name", "annotation", "notes"] for ga in model_a.genes: try: model_b.genes.get_by_id(ga.id) except KeyError: self['removed_genes'].append(ga.id) for gb in model_b.genes: try: ga = model_a.genes.get_by_id(gb.id) except KeyError: ga = None # reaction has changed or is new if ga is None or not check_obj_sim(ga, gb, genefields): self['genes'].append( dict( id=gb.id, name=gb.name, annotation=gb.annotation, notes=gb.notes, functional=gb.functional, ) )
[docs]def model_diff(model_a, model_b): """ @depricated - use ModelDiff.model_diff(model_a, model_b) Returns a ModelDiff dictionary that contains all of the changed reactions between model a and model b This includes any reactions or metabolites removed, or any reactions or metabolites added/changed This does not say HOW a model has changed if reactions or metabolites are changed they are just included with their new values Diff assumes l -> r (i.e. model_a is the base model) """ return ModelDiff.model_diff(model_a, model_b)