Skip to content

Commit

Permalink
Release v0.2.5
Browse files Browse the repository at this point in the history
  • Loading branch information
dilpath authored Dec 12, 2023
2 parents 9f22a6d + 9e5bcc4 commit 4e6a018
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 21 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

## 0.2 series

### 0.2.5

* Fix accessing `preequilibrationConditionId` without checking for presence
by @dweindl in https://github.com/PEtab-dev/libpetab-python/pull/228
* Startpoint sampling for a subset of parameters
by @dweindl in https://github.com/PEtab-dev/libpetab-python/pull/230
* Treat `observableParameter` overrides as placeholders in noise formulae
by @dilpath in https://github.com/PEtab-dev/libpetab-python/pull/231

**Full Changelog**: https://github.com/PEtab-dev/libpetab-python/compare/v0.2.4...v0.2.5

### 0.2.4

* Made figure sizes for visualization functions customizable via `petab.visualize.plotting.DEFAULT_FIGSIZE`
Expand Down
39 changes: 32 additions & 7 deletions petab/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,27 @@ def append_overrides(overrides):
)

# Add output parameters except for placeholders
for kwargs in [
dict(observables=True, noise=False),
dict(observables=False, noise=True),
]:
for formula_type, placeholder_sources in (
(
# Observable formulae
{'observables': True, 'noise': False},
# can only contain observable placeholders
{'noise': False, 'observables': True}
),
(
# Noise formulae
{'observables': False, 'noise': True},
# can contain noise and observable placeholders
{'noise': True, 'observables': True}
),
):
output_parameters = observables.get_output_parameters(
observable_df, model, mapping_df=mapping_df, **kwargs
observable_df, model, mapping_df=mapping_df, **formula_type,
)
placeholders = observables.get_placeholders(
observable_df,
**placeholder_sources,
)
placeholders = observables.get_placeholders(observable_df, **kwargs)
for p in output_parameters:
if p not in placeholders:
parameter_ids[p] = None
Expand Down Expand Up @@ -424,20 +437,32 @@ def append_overrides(overrides):
def get_priors_from_df(
parameter_df: pd.DataFrame,
mode: Literal["initialization", "objective"],
parameter_ids: Sequence[str] = None,
) -> List[Tuple]:
"""Create list with information about the parameter priors
Arguments:
parameter_df: PEtab parameter table
mode: ``'initialization'`` or ``'objective'``
parameter_ids: A sequence of parameter IDs for which to sample starting points.
For subsetting or reordering the parameters.
Defaults to all estimated parameters.
Returns:
List with prior information.
"""

# get types and parameters of priors from dataframe
par_to_estimate = parameter_df.loc[parameter_df[ESTIMATE] == 1]

if parameter_ids:
try:
par_to_estimate = par_to_estimate.loc[parameter_ids, :]
except KeyError as e:
missing_ids = set(parameter_ids) - set(par_to_estimate.index)
raise KeyError(
f"Parameter table does not contain estimated parameter(s) {missing_ids}."
) from e

prior_list = []
for _, row in par_to_estimate.iterrows():
# retrieve info about type
Expand Down
4 changes: 2 additions & 2 deletions petab/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,13 +930,13 @@ def create_parameter_df(self, *args, **kwargs):
**kwargs,
)

def sample_parameter_startpoints(self, n_starts: int = 100):
def sample_parameter_startpoints(self, n_starts: int = 100, **kwargs):
"""Create 2D array with starting points for optimization
See :py:func:`petab.sample_parameter_startpoints`.
"""
return sampling.sample_parameter_startpoints(
self.parameter_df, n_starts=n_starts
self.parameter_df, n_starts=n_starts, **kwargs
)

