Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1f150af

Browse files
committedJan 25, 2020
Add function for converting SBML-observable models to observable table
1 parent d7b9380 commit 1f150af

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed
 

‎petab/migrations.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""Functions for migrating PEtab files from different versions"""
2+
3+
import logging
4+
import pandas as pd
5+
6+
from . import Problem
7+
from . import get_notnull_columns
8+
from .C import *
9+
from .lint import lint_problem
10+
from .measurements import get_placeholders
11+
from .core import get_observable_id
12+
from .sbml import get_sigmas, get_observables
13+
14+
15+
def sbml_observables_to_table(problem: Problem):
16+
"""Transform PEtab files where observables are defined inside the SBML
17+
model to the newer format where they are specified in a separate table.
18+
19+
For details see https://github.com/ICB-DCM/PEtab/issues/241.
20+
21+
Modifies ``problem`` in place. Does not alter any files.
22+
23+
Assumes the input is adheres to the PEtab format. You may want to check
24+
that with ``petablint`` from PEtab 0.0.2.
25+
"""
26+
27+
logging.basicConfig(level=logging.DEBUG)
28+
29+
# Ensure all measurements for the same observable have the same
30+
# observableTransformation and noiseModel
31+
32+
measurement_df = problem.measurement_df
33+
grouping_cols = get_notnull_columns(
34+
measurement_df, [OBSERVABLE_ID, OBSERVABLE_TRANSFORMATION,
35+
NOISE_DISTRIBUTION])
36+
grouped = measurement_df.groupby(grouping_cols).count().reset_index()
37+
38+
if len(grouped[OBSERVABLE_ID]) != len(grouped[OBSERVABLE_ID]):
39+
raise ValueError("In order to create an observable table, all "
40+
"measurements for the same observable must have the "
41+
"same observableTransformation and noiseModel."
42+
"Multiple observableTransformation and noiseModel "
43+
"has to be handled by different observables.")
44+
45+
# get observables and sigmas from SBML file and directly remove them
46+
observables = get_observables(problem.sbml_model, remove=True)
47+
sigmas = get_sigmas(problem.sbml_model, remove=True)
48+
assert observables.keys() == sigmas.keys()
49+
50+
if not observables:
51+
raise RuntimeError("No observables in SBML model to convert. "
52+
"Has this model already been processed?")
53+
54+
# Create observable dataframe and add to `problem`
55+
56+
for obs_id, noise in sigmas.items():
57+
observables[obs_id][NOISE_FORMULA] = noise
58+
59+
# set observableTransformation and noiseModel
60+
for obs_id_long in observables.keys():
61+
obs_id = get_observable_id(obs_id_long)
62+
cur_mes_df = measurement_df[measurement_df[OBSERVABLE_ID] == obs_id]
63+
if not len(cur_mes_df):
64+
# observable defined, but no measurements
65+
continue
66+
67+
if OBSERVABLE_TRANSFORMATION in cur_mes_df:
68+
observables[obs_id_long][OBSERVABLE_TRANSFORMATION] = \
69+
cur_mes_df[OBSERVABLE_TRANSFORMATION].values[0]
70+
else:
71+
observables[obs_id_long][OBSERVABLE_TRANSFORMATION] = LIN
72+
73+
if NOISE_DISTRIBUTION in cur_mes_df:
74+
observables[obs_id_long][NOISE_DISTRIBUTION] = \
75+
cur_mes_df[NOISE_DISTRIBUTION].values[0]
76+
else:
77+
observables[obs_id_long][NOISE_DISTRIBUTION] = NORMAL
78+
79+
observable_df = pd.DataFrame(observables).transpose().reset_index()
80+
observable_df.rename(columns={"index": OBSERVABLE_ID,
81+
"name": OBSERVABLE_NAME,
82+
"formula": OBSERVABLE_FORMULA},
83+
errors="raise", inplace=True)
84+
observable_df[OBSERVABLE_ID] = observable_df[OBSERVABLE_ID].apply(
85+
get_observable_id)
86+
observable_df.set_index([OBSERVABLE_ID], inplace=True)
87+
problem.observable_df = observable_df
88+
89+
# remove observableParameters and noiseParameters from SBML file
90+
# noise and observable parameters and AssignmentRules have already been
91+
# removed
92+
sbml_model = problem.sbml_model
93+
placeholders = set()
94+
for k, v in observables.items():
95+
placeholders |= get_placeholders(
96+
v['formula'],
97+
get_observable_id(k),
98+
'observable')
99+
for k, v in sigmas.items():
100+
placeholders |= get_placeholders(
101+
v, get_observable_id(k), 'noise')
102+
for placeholder in placeholders:
103+
ret = sbml_model.removeParameter(placeholder)
104+
if not ret:
105+
raise RuntimeError("Unknown problem when trying to remove "
106+
f"placeholder parameter {placeholder}.")
107+
108+
# drop obsolete measurement columns
109+
if OBSERVABLE_TRANSFORMATION in measurement_df:
110+
measurement_df.drop(OBSERVABLE_TRANSFORMATION, axis=1, inplace=True)
111+
if NOISE_DISTRIBUTION in measurement_df:
112+
measurement_df.drop(NOISE_DISTRIBUTION, axis=1, inplace=True)
113+
114+
if lint_problem(problem):
115+
raise RuntimeError("Unknown error converting PEtab problem to "
116+
"observable table based format.")

0 commit comments

Comments
 (0)
Please sign in to comment.