Skip to content

Commit 4e6a018

Browse files
authored
Release v0.2.5
2 parents 9f22a6d + 9e5bcc4 commit 4e6a018

File tree

8 files changed

+109
-21
lines changed

8 files changed

+109
-21
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
## 0.2 series
44

5+
### 0.2.5
6+
7+
* Fix accessing `preequilibrationConditionId` without checking for presence
8+
by @dweindl in https://github.com/PEtab-dev/libpetab-python/pull/228
9+
* Startpoint sampling for a subset of parameters
10+
by @dweindl in https://github.com/PEtab-dev/libpetab-python/pull/230
11+
* Treat `observableParameter` overrides as placeholders in noise formulae
12+
by @dilpath in https://github.com/PEtab-dev/libpetab-python/pull/231
13+
14+
**Full Changelog**: https://github.com/PEtab-dev/libpetab-python/compare/v0.2.4...v0.2.5
15+
516
### 0.2.4
617

718
* Made figure sizes for visualization functions customizable via `petab.visualize.plotting.DEFAULT_FIGSIZE`

petab/parameters.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,27 @@ def append_overrides(overrides):
298298
)
299299

300300
# Add output parameters except for placeholders
301-
for kwargs in [
302-
dict(observables=True, noise=False),
303-
dict(observables=False, noise=True),
304-
]:
301+
for formula_type, placeholder_sources in (
302+
(
303+
# Observable formulae
304+
{'observables': True, 'noise': False},
305+
# can only contain observable placeholders
306+
{'noise': False, 'observables': True}
307+
),
308+
(
309+
# Noise formulae
310+
{'observables': False, 'noise': True},
311+
# can contain noise and observable placeholders
312+
{'noise': True, 'observables': True}
313+
),
314+
):
305315
output_parameters = observables.get_output_parameters(
306-
observable_df, model, mapping_df=mapping_df, **kwargs
316+
observable_df, model, mapping_df=mapping_df, **formula_type,
317+
)
318+
placeholders = observables.get_placeholders(
319+
observable_df,
320+
**placeholder_sources,
307321
)
308-
placeholders = observables.get_placeholders(observable_df, **kwargs)
309322
for p in output_parameters:
310323
if p not in placeholders:
311324
parameter_ids[p] = None
@@ -424,20 +437,32 @@ def append_overrides(overrides):
424437
def get_priors_from_df(
425438
parameter_df: pd.DataFrame,
426439
mode: Literal["initialization", "objective"],
440+
parameter_ids: Sequence[str] = None,
427441
) -> List[Tuple]:
428442
"""Create list with information about the parameter priors
429443
430444
Arguments:
431445
parameter_df: PEtab parameter table
432446
mode: ``'initialization'`` or ``'objective'``
447+
parameter_ids: A sequence of parameter IDs for which to sample starting points.
448+
For subsetting or reordering the parameters.
449+
Defaults to all estimated parameters.
433450
434451
Returns:
435452
List with prior information.
436453
"""
437-
438454
# get types and parameters of priors from dataframe
439455
par_to_estimate = parameter_df.loc[parameter_df[ESTIMATE] == 1]
440456

457+
if parameter_ids:
458+
try:
459+
par_to_estimate = par_to_estimate.loc[parameter_ids, :]
460+
except KeyError as e:
461+
missing_ids = set(parameter_ids) - set(par_to_estimate.index)
462+
raise KeyError(
463+
f"Parameter table does not contain estimated parameter(s) {missing_ids}."
464+
) from e
465+
441466
prior_list = []
442467
for _, row in par_to_estimate.iterrows():
443468
# retrieve info about type

petab/problem.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,13 +930,13 @@ def create_parameter_df(self, *args, **kwargs):
930930
**kwargs,
931931
)
932932

