From 4b4fdfb72960bd6b2c73c86335dc48080e7cff29 Mon Sep 17 00:00:00 2001 From: Maren Philipps Date: Wed, 10 Apr 2024 17:39:04 +0200 Subject: [PATCH 1/5] fix Period.get_measurements --- petab_timecourse/timecourse.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/petab_timecourse/timecourse.py b/petab_timecourse/timecourse.py index 4a459e1..20d8525 100644 --- a/petab_timecourse/timecourse.py +++ b/petab_timecourse/timecourse.py @@ -84,14 +84,15 @@ def get_condition(self, petab_problem: petab.Problem) -> pd.Series: def get_measurements( self, petab_problem: petab.Problem, - t0: float, + t0: Union[int, float], + t_end: Union[int, float], include_end: bool = False, ) -> pd.Series: after_start = petab_problem.measurement_df[TIME] >= t0 before_end = ( - petab_problem.measurement_df[TIME] <= t0 + petab_problem.measurement_df[TIME] <= t_end if include_end else - petab_problem.measurement_df[TIME] < t0 + petab_problem.measurement_df[TIME] < t_end ) return petab_problem.measurement_df.loc[after_start & before_end] From bd1e4ccb7c875282c3fb044dfcaeb969f24edec6 Mon Sep 17 00:00:00 2001 From: Maren Philipps Date: Thu, 11 Apr 2024 16:19:19 +0200 Subject: [PATCH 2/5] create Periods with parameters --- petab_timecourse/problem.py | 12 ++++++--- petab_timecourse/timecourse.py | 46 +++++++++++++++++----------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/petab_timecourse/problem.py b/petab_timecourse/problem.py index 396f0d7..dd53db1 100644 --- a/petab_timecourse/problem.py +++ b/petab_timecourse/problem.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import Any, Dict, Iterable, List, Optional, TextIO, Tuple, Union -#import amici +# import amici import numpy as np import pandas as pd import petab @@ -41,7 +41,7 @@ class Problem(petab.Problem): def from_yaml(*args, **kwargs): problem = petab.Problem.from_yaml(*args, **kwargs) - timecourse_files = problem.extensions_config['timecourse']['timecourse_files'] + timecourse_files = problem.extensions_config["timecourse"]["timecourse_files"] if len(timecourse_files) > 1: raise ValueError("multiple timecourse files are not yet supported.") if len(timecourse_files) < 1: @@ -54,6 +54,7 @@ def from_yaml(*args, **kwargs): # TODO remove when integrated into main PEtab library from functools import partial + problem.get_timecourse = partial(get_timecourse, self=problem) return problem @@ -62,8 +63,10 @@ def get_timecourse(self, timecourse_id: str) -> Timecourse: return Timecourse.from_df( timecourse_df=self.timecourse_df, timecourse_id=timecourse_id, + condition_df=self.condition_df, ) + def to_single_petab_with_events(problem: Problem): # create an SBML that encodes all timecourses # as events. @@ -72,6 +75,7 @@ def to_single_petab_with_events(problem: Problem): # timecourse when simulating the single SBML pass + def to_multiple_petab(problem: Problem): # one PEtab per unique period, and the order in which to # simulate each PEtab, for each timecourse @@ -79,10 +83,12 @@ def to_multiple_petab(problem: Problem): # of that period pass + def to_single_petab(problem: Problem): - # + # pass + def get_models(problem: petab.Problem): models = {} for timecourse_id in problem.timecourse_df.index: diff --git a/petab_timecourse/timecourse.py b/petab_timecourse/timecourse.py index 20d8525..1878bc1 100644 --- a/petab_timecourse/timecourse.py +++ b/petab_timecourse/timecourse.py @@ -50,6 +50,7 @@ class Period: The PEtab problem for this time period. e.g.: only contains measurements within the period start and end. """ + def __init__( self, duration: TYPE_TIME, @@ -91,8 +92,8 @@ def get_measurements( after_start = petab_problem.measurement_df[TIME] >= t0 before_end = ( petab_problem.measurement_df[TIME] <= t_end - if include_end else - petab_problem.measurement_df[TIME] < t_end + if include_end + else petab_problem.measurement_df[TIME] < t_end ) return petab_problem.measurement_df.loc[after_start & before_end] @@ -114,6 +115,7 @@ class Timecourse: periods: The periods of the timecourse. """ + def __init__( self, timecourse_id: str, @@ -139,11 +141,11 @@ def condition_ids(self): @staticmethod def from_timecourses( - timecourses: Sequence['Timecourse'], + timecourses: Sequence["Timecourse"], durations: Sequence[float], *args, **kwargs, - ) -> 'Timecourse': + ) -> "Timecourse": """Create a timecourse from a sequence of timecourses. Args: @@ -158,8 +160,8 @@ def from_timecourses( """ if len(durations) != len(timecourses) - 1: raise ValueError( - 'Please specify one fewer durations than timecourses. The ' - 'duration of the final timecourse will be unlimited.' + "Please specify one fewer durations than timecourses. The " + "duration of the final timecourse will be unlimited." ) periods = [] @@ -218,12 +220,10 @@ def to_df(self): ) data[TIMECOURSE] = timecourse_str - return get_timecourse_df( - pd.DataFrame(data=data) - ) + return get_timecourse_df(pd.DataFrame(data=data)) @staticmethod - def from_df_row(row: pd.Series) -> 'Timecourse': + def from_df_row(row: pd.Series, condition_df: pd.DataFrame) -> "Timecourse": periods = [] period_sequence = [ time__condition_id.split(TIME_CONDITION_DELIMITER) @@ -231,6 +231,7 @@ def from_df_row(row: pd.Series) -> 'Timecourse': ] t0 = None for period_index, (start, condition_id) in enumerate(period_sequence): + # calculate duration if t0 is None: try: t0 = float(start) @@ -241,20 +242,21 @@ def from_df_row(row: pd.Series) -> 'Timecourse': # End the period early if another period comes afterwards if period_index < len(period_sequence) - 1: end = period_sequence[period_index + 1][0] - try: start = float(start) end = float(end) except ValueError: raise ValueError( - 'Parameterized timepoints are not yet supported. ' - 'Please request.' + "Parameterized timepoints are not yet supported. " "Please request." ) + # get parameters + parameters = condition_df.loc[condition_id].to_dict() periods.append( Period( - duration=end-start, + duration=end - start, condition_id=condition_id, + parameters=parameters, ) ) @@ -269,16 +271,15 @@ def from_df_row(row: pd.Series) -> 'Timecourse': def from_df( timecourse_df: pd.DataFrame, timecourse_id: str, - ) -> 'Timecourse': - return Timecourse.from_df_row(timecourse_df.loc[timecourse_id]) + condition_df: pd.DataFrame, + ) -> "Timecourse": + return Timecourse.from_df_row(timecourse_df.loc[timecourse_id], condition_df) def __len__(self): return len(self.periods) -def get_timecourse_df( - timecourse_file: Union[str, pd.DataFrame, None] -) -> pd.DataFrame: +def get_timecourse_df(timecourse_file: Union[str, pd.DataFrame, None]) -> pd.DataFrame: """Read the provided condition file into a ``pandas.Dataframe`` Conditions are rows, parameters are columns, conditionId is index. Arguments: @@ -290,8 +291,8 @@ def get_timecourse_df( if isinstance(timecourse_file, (str, Path)): timecourse_file = pd.read_csv( timecourse_file, - sep='\t', - float_precision='round_trip', + sep="\t", + float_precision="round_trip", ) petab.lint.assert_no_leading_trailing_whitespace( @@ -304,7 +305,6 @@ def get_timecourse_df( try: timecourse_file.set_index([TIMECOURSE_ID], inplace=True) except KeyError: - raise KeyError( - f'Timecourse table missing mandatory field {TIMECOURSE_ID}.') + raise KeyError(f"Timecourse table missing mandatory field {TIMECOURSE_ID}.") return timecourse_file From 01f6b8a5c984f64aae517d25323319b909d5e9e4 Mon Sep 17 00:00:00 2001 From: Maren Philipps Date: Wed, 17 Apr 2024 11:35:11 +0200 Subject: [PATCH 3/5] add Timecourse export function --- petab_timecourse/timecourse.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/petab_timecourse/timecourse.py b/petab_timecourse/timecourse.py index 1878bc1..dfcb8dc 100644 --- a/petab_timecourse/timecourse.py +++ b/petab_timecourse/timecourse.py @@ -277,6 +277,15 @@ def from_df( def __len__(self): return len(self.periods) + + def export_to_amici(self) -> Tuple[Tuple[float, Dict[str, float]]]: + """ + Returns: + One tuple per period, with period duration and condition parameters. + """ + durations = [p.duration for p in self.periods] + parameters = [p.parameters for p in self.periods] + return tuple(zip(durations, parameters)) def get_timecourse_df(timecourse_file: Union[str, pd.DataFrame, None]) -> pd.DataFrame: From 188c86dcaa679a43f8895ae391a1e0249385dab3 Mon Sep 17 00:00:00 2001 From: Maren Philipps Date: Wed, 17 Apr 2024 11:35:58 +0200 Subject: [PATCH 4/5] fix docstring --- petab_timecourse/timecourse.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/petab_timecourse/timecourse.py b/petab_timecourse/timecourse.py index dfcb8dc..ac82a0f 100644 --- a/petab_timecourse/timecourse.py +++ b/petab_timecourse/timecourse.py @@ -289,10 +289,10 @@ def export_to_amici(self) -> Tuple[Tuple[float, Dict[str, float]]]: def get_timecourse_df(timecourse_file: Union[str, pd.DataFrame, None]) -> pd.DataFrame: - """Read the provided condition file into a ``pandas.Dataframe`` - Conditions are rows, parameters are columns, conditionId is index. + """Read the provided timecourse file into a ``pandas.Dataframe`` + Timecourses are rows, periods are columns, timecourseId is index. Arguments: - condition_file: File name of PEtab condition file or pandas.Dataframe + timecourse_file: File name of PEtab timecourse file or pandas.Dataframe """ if timecourse_file is None: return timecourse_file From 6a8ab7d762aeaea0c4279137f7c3130a39a2c1af Mon Sep 17 00:00:00 2001 From: Maren Philipps Date: Wed, 15 May 2024 18:26:38 +0200 Subject: [PATCH 5/5] fix function call to Timecourse.from_df --- doc/examples/amici_periods_vs_events/simulate_as_events.py | 2 +- doc/examples/amici_periods_vs_events/simulate_in_periods.py | 2 +- petab_timecourse/sbml.py | 1 + petab_timecourse/simulator.py | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/examples/amici_periods_vs_events/simulate_as_events.py b/doc/examples/amici_periods_vs_events/simulate_as_events.py index 307570d..6ac383d 100644 --- a/doc/examples/amici_periods_vs_events/simulate_as_events.py +++ b/doc/examples/amici_periods_vs_events/simulate_as_events.py @@ -33,7 +33,7 @@ # Setup timecourse, add it to the PEtab problem timecourse_df = petab_timecourse.get_timecourse_df(petab_path / 'timecourse.tsv') petab_problem.timecourse_df = timecourse_df -timecourse = petab_timecourse.Timecourse.from_df(timecourse_df, timecourse_id) +timecourse = petab_timecourse.Timecourse.from_df(timecourse_df, timecourse_id, petab_problem.condition_df) # Setup AMICI solver_settings = { diff --git a/doc/examples/amici_periods_vs_events/simulate_in_periods.py b/doc/examples/amici_periods_vs_events/simulate_in_periods.py index 5495844..9b4ed40 100644 --- a/doc/examples/amici_periods_vs_events/simulate_in_periods.py +++ b/doc/examples/amici_periods_vs_events/simulate_in_periods.py @@ -33,7 +33,7 @@ # Setup timecourse, add it to the PEtab problem timecourse_df = petab_timecourse.get_timecourse_df(petab_path / 'timecourse.tsv') petab_problem.timecourse_df = timecourse_df -timecourse = petab_timecourse.Timecourse.from_df(timecourse_df, timecourse_id) +timecourse = petab_timecourse.Timecourse.from_df(timecourse_df, timecourse_id, petab_problem.condition_df) # Setup AMICI solver_settings = { diff --git a/petab_timecourse/sbml.py b/petab_timecourse/sbml.py index 41015a8..584ac4d 100644 --- a/petab_timecourse/sbml.py +++ b/petab_timecourse/sbml.py @@ -134,6 +134,7 @@ def add_timecourse_as_events( timecourse = Timecourse.from_df( timecourse_df=petab_problem.timecourse_df, timecourse_id=timecourse_id, + condition_df=petab_problem.condition_df, ) #timecourse = parse_timecourse_string( # petab_problem.timecourse_df.loc[timecourse_id][TIMECOURSE], diff --git a/petab_timecourse/simulator.py b/petab_timecourse/simulator.py index fb7a9bf..e387f8f 100644 --- a/petab_timecourse/simulator.py +++ b/petab_timecourse/simulator.py @@ -56,6 +56,7 @@ def __init__( self.timecourse = Timecourse.from_df( timecourse_df=petab_problem.timecourse_df, timecourse_id=timecourse_id, + condition_df=petab_problem.condition_df, ) # FIXME t0 should not be 0 for preeq/presim