def sample_parameter_startpoints_dict(
Expand Down
10 changes: 7 additions & 3 deletions petab/sampling.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Functions related to parameter sampling"""

from typing import Tuple
from typing import Sequence, Tuple

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -110,24 +110,28 @@ def sample_parameter_startpoints(
parameter_df: pd.DataFrame,
n_starts: int = 100,
seed: int = None,
parameter_ids: Sequence[str] = None,
) -> np.array:
"""Create :class:`numpy.array` with starting points for an optimization
Arguments:
parameter_df: PEtab parameter DataFrame
n_starts: Number of points to be sampled
seed: Random number generator seed (see :func:`numpy.random.seed`)
parameter_ids: A sequence of parameter IDs for which to sample starting points.
For subsetting or reordering the parameters.
Defaults to all estimated parameters.
Returns:
Array of sampled starting points with dimensions
n_startpoints x n_optimization_parameters
`n_startpoints` x `n_optimization_parameters`
"""
if seed is not None:
np.random.seed(seed)

# get types and parameters of priors from dataframe
prior_list = parameters.get_priors_from_df(
parameter_df, mode=INITIALIZATION
parameter_df, mode=INITIALIZATION, parameter_ids=parameter_ids
)

startpoints = [sample_from_prior(prior, n_starts) for prior in prior_list]
Expand Down
2 changes: 1 addition & 1 deletion petab/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""PEtab library version"""
__version__ = "0.2.4"
__version__ = "0.2.5"
9 changes: 5 additions & 4 deletions petab/visualize/data_overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ def get_data_per_observable(measurement_df: pd.DataFrame) -> pd.DataFrame:
"""

my_measurements = measurement_df.copy()
my_measurements[PREEQUILIBRATION_CONDITION_ID] = my_measurements[
PREEQUILIBRATION_CONDITION_ID
].astype("object")

index = [SIMULATION_CONDITION_ID]
if PREEQUILIBRATION_CONDITION_ID in my_measurements:
my_measurements[PREEQUILIBRATION_CONDITION_ID].fillna("", inplace=True)
my_measurements[PREEQUILIBRATION_CONDITION_ID] = (
my_measurements[PREEQUILIBRATION_CONDITION_ID]
.astype("object")
.fillna("", inplace=True)
)
index.append(PREEQUILIBRATION_CONDITION_ID)

data_per_observable = pd.pivot_table(
Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
filterwarnings =
error
ignore:.*inspect.getargspec\(\) is deprecated.*:DeprecationWarning
ignore:.*Passing unrecognized arguments to super\(PyDevIPCompleter6\).*:DeprecationWarning
54 changes: 50 additions & 4 deletions tests/test_petab.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ def petab_problem():

observable_df = pd.DataFrame(
data={
OBSERVABLE_ID: ["observable_1"],
OBSERVABLE_ID: ["obs1"],
OBSERVABLE_NAME: ["julius"],
OBSERVABLE_FORMULA: ["observable_1"],
NOISE_FORMULA: [1],
OBSERVABLE_FORMULA: ["observable_1 * observableParameter1_obs1"],
NOISE_FORMULA: ["0.1 * observable_1 * observableParameter1_obs1"],
}
).set_index(OBSERVABLE_ID)

Expand Down Expand Up @@ -179,6 +179,7 @@ def test_get_priors_from_df():
"""Check petab.get_priors_from_df."""
parameter_df = pd.DataFrame(
{
PARAMETER_ID: ["p1", "p2", "p3", "p4", "p5"],
PARAMETER_SCALE: [LOG10, LOG10, LOG10, LOG10, LOG10],
LOWER_BOUND: [1e-8, 1e-9, 1e-10, 1e-11, 1e-5],
UPPER_BOUND: [1e8, 1e9, 1e10, 1e11, 1e5],
Expand All @@ -193,6 +194,7 @@ def test_get_priors_from_df():
],
}
)
parameter_df = petab.get_parameter_df(parameter_df)

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

Expand Down Expand Up @@ -225,6 +227,18 @@ def test_get_priors_from_df():
assert prior_pars[1] == (-5, 5)
assert prior_pars[2] == (1e-5, 1e5)

# check subsetting / reordering works
prior_list_subset = petab.get_priors_from_df(
parameter_df, mode=INITIALIZATION, parameter_ids=["p2", "p1"]
)
assert len(prior_list_subset) == 2
assert prior_list_subset == [prior_list[1], prior_list[0]]

with pytest.raises(KeyError, match="Parameter table does not contain"):
petab.get_priors_from_df(
parameter_df, mode=INITIALIZATION, parameter_ids=["non_existent"]
)


def test_startpoint_sampling(fujita_model_scaling):
n_starts = 10
Expand Down Expand Up @@ -639,7 +653,7 @@ def test_concat_condition_df():

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


def test_parameter_properties(petab_problem): # pylint: disable=W0621
Expand Down Expand Up @@ -809,3 +823,35 @@ def test_problem_from_yaml_v1_multiple_files():
assert petab_problem.measurement_df.shape[0] == 2
assert petab_problem.observable_df.shape[0] == 2
assert petab_problem.condition_df.shape[0] == 2


def test_get_required_parameters_for_parameter_table(petab_problem):
"""Test identification of required parameter table parameters.
NB: currently, this test only checks that observable parameter placeholders
in noise formulae are correctly identified as not required in the parameter
table.
"""
noise_placeholders = petab.observables.get_output_parameters(
petab_problem.observable_df,
petab_problem.model,
observables=False,
noise=True,
)
# The observable parameter (scaling) appears in the noise formula,
# as part of the proportional error model.
assert "observableParameter1_obs1" in noise_placeholders

required_parameters_for_parameter_table = \
petab.parameters.get_required_parameters_for_parameter_table(
model=petab_problem.model,
condition_df=petab_problem.condition_df,
observable_df=petab_problem.observable_df,
measurement_df=petab_problem.measurement_df,
)
# The observable parameter is correctly recognized as a placeholder,
# i.e. does not need to be in the parameter table.
assert (
"observableParameter1_obs1"
not in required_parameters_for_parameter_table
)

0 comments on commit 4e6a018

Please sign in to comment.