Skip to content

Commit

Permalink
Merge pull request #855 from opencobra/refactor/summary
Browse files Browse the repository at this point in the history
refactor: introduce Summary class
  • Loading branch information
Midnighter committed Aug 4, 2019
2 parents d334e35 + a67182e commit 4f3eeef
Show file tree
Hide file tree
Showing 18 changed files with 1,222 additions and 597 deletions.
1 change: 1 addition & 0 deletions cobra/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from cobra.core.group import Group
from cobra.core.solution import Solution, LegacySolution, get_solution
from cobra.core.species import Species
from cobra.core.summary import MetaboliteSummary, ReactionSummary, Summary
63 changes: 44 additions & 19 deletions cobra/core/metabolite.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

"""Define the Metabolite class."""

from __future__ import absolute_import

import re
Expand Down Expand Up @@ -206,40 +208,63 @@ def remove_from_model(self, destructive=False):
"""
self._model.remove_metabolites(self, destructive)

def summary(self, solution=None, threshold=0.01, fva=None, names=False,
floatfmt='.3g'):
def summary(
self,
solution=None,
threshold=0.01,
fva=None,
names=False,
float_format="{:.3g}".format
):
"""
Print a summary of the production and consumption fluxes.
Create a summary of the producing and consuming fluxes.
This method requires the model for which this metabolite is a part
to be solved.
Parameters
----------
solution : cobra.Solution, optional
A previously solved model solution to use for generating the
summary. If none provided (default), the summary method will
resolve the model. Note that the solution object must match the
model, i.e., changes to the model such as changed bounds,
added or removed reactions are not taken into account by this
method.
A previous model solution to use for generating the summary. If
None, the summary method will resolve the model. Note that the
solution object must match the model, i.e., changes to the model
such as changed bounds, added or removed reactions are not taken
into account by this method (default None).
threshold : float, optional
Threshold below which fluxes are not reported.
fva : pandas.DataFrame, float or None, optional
Threshold below which fluxes are not reported. May not be smaller
than the model tolerance (default 0.01).
fva : pandas.DataFrame or float, optional
Whether or not to include flux variability analysis in the output.
If given, fva should either be a previous FVA solution matching
the model or a float between 0 and 1 representing the
fraction of the optimum objective to be searched.
If given, fva should either be a previous FVA solution matching the
model or a float between 0 and 1 representing the fraction of the
optimum objective to be searched (default None).
names : bool, optional
Emit reaction and metabolite names rather than identifiers (default
False).
floatfmt : string, optional
Format string for floats (default '.3g').
float_format : callable, optional
Format string for floats (default ``'{:3G}'.format``).
Returns
-------
cobra.MetaboliteSummary
See Also
--------
Reaction.summary
Model.summary
"""
from cobra.flux_analysis.summary import metabolite_summary
return metabolite_summary(self, solution=solution, threshold=threshold,
fva=fva, names=names, floatfmt=floatfmt)
from cobra.core.summary import MetaboliteSummary

return MetaboliteSummary(
metabolite=self,
model=self._model,
solution=solution,
threshold=threshold,
fva=fva,
names=names,
float_format=float_format
)

def _repr_html_(self):
return """
Expand Down
90 changes: 57 additions & 33 deletions cobra/core/model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

"""Define the Model class."""

from __future__ import absolute_import

import logging
Expand Down Expand Up @@ -31,8 +33,8 @@
from cobra.util.util import AutoVivification, format_long_string


LOGGER = logging.getLogger(__name__)
CONFIGURATION = Configuration()
logger = logging.getLogger(__name__)
configuration = Configuration()


class Model(Object):
Expand Down Expand Up @@ -111,13 +113,13 @@ def __init__(self, id_or_model=None, name=None):
# if not hasattr(self, '_solver'): # backwards compatibility
# with older cobrapy pickles?

interface = CONFIGURATION.solver
interface = configuration.solver
self._solver = interface.Model()
self._solver.objective = interface.Objective(Zero)
self._populate_solver(self.reactions, self.metabolites)

self._tolerance = None
self.tolerance = CONFIGURATION.tolerance
self.tolerance = configuration.tolerance

@property
def solver(self):
Expand Down Expand Up @@ -173,19 +175,19 @@ def tolerance(self, value):
try:
solver_tolerances.feasibility = value
except AttributeError:
LOGGER.info("The current solver doesn't allow setting"
logger.info("The current solver doesn't allow setting"
"feasibility tolerance.")

try:
solver_tolerances.optimality = value
except AttributeError:
LOGGER.info("The current solver doesn't allow setting"
logger.info("The current solver doesn't allow setting"
"optimality tolerance.")

try:
solver_tolerances.integrality = value
except AttributeError:
LOGGER.info("The current solver doesn't allow setting"
logger.info("The current solver doesn't allow setting"
"integrality tolerance.")

self._tolerance = value
Expand Down Expand Up @@ -282,7 +284,7 @@ def set_active_bound(reaction, bound):
for rxn_id, bound in iteritems(medium):
rxn = self.reactions.get_by_id(rxn_id)
if rxn not in exchange_rxns:
LOGGER.warn("%s does not seem to be an"
logger.warn("%s does not seem to be an"
" an exchange reaction. Applying bounds anyway.",
rxn.id)
media_rxns.append(rxn)
Expand Down Expand Up @@ -586,8 +588,8 @@ def add_boundary(self, metabolite, type="exchange", reaction_id=None,
'atp_c --> '
"""
ub = CONFIGURATION.upper_bound if ub is None else ub
lb = CONFIGURATION.lower_bound if lb is None else lb
ub = configuration.upper_bound if ub is None else ub
lb = configuration.lower_bound if lb is None else lb
types = {
"exchange": ("EX", lb, ub, sbo_terms["exchange"]),
"demand": ("DM", 0, ub, sbo_terms["demand"]),
Expand Down Expand Up @@ -639,7 +641,7 @@ def add_reactions(self, reaction_list):
"""
def existing_filter(rxn):
if rxn.id in self.reactions:
LOGGER.warning(
logger.warning(
"Ignoring reaction '%s' since it already exists.", rxn.id)
return False
return True
Expand Down Expand Up @@ -787,7 +789,7 @@ def add_groups(self, group_list):

def existing_filter(group):
if group.id in self.groups:
LOGGER.warning(
logger.warning(
"Ignoring group '%s' since it already exists.", group.id)
return False
return True
Expand Down Expand Up @@ -837,7 +839,7 @@ def remove_groups(self, group_list):
for group in group_list:
# make sure the group is in the model
if group.id not in self.groups:
LOGGER.warning("%r not in %r. Ignored.", group, self)
logger.warning("%r not in %r. Ignored.", group, self)
else:
self.groups.remove(group)
group._model = None
Expand Down Expand Up @@ -1169,37 +1171,59 @@ def objective_direction(self, value):
else:
raise ValueError("Unknown objective direction '{}'.".format(value))

def summary(self, solution=None, threshold=1E-06, fva=None, names=False,
floatfmt='.3g'):
def summary(
self,
solution=None,
threshold=0.01,
fva=None,
names=False,
float_format="{:.3g}".format
):
"""
Print a summary of the input and output fluxes of the model.
Create a summary of the exchange fluxes of the model.
Parameters
----------
solution: cobra.Solution, optional
A previously solved model solution to use for generating the
summary. If none provided (default), the summary method will
resolve the model. Note that the solution object must match the
model, i.e., changes to the model such as changed bounds,
added or removed reactions are not taken into account by this
method.
solution : cobra.Solution, optional
A previous model solution to use for generating the summary. If
None, the summary method will resolve the model. Note that the
solution object must match the model, i.e., changes to the model
such as changed bounds, added or removed reactions are not taken
into account by this method (default None).
threshold : float, optional
Threshold below which fluxes are not reported.
fva : pandas.DataFrame, float or None, optional
Threshold below which fluxes are not reported. May not be smaller
than the model tolerance (default 0.01).
fva : pandas.DataFrame or float, optional
Whether or not to include flux variability analysis in the output.
If given, fva should either be a previous FVA solution matching
the model or a float between 0 and 1 representing the
fraction of the optimum objective to be searched.
If given, fva should either be a previous FVA solution matching the
model or a float between 0 and 1 representing the fraction of the
optimum objective to be searched (default None).
names : bool, optional
Emit reaction and metabolite names rather than identifiers (default
False).
floatfmt : string, optional
Format string for floats (default '.3g').
float_format : callable, optional
Format string for floats (default ``'{:3G}'.format``).
Returns
-------
cobra.ModelSummary
See Also
--------
Reaction.summary
Metabolite.summary
"""
from cobra.flux_analysis.summary import model_summary
return model_summary(self, solution=solution, threshold=threshold,
fva=fva, names=names, floatfmt=floatfmt)
from cobra.core.summary import ModelSummary

return ModelSummary(
model=self,
solution=solution,
threshold=threshold,
fva=fva,
names=names,
float_format=float_format
)

def __enter__(self):
"""Record all future changes to the model, undoing them when a call to
Expand Down
57 changes: 57 additions & 0 deletions cobra/core/reaction.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

"""Define the Reaction class."""

from __future__ import absolute_import, print_function

import hashlib
Expand Down Expand Up @@ -1092,6 +1094,61 @@ def build_reaction_from_string(self, reaction_str, verbose=True,
met = Metabolite(met_id)
self.add_metabolites({met: num})

def summary(
self,
solution=None,
threshold=0.01,
fva=None,
names=False,
float_format="{:.3g}".format
):
"""
Create a summary of the producing and consuming fluxes of the reaction.
Parameters
----------
solution : cobra.Solution, optional
A previous model solution to use for generating the summary. If
None, the summary method will resolve the model. Note that the
solution object must match the model, i.e., changes to the model
such as changed bounds, added or removed reactions are not taken
into account by this method (default None).
threshold : float, optional
Threshold below which fluxes are not reported. May not be smaller
than the model tolerance (default 0.01).
fva : pandas.DataFrame or float, optional
Whether or not to include flux variability analysis in the output.
If given, fva should either be a previous FVA solution matching the
model or a float between 0 and 1 representing the fraction of the
optimum objective to be searched (default None).
names : bool, optional
Emit reaction and metabolite names rather than identifiers (default
False).
float_format : callable, optional
Format string for floats (default ``'{:3G}'.format``).
Returns
-------
cobra.ReactionSummary
See Also
--------
Metabolite.summary
Model.summary
"""
from cobra.core.summary import ReactionSummary

return ReactionSummary(
reaction=self,
model=self._model,
solution=solution,
threshold=threshold,
fva=fva,
names=names,
float_format=float_format
)

def __str__(self):
return "{id}: {stoichiometry}".format(
id=self.id, stoichiometry=self.build_reaction_string())
Expand Down
8 changes: 8 additions & 0 deletions cobra/core/summary/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-

from __future__ import absolute_import

from cobra.core.summary.summary import Summary
from cobra.core.summary.metabolite_summary import MetaboliteSummary
from cobra.core.summary.model_summary import ModelSummary
from cobra.core.summary.reaction_summary import ReactionSummary

0 comments on commit 4f3eeef

Please sign in to comment.