933-
def sample_parameter_startpoints(self, n_starts: int = 100):
933+
def sample_parameter_startpoints(self, n_starts: int = 100, **kwargs):
934934
"""Create 2D array with starting points for optimization
935935
936936
See :py:func:`petab.sample_parameter_startpoints`.
937937
"""
938938
return sampling.sample_parameter_startpoints(
939-
self.parameter_df, n_starts=n_starts
939+
self.parameter_df, n_starts=n_starts, **kwargs
940940
)
941941

942942
def sample_parameter_startpoints_dict(

petab/sampling.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Functions related to parameter sampling"""
22

3-
from typing import Tuple
3+
from typing import Sequence, Tuple
44

55
import numpy as np
66
import pandas as pd
@@ -110,24 +110,28 @@ def sample_parameter_startpoints(
110110
parameter_df: pd.DataFrame,
111111
n_starts: int = 100,
112112
seed: int = None,
113+
parameter_ids: Sequence[str] = None,
113114
) -> np.array:
114115
"""Create :class:`numpy.array` with starting points for an optimization
115116
116117
Arguments:
117118
parameter_df: PEtab parameter DataFrame
118119
n_starts: Number of points to be sampled
119120
seed: Random number generator seed (see :func:`numpy.random.seed`)
121+
parameter_ids: A sequence of parameter IDs for which to sample starting points.
122+
For subsetting or reordering the parameters.
123+
Defaults to all estimated parameters.
120124
121125
Returns:
122126
Array of sampled starting points with dimensions
123-
n_startpoints x n_optimization_parameters
127+
`n_startpoints` x `n_optimization_parameters`
124128
"""
125129
if seed is not None:
126130
np.random.seed(seed)
127131

128132
# get types and parameters of priors from dataframe
129133
prior_list = parameters.get_priors_from_df(
130-
parameter_df, mode=INITIALIZATION
134+
parameter_df, mode=INITIALIZATION, parameter_ids=parameter_ids
131135
)
132136

133137
startpoints = [sample_from_prior(prior, n_starts) for prior in prior_list]

petab/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""PEtab library version"""
2-
__version__ = "0.2.4"
2+
__version__ = "0.2.5"

petab/visualize/data_overview.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ def get_data_per_observable(measurement_df: pd.DataFrame) -> pd.DataFrame:
6161
"""
6262

6363
my_measurements = measurement_df.copy()
64-
my_measurements[PREEQUILIBRATION_CONDITION_ID] = my_measurements[
65-
PREEQUILIBRATION_CONDITION_ID
66-
].astype("object")
6764

6865
index = [SIMULATION_CONDITION_ID]
6966
if PREEQUILIBRATION_CONDITION_ID in my_measurements:
70-
my_measurements[PREEQUILIBRATION_CONDITION_ID].fillna("", inplace=True)
67+
my_measurements[PREEQUILIBRATION_CONDITION_ID] = (
68+
my_measurements[PREEQUILIBRATION_CONDITION_ID]
69+
.astype("object")
70+
.fillna("", inplace=True)
71+
)
7172
index.append(PREEQUILIBRATION_CONDITION_ID)
7273

7374
data_per_observable = pd.pivot_table(

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
filterwarnings =
33
error
44
ignore:.*inspect.getargspec\(\) is deprecated.*:DeprecationWarning
5+
ignore:.*Passing unrecognized arguments to super\(PyDevIPCompleter6\).*:DeprecationWarning

tests/test_petab.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ def petab_problem():
6868

6969
observable_df = pd.DataFrame(
7070
data={
71-
OBSERVABLE_ID: ["observable_1"],
71+
OBSERVABLE_ID: ["obs1"],
7272
OBSERVABLE_NAME: ["julius"],
73-
OBSERVABLE_FORMULA: ["observable_1"],
74-
NOISE_FORMULA: [1],
73+
OBSERVABLE_FORMULA: ["observable_1 * observableParameter1_obs1"],
74+
NOISE_FORMULA: ["0.1 * observable_1 * observableParameter1_obs1"],
7575
}
7676
).set_index(OBSERVABLE_ID)
7777

@@ -179,6 +179,7 @@ def test_get_priors_from_df():
179179
"""Check petab.get_priors_from_df."""
180180
parameter_df = pd.DataFrame(
181181
{
182+
PARAMETER_ID: ["p1", "p2", "p3", "p4", "p5"],
182183
PARAMETER_SCALE: [LOG10, LOG10, LOG10, LOG10, LOG10],
183184
LOWER_BOUND: [1e-8, 1e-9, 1e-10, 1e-11, 1e-5],
184185
UPPER_BOUND: [1e8, 1e9, 1e10, 1e11, 1e5],
@@ -193,6 +194,7 @@ def test_get_priors_from_df():
193194
],
194195
}
195196
)
197+
parameter_df = petab.get_parameter_df(parameter_df)
196198

197199
prior_list = petab.get_priors_from_df(parameter_df, mode=INITIALIZATION)
198200

@@ -225,6 +227,18 @@ def test_get_priors_from_df():
225227
assert prior_pars[1] == (-5, 5)
226228
assert prior_pars[2] == (1e-5, 1e5)
227229

230+
# check subsetting / reordering works
231+
prior_list_subset = petab.get_priors_from_df(
232+
parameter_df, mode=INITIALIZATION, parameter_ids=["p2", "p1"]
233+
)
234+
assert len(prior_list_subset) == 2
235+
assert prior_list_subset == [prior_list[1], prior_list[0]]
236+
237+
with pytest.raises(KeyError, match="Parameter table does not contain"):
238+
petab.get_priors_from_df(
239+
parameter_df, mode=INITIALIZATION, parameter_ids=["non_existent"]
240+
)
241+
228242

229243
def test_startpoint_sampling(fujita_model_scaling):
230244
n_starts = 10
@@ -639,7 +653,7 @@ def test_concat_condition_df():
639653

640654
def test_get_observable_ids(petab_problem): # pylint: disable=W0621
641655
"""Test if observable ids functions returns correct value."""
642-
assert set(petab_problem.get_observable_ids()) == {"observable_1"}
656+
assert set(petab_problem.get_observable_ids()) == {"obs1"}
643657

644658

645659
def test_parameter_properties(petab_problem): # pylint: disable=W0621
@@ -809,3 +823,35 @@ def test_problem_from_yaml_v1_multiple_files():
809823
assert petab_problem.measurement_df.shape[0] == 2
810824
assert petab_problem.observable_df.shape[0] == 2
811825
assert petab_problem.condition_df.shape[0] == 2
826+
827+
828+
def test_get_required_parameters_for_parameter_table(petab_problem):
829+
"""Test identification of required parameter table parameters.
830+
831+
NB: currently, this test only checks that observable parameter placeholders
832+
in noise formulae are correctly identified as not required in the parameter
833+
table.
834+
"""
835+
noise_placeholders = petab.observables.get_output_parameters(
836+
petab_problem.observable_df,
837+
petab_problem.model,
838+
observables=False,
839+
noise=True,
840+
)
841+
# The observable parameter (scaling) appears in the noise formula,
842+
# as part of the proportional error model.
843+
assert "observableParameter1_obs1" in noise_placeholders
844+
845+
required_parameters_for_parameter_table = \
846+
petab.parameters.get_required_parameters_for_parameter_table(
847+
model=petab_problem.model,
848+
condition_df=petab_problem.condition_df,
849+
observable_df=petab_problem.observable_df,
850+
measurement_df=petab_problem.measurement_df,
851+
)
852+
# The observable parameter is correctly recognized as a placeholder,
853+
# i.e. does not need to be in the parameter table.
854+
assert (
855+
"observableParameter1_obs1"
856+
not in required_parameters_for_parameter_table
857+
)

0 commit comments

Comments
 (0)