diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2823b646..8f748193 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,10 +3,10 @@ # default owners * @PEtab-dev/libpetab-python-maintainers -/petab/visualize/ @plakrisenko -/tests/test_vis* @plakrisenko +/petab/visualize/ @m-philipps +/tests/test_vis* @m-philipps /petab/simulate.py @dilpath /tests/test_simulate.py @dilpath -/petab/calculate.py @yannikschaelte -/tests/test_calculate.py @yannikschaelte /doc/ @dweindl +/doc/example @m-philipps +/doc/example/example_petablint.ipynb @dweindl diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bd5768d..792cd429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## 0.2 series +### 0.2.6 + +* Fixed `flatten_timepoint_specific_output_overrides` not supporting + observableParameter overrides as placeholders in noise formulae + by @dweindl in https://github.com/PEtab-dev/libpetab-python/pull/235 +* Visualization: fixed replicate sorting + by @plakrisenko in https://github.com/PEtab-dev/libpetab-python/pull/232 +* Doc: Revised visualization examples + by @dweindl in https://github.com/PEtab-dev/libpetab-python/pull/236 + +**Full Changelog**: https://github.com/PEtab-dev/libpetab-python/compare/v0.2.5...v0.2.6 + ### 0.2.5 * Fix accessing `preequilibrationConditionId` without checking for presence diff --git a/doc/conf.py b/doc/conf.py index d29b2984..a309a3df 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -84,7 +84,8 @@ # myst_nb options # https://myst-nb.readthedocs.io/en/latest/configuration.html nb_execution_mode = "force" - +nb_execution_raise_on_error = True +nb_execution_show_tb = True source_suffix = { ".rst": "restructuredtext", diff --git a/doc/example.rst b/doc/example.rst index f3383103..6fe6dab5 100644 --- a/doc/example.rst +++ b/doc/example.rst @@ -10,8 +10,6 @@ The following examples should help to get a better idea of how to use the PEtab example/example_petablint.ipynb example/example_visualization.ipynb - example/example_visualization_without_visspec.ipynb - example/example_visualization_with_visspec.ipynb Examples of systems biology parameter estimation problems specified in PEtab can be found in the `systems biology benchmark model collection `_. diff --git a/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_datasetIds.tsv b/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_datasetIds.tsv index 150779bf..9cf511a7 100644 --- a/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_datasetIds.tsv +++ b/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_datasetIds.tsv @@ -1,19 +1,19 @@ -plotId plotTypeSimulation plotTypeData datasetId xValues legendEntry -plot1 LinePlot provided model1_data1_pEGFR_tot time Data 1, pEGFR total -plot1 LinePlot provided model1_data2_pEGFR_tot time Data 2, pEGFR total -plot1 LinePlot provided model1_data3_pEGFR_tot time Data 3, pEGFR total -plot1 LinePlot provided model1_data4_pEGFR_tot time Data 4, pEGFR total -plot1 LinePlot provided model1_data5_pEGFR_tot time Data 5, pEGFR total -plot1 LinePlot provided model1_data6_pEGFR_tot time Data 6, pEGFR total -plot2 LinePlot provided model1_data1_pAkt_tot time Data 1, pAkt total -plot2 LinePlot provided model1_data2_pAkt_tot time Data 2, pAkt total -plot2 LinePlot provided model1_data3_pAkt_tot time Data 3, pAkt total -plot2 LinePlot provided model1_data4_pAkt_tot time Data 4, pAkt total -plot2 LinePlot provided model1_data5_pAkt_tot time Data 5, pAkt total -plot2 LinePlot provided model1_data6_pAkt_tot time Data 6, pAkt total -plot3 LinePlot provided model1_data1_pS6_tot time Data 1, pS6 total -plot3 LinePlot provided model1_data2_pS6_tot time Data 2, pS6 total -plot3 LinePlot provided model1_data3_pS6_tot time Data 3, pS6 total -plot3 LinePlot provided model1_data4_pS6_tot time Data 4, pS6 total -plot3 LinePlot provided model1_data5_pS6_tot time Data 5, pS6 total -plot3 LinePlot provided model1_data6_pS6_tot time Data 6, pS6 total +plotId plotTypeData datasetId xValues legendEntry +plot1 provided model1_data1_pEGFR_tot time Data 1, pEGFR total +plot1 provided model1_data2_pEGFR_tot time Data 2, pEGFR total +plot1 provided model1_data3_pEGFR_tot time Data 3, pEGFR total +plot1 provided model1_data4_pEGFR_tot time Data 4, pEGFR total +plot1 provided model1_data5_pEGFR_tot time Data 5, pEGFR total +plot1 provided model1_data6_pEGFR_tot time Data 6, pEGFR total +plot2 provided model1_data1_pAkt_tot time Data 1, pAkt total +plot2 provided model1_data2_pAkt_tot time Data 2, pAkt total +plot2 provided model1_data3_pAkt_tot time Data 3, pAkt total +plot2 provided model1_data4_pAkt_tot time Data 4, pAkt total +plot2 provided model1_data5_pAkt_tot time Data 5, pAkt total +plot2 provided model1_data6_pAkt_tot time Data 6, pAkt total +plot3 provided model1_data1_pS6_tot time Data 1, pS6 total +plot3 provided model1_data2_pS6_tot time Data 2, pS6 total +plot3 provided model1_data3_pS6_tot time Data 3, pS6 total +plot3 provided model1_data4_pS6_tot time Data 4, pS6 total +plot3 provided model1_data5_pS6_tot time Data 5, pS6 total +plot3 provided model1_data6_pS6_tot time Data 6, pS6 total diff --git a/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_individual_datasets.tsv b/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_individual_datasets.tsv index c4e63564..71551359 100644 --- a/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_individual_datasets.tsv +++ b/doc/example/example_Fujita/visuSpecs/Fujita_visuSpec_individual_datasets.tsv @@ -1,7 +1,7 @@ -plotId plotTypeSimulation plotTypeData datasetId xValues -plot1 LinePlot provided model1_data1_pEGFR_tot time -plot2 LinePlot provided model1_data2_pEGFR_tot time -plot2 LinePlot provided model1_data3_pEGFR_tot time -plot3 LinePlot provided model1_data4_pEGFR_tot time -plot3 LinePlot provided model1_data5_pEGFR_tot time -plot3 LinePlot provided model1_data6_pEGFR_tot time +plotId plotTypeData datasetId xValues +plot1 provided model1_data1_pEGFR_tot time +plot2 provided model1_data2_pEGFR_tot time +plot2 provided model1_data3_pEGFR_tot time +plot3 provided model1_data4_pEGFR_tot time +plot3 provided model1_data5_pEGFR_tot time +plot3 provided model1_data6_pEGFR_tot time diff --git a/doc/example/example_Isensee/Isensee.yaml b/doc/example/example_Isensee/Isensee.yaml new file mode 100644 index 00000000..a12e5a36 --- /dev/null +++ b/doc/example/example_Isensee/Isensee.yaml @@ -0,0 +1,11 @@ +format_version: 1 +parameter_file: +problems: +- condition_files: + - Isensee_experimentalCondition.tsv + measurement_files: + - Isensee_measurementData.tsv + observable_files: [] + sbml_files: [] + visualization_files: + - Isensee_visualizationSpecification.tsv diff --git a/doc/example/example_Isensee/Isensee_no_vis.yaml b/doc/example/example_Isensee/Isensee_no_vis.yaml new file mode 100644 index 00000000..16cb60b9 --- /dev/null +++ b/doc/example/example_Isensee/Isensee_no_vis.yaml @@ -0,0 +1,9 @@ +format_version: 1 +parameter_file: +problems: +- condition_files: + - Isensee_experimentalCondition.tsv + measurement_files: + - Isensee_measurementData.tsv + observable_files: [] + sbml_files: [] diff --git a/doc/example/example_visualization.ipynb b/doc/example/example_visualization.ipynb index 113de75a..f1d89b13 100644 --- a/doc/example/example_visualization.ipynb +++ b/doc/example/example_visualization.ipynb @@ -2,295 +2,576 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, "source": [ - "# Visualization of data and simulations" - ] + "# Visualization of data and simulations\n", + "\n", + "In this notebook, we illustrate the visualization functions of the petab library to visualize measurements or simulations.\n", + "\n", + "Some basic visualizations can be generated from a PEtab problem directly, without the need for a visualization specification file. This is illustrated in the first part of this notebook. For more advanced visualizations, a visualization specification file is required. This is illustrated in the second part of this notebook.\n", + "\n", + "For the following demonstrations, we will use two example problems obtained from the [Benchmark collection](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/), [Fujita_SciSignal2010](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Fujita_SciSignal2010) and [Isensee_JCB2018](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Isensee_JCB2018). Their specifics don't matter for the purpose of this notebook—we just need some PEtab problems to work with." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import petab\n", + "from petab import Problem\n", + "from petab.visualize import plot_problem\n", + "\n", + "example_dir_fujita = Path(\"example_Fujita\")\n", + "petab_yaml_fujita = example_dir_fujita / \"Fujita.yaml\"\n", + "example_dir_isensee = Path(\"example_Isensee\")\n", + "petab_yaml_isensee = example_dir_isensee / \"Isensee_no_vis.yaml\"\n", + "petab_yaml_isensee_vis = example_dir_isensee / \"Isensee.yaml\"\n", + "\n", + "# we change some settings to make the plots better readable\n", + "petab.visualize.plotting.DEFAULT_FIGSIZE[:] = (10, 8)\n", + "plt.rcParams[\"figure.figsize\"] = petab.visualize.plotting.DEFAULT_FIGSIZE\n", + "plt.rcParams[\"font.size\"] = 12\n", + "plt.rcParams[\"figure.dpi\"] = 150\n", + "plt.rcParams[\"legend.fontsize\"] = 10" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "In this notebook, we illustrate the visualization functions of petab." - ] + "## Visualization without visualization specification file" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Plotting measurements\n", + "\n", + "For the most basic visualization, we can use the [plot_problem()](https://petab.readthedocs.io/projects/libpetab-python/en/latest/build/_autosummary/petab.visualize.html#petab.visualize.plot_problem) function." + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": { - "pycharm": { - "is_executing": false - }, - "scrolled": false - }, "outputs": [], "source": [ - "from petab.visualize import plot_with_vis_spec, plot_without_vis_spec" - ] + "# load PEtab problem\n", + "petab_problem = Problem.from_yaml(petab_yaml_fujita)\n", + "\n", + "# plot measurements\n", + "petab.visualize.plot_problem(petab_problem);" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "As nothing was specified regarding what should be plotted, the defaults were used. Namely, it was assumed that measurements are time series data, and they were grouped by observables.\n", + "\n", + "### Subplots / subsetting the data\n", + "\n", + "Measurements or simulations can be grouped by observables, simulation conditions, or datasetIds with the `plot_problem()` function. This can be specified by setting the value of `group_by` parameter to `'observable'` (default), `'simulation'`, or `'dataset'` and by providing corresponding ids in `grouping_list`, which is a list of lists. Each sublist specifies a separate plot and its elements are either simulation condition IDs or observable IDs or the dataset IDs.\n", + "\n", + "#### By observable\n", + "\n", + "We can specify how many subplots there should be and what should be plotted on each of them. It can easily be done by providing `grouping_list`, which by default specifies, which observables should be plotted on a particular plot. The value of `grouping_list` should be a list of lists, each sublist corresponds to a separate plot." + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, + "outputs": [], + "source": [ + "petab.visualize.plot_problem(\n", + " petab_problem,\n", + " grouping_list=[[\"pEGFR_tot\"], [\"pAkt_tot\", \"pS6_tot\"]],\n", + " group_by=\"observable\",\n", + ")\n", + "plt.gcf().set_size_inches(10, 4)" + ], "metadata": { - "pycharm": { - "is_executing": false - } - }, + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "#### By simulation condition\n", + "\n", + "Another option is to specify which simulation conditions should be plotted:" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, "outputs": [], "source": [ - "folder = \"example_Isensee/\"\n", + "petab.visualize.plot_problem(\n", + " petab_problem,\n", + " grouping_list=[\n", + " [\"model1_data1\"],\n", + " [\"model1_data2\", \"model1_data3\"],\n", + " [\"model1_data4\", \"model1_data5\", \"model1_data6\"],\n", + " ],\n", + " group_by=\"simulation\",\n", + ");" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "#### By datasetId\n", "\n", - "data_file_path = folder + \"Isensee_measurementData.tsv\"\n", - "condition_file_path = folder + \"Isensee_experimentalCondition.tsv\"\n", - "visualization_file_path = folder + \"Isensee_visualizationSpecification.tsv\"\n", - "simulation_file_path = folder + \"Isensee_simulationData.tsv\"" - ] + "Finally, measurements can be grouped by `datasetId`s as specified in the measurements table, by passing lists of `datasetId`s. Each sublist corresponds to a subplot:" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "ax = plot_with_vis_spec(\n", - " visualization_file_path,\n", - " condition_file_path,\n", - " data_file_path,\n", - " simulation_file_path,\n", - ")" - ] + "petab.visualize.plot_problem(\n", + " petab_problem,\n", + " grouping_list=[\n", + " [\n", + " \"model1_data1_pEGFR_tot\",\n", + " \"model1_data2_pEGFR_tot\",\n", + " \"model1_data3_pEGFR_tot\",\n", + " \"model1_data4_pEGFR_tot\",\n", + " \"model1_data5_pEGFR_tot\",\n", + " \"model1_data6_pEGFR_tot\",\n", + " ],\n", + " [\n", + " \"model1_data1_pAkt_tot\",\n", + " \"model1_data2_pAkt_tot\",\n", + " \"model1_data3_pAkt_tot\",\n", + " \"model1_data4_pAkt_tot\",\n", + " \"model1_data5_pAkt_tot\",\n", + " \"model1_data6_pAkt_tot\",\n", + " ],\n", + " [\n", + " \"model1_data1_pS6_tot\",\n", + " \"model1_data2_pS6_tot\",\n", + " \"model1_data3_pS6_tot\",\n", + " \"model1_data4_pS6_tot\",\n", + " \"model1_data5_pS6_tot\",\n", + " \"model1_data6_pS6_tot\",\n", + " ],\n", + " ],\n", + " group_by=\"dataset\",\n", + ");" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "Now, we want to call the plotting routines without using the simulated data, only the visualization specification file." - ] + "### Plotting simulations\n", + "\n", + "We can also plot simulations together with the measurements, for example, to judge the model fit. For this, we need to provide a simulation file as `simulations_df`. A simulation file has the same format as the measurement file, but instead of the `measurement` column, it contains simulation outputs in the `simulation` column. The simulations are plotted as solid lines, while the measurements are plotted as dashed lines:" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "ax_without_sim = plot_with_vis_spec(\n", - " visualization_file_path, condition_file_path, data_file_path\n", - ")" - ] + "simu_file_Fujita = example_dir_fujita / \"Fujita_simulatedData.tsv\"\n", + "\n", + "sim_cond_id_list = [\n", + " [\"model1_data1\"],\n", + " [\"model1_data6\"],\n", + "]\n", + "petab_problem = Problem.from_yaml(petab_yaml_fujita)\n", + "plot_problem(\n", + " petab_problem,\n", + " simulations_df=simu_file_Fujita,\n", + " grouping_list=sim_cond_id_list,\n", + " group_by=\"simulation\",\n", + " plotted_noise=\"provided\",\n", + ")\n", + "plt.gcf().set_size_inches(10, 4)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "It is also possible to plot only the simulations without the measurements by setting `petab_problem.measurement_df = None`." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Visualization with a visualization specification file\n", + "\n", + "As described in the [PEtab documentation](https://petab.readthedocs.io/en/latest/documentation_data_format.html), the visualization specification file is a tab-separated value file specifying which data to plot in which way. In the following, we will build up a visualization specification file step by step." + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "One can also plot only simulated data:" - ] + "Without a visualization file, the independent variable defaults to time, and each observable is plotted in a separate subplot:" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "ax = plot_with_vis_spec(\n", - " visualization_file_path,\n", - " condition_file_path,\n", - " simulations_df=simulation_file_path,\n", - ")" - ] + "petab_problem = Problem.from_yaml(petab_yaml_fujita)\n", + "petab.visualize.plot_problem(petab_problem);" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "If both measurements and simulated data are available, they can be visualized using scatter plot:" - ] + "First, let us create a visualization specification file with only mandatory columns. In fact, there is only one mandatory column: `plotId`.\n", + "The most basic visualization file looks like this:" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "visualization_file_scatterplots = (\n", - " folder + \"Isensee_visualizationSpecification_scatterplot.tsv\"\n", + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_mandatory.tsv\"\n", ")\n", - "ax = plot_with_vis_spec(\n", - " visualization_file_scatterplots,\n", - " condition_file_path,\n", - " data_file_path,\n", - " simulation_file_path,\n", - ")" - ] + "petab_problem.visualization_df" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "We can also call the plotting routine without the visualization specification file, but by passing a list of lists as `dataset_id_list`. Each sublist corresponds to a plot, and contains the datasetIds which should be plotted.\n", - "In this simply structured plotting routine, the independent variable will always be time." - ] + "This way, all data will be shown in a single plot, taking time as independent variable. This is not very appealing yet, but we will improve it step by step." + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "datasets = [\n", - " [\n", - " \"JI09_150302_Drg345_343_CycNuc__4_ABnOH_and_ctrl\",\n", - " \"JI09_150302_Drg345_343_CycNuc__4_ABnOH_and_Fsk\",\n", - " ],\n", - " [\n", - " \"JI09_160201_Drg453-452_CycNuc__ctrl\",\n", - " \"JI09_160201_Drg453-452_CycNuc__Fsk\",\n", - " \"JI09_160201_Drg453-452_CycNuc__Sp8_Br_cAMPS_AM\",\n", - " ],\n", - "]\n", - "ax_without_sim = plot_without_vis_spec(\n", - " condition_file_path, datasets, \"dataset\", data_file_path\n", - ")" - ] + "petab.visualize.plot_problem(petab_problem)\n", + "plt.gcf().set_size_inches(10, 4)" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "Let's look more closely at the plotting routines, if no visualization specification file is provided. If such a file is missing, PEtab needs to know how to group the data points. For this, three options can be used:\n", - " * dataset_id_list\n", - " * sim_cond_id_lis\n", - " * observable_id_list\n", - "\n", - "Each of them is a list of lists. Again, each sublist is a plot and its content are either simulation condition IDs or observable IDs or the dataset IDs.\n", + "### Logarithmic scale and offset\n", "\n", - "We want to illustrate this functionality by using a simpler example, a model published in 2010 by Fujita et al." - ] + "Let's change some settings. For example, we can change the scale of the y-axis to logarithmic and apply an offset for the independent variable:" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "data_file_Fujita = \"example_Fujita/Fujita_measurementData.tsv\"\n", - "condition_file_Fujita = \"example_Fujita/Fujita_experimentalCondition.tsv\"\n", - "\n", - "# Plot 4 axes objects, plotting\n", - "# - in the first window all observables of the simulation condition 'model1_data1'\n", - "# - in the second window all observables of the simulation conditions 'model1_data2', 'model1_data3'\n", - "# - in the third window all observables of the simulation conditions 'model1_data4', 'model1_data5'\n", - "# - in the fourth window all observables of the simulation condition 'model1_data6'\n", - "\n", - "sim_cond_id_list = [\n", - " [\"model1_data1\"],\n", - " [\"model1_data2\", \"model1_data3\"],\n", - " [\"model1_data4\", \"model1_data5\"],\n", - " [\"model1_data6\"],\n", - "]\n", - "\n", - "ax = plot_without_vis_spec(\n", - " condition_file_Fujita,\n", - " sim_cond_id_list,\n", - " \"simulation\",\n", - " data_file_Fujita,\n", - " plotted_noise=\"provided\",\n", - ")" - ] + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_1.tsv\"\n", + ")\n", + "petab_problem.visualization_df" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "# Plot 3 axes objects, plotting\n", - "# - in the first window the observable 'pS6_tot' for all simulation conditions\n", - "# - in the second window the observable 'pEGFR_tot' for all simulation conditions\n", - "# - in the third window the observable 'pAkt_tot' for all simulation conditions\n", - "\n", - "observable_id_list = [[\"pS6_tot\"], [\"pEGFR_tot\"], [\"pAkt_tot\"]]\n", - "\n", + "petab.visualize.plot_problem(petab_problem)\n", + "plt.gcf().set_size_inches(10, 4)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Subplots by observable\n", "\n", - "ax = plot_without_vis_spec(\n", - " condition_file_Fujita,\n", - " observable_id_list,\n", - " \"observable\",\n", - " data_file_Fujita,\n", - " plotted_noise=\"provided\",\n", - ")" - ] + "Next, to make the plot less crowded, we group the measurements by observables by adding two subplots and specifying which observables to plot on each via the `yValues` column:" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "# Plot 2 axes objects, plotting\n", - "# - in the first window the observable 'pS6_tot' for all simulation conditions\n", - "# - in the second window the observable 'pEGFR_tot' for all simulation conditions\n", - "# - in the third window the observable 'pAkt_tot' for all simulation conditions\n", - "# while using the noise values which are saved in the PEtab files\n", - "\n", - "observable_id_list = [[\"pS6_tot\"], [\"pEGFR_tot\"]]\n", + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_2.tsv\"\n", + ")\n", + "petab_problem.visualization_df" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "petab.visualize.plot_problem(petab_problem)\n", + "plt.gcf().set_size_inches(10, 4)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Subplots by dataset\n", "\n", + "We can also plot different datasets (as specified by the optional `datasetId` column in the measurement table) in separate subplots:" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_fujita\n", + " / \"visuSpecs\"\n", + " / \"Fujita_visuSpec_individual_datasets.tsv\"\n", + ")\n", + "petab_problem.visualization_df" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "petab.visualize.plot_problem(petab_problem)\n", + "plt.gcf().set_size_inches(10, 4)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Legend entries\n", "\n", - "ax = plot_without_vis_spec(\n", - " condition_file_Fujita,\n", - " observable_id_list,\n", - " \"observable\",\n", - " data_file_Fujita,\n", - " plotted_noise=\"provided\",\n", - ")" - ] + "So far, the legend entries don't look very nice. We can change them by specifying the desired labels in the `legendEntries` column:" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_fujita / \"visuSpecs\" / \"Fujita_visuSpec_datasetIds.tsv\"\n", + ")\n", + "petab_problem.visualization_df" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "petab.visualize.plot_problem(petab_problem)\n", + "plt.gcf().set_size_inches(10, 6)" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "Plot only simulations" - ] + "### Plotting individual replicates\n", + "\n", + "If the measurement file contains replicates, the replicates can also be visualized individually by setting the value for `plotTypeData` to `replicate`. Below, you can see the same measurement data plotted as mean and standard deviations (left) and as replicates (right):" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "simu_file_Fujita = \"example_Fujita/Fujita_simulatedData.tsv\"\n", + "petab_problem = Problem.from_yaml(petab_yaml_isensee)\n", + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_isensee / \"Isensee_visualizationSpecification_replicates.tsv\"\n", + ")\n", + "plot_problem(\n", + " petab_problem,\n", + " simulations_df=example_dir_isensee / \"Isensee_simulationData.tsv\",\n", + ")\n", + "plt.gcf().set_size_inches(16, 9)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Scatter plots\n", "\n", - "sim_cond_id_list = [\n", - " [\"model1_data1\"],\n", - " [\"model1_data2\", \"model1_data3\"],\n", - " [\"model1_data4\", \"model1_data5\"],\n", - " [\"model1_data6\"],\n", - "]\n", + "If both measurements and simulated data are available, they can be visualized as scatter plot by setting `plotTypeSimulation` to `ScatterPlot`:" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "petab_problem = Problem.from_yaml(petab_yaml_isensee)\n", + "petab_problem.visualization_df = petab.get_visualization_df(\n", + " example_dir_isensee / \"Isensee_visualizationSpecification_scatterplot.tsv\"\n", + ")\n", + "plot_problem(\n", + " petab_problem,\n", + " simulations_df=example_dir_isensee / \"Isensee_simulationData.tsv\",\n", + ")\n", + "plt.gcf().set_size_inches(10, 4)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Further examples\n", "\n", - "ax = plot_without_vis_spec(\n", - " condition_file_Fujita,\n", - " sim_cond_id_list,\n", - " \"simulation\",\n", - " simulations_df=simu_file_Fujita,\n", - " plotted_noise=\"provided\",\n", - ")" - ] + "Here are some further visualization examples, including barplots (by setting `plotTypeSimulation` to `BarPlot`):" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", "execution_count": null, - "metadata": {}, "outputs": [], "source": [ - "observable_id_list = [[\"pS6_tot\"], [\"pEGFR_tot\"], [\"pAkt_tot\"]]\n", + "petab_problem = petab.Problem.from_yaml(petab_yaml_isensee_vis)\n", + "plot_problem(\n", + " petab_problem,\n", + " simulations_df=example_dir_isensee / \"Isensee_simulationData.tsv\",\n", + ")\n", + "plt.gcf().set_size_inches(20, 12)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Also with a visualization file, there is the option to plot only simulations, only measurements, or both, as was illustrated above in the examples without a visualization file.\n", "\n", - "ax = plot_without_vis_spec(\n", - " condition_file_Fujita,\n", - " observable_id_list,\n", - " \"observable\",\n", - " simulations_df=simu_file_Fujita,\n", - " plotted_noise=\"provided\",\n", - ")" - ] + "Refer to the [PEtab documentation](https://petab.readthedocs.io/en/latest/documentation_data_format.html) for descriptions of all possible settings. If you have any questions or encounter some problems, please create a GitHub [issue](https://github.com/PEtab-dev/libpetab-python/issues). We will be happy to help!" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + } } ], "metadata": { @@ -310,6 +591,15 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" + }, + "pycharm": { + "stem_cell": { + "cell_type": "raw", + "metadata": { + "collapsed": false + }, + "source": [] + } } }, "nbformat": 4, diff --git a/doc/example/example_visualization_with_visspec.ipynb b/doc/example/example_visualization_with_visspec.ipynb deleted file mode 100644 index 403fca1c..00000000 --- a/doc/example/example_visualization_with_visspec.ipynb +++ /dev/null @@ -1,241 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization with visualization specification file\n", - "\n", - "\n", - "As described in the [PEtab documentation](https://petab.readthedocs.io/en/latest/documentation_data_format.html), the visualization specification file (VS) is a tab-separated value file containing the specification of the visualization routines which come with the PEtab repository. In this example, we will discuss how to specify different visualization settings by means of the VS.\n", - "\n", - "Let's plot measurements corresponding to the ones of the models from the Benchmark collection [Fujita_SciSignal2010](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Fujita_SciSignal2010)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import petab\n", - "from petab.visualize import plot_data_and_simulation\n", - "\n", - "folder = \"example_Fujita/\"\n", - "data_file_path = folder + \"Fujita_measurementData.tsv\"\n", - "condition_file_path = folder + \"Fujita_experimentalCondition.tsv\"\n", - "observables_file_path = folder + \"Fujita_observables.tsv\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If no settings are provided, the measurements will be plotted with default settings. Namely, it will be assumed that the independent variable is time, and that data for each observable is to be plotted in a separate subplot. This also applies to the case when VS is empty:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "visualization_file_path = folder + \"/visuSpecs/Fujita_visuSpec_empty.tsv\"\n", - "\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - " visualization_files=visualization_file_path,\n", - ")\n", - "petab.visualize.plot_problem(petab_problem=pp);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First of all, let us create a visualization specification file with only mandatory columns. In fact, there is only one mandatory column: `plotId`.\n", - "Resulting plot using the VS\n", - "\n", - "| plotId |\n", - "|---|\n", - "| plot1 |\n", - "\n", - "This way, all data will be shown in a single plot, taking time as independent variable." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "visualization_file_path = folder + \"/visuSpecs/Fujita_visuSpec_mandatory.tsv\"\n", - "\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - " visualization_files=visualization_file_path,\n", - ")\n", - "\n", - "petab.visualize.plot_problem(petab_problem=pp);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's try adding some settings:\n", - "\n", - "| plotId | xOffset | yScale |\n", - "|---|---|---|\n", - "| plot1 | 100 | log |\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "visualization_file_path = folder + \"/visuSpecs/Fujita_visuSpec_1.tsv\"\n", - "\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - " visualization_files=visualization_file_path,\n", - ")\n", - "\n", - "petab.visualize.plot_problem(petab_problem=pp);" - ] - }, - { - "cell_type": "markdown", - "source": [ - "As you can see, the scale of the y-axis changed to a logarithmic one and an offset for the independent variable is set to 100.\n", - "\n", - "You can group measurements by observables by adding `yValues` column:\n", - "\n", - "| plotId | yValues | yOffset | yScale | plotName |\n", - "|---|---|---|---|---|\n", - "| plot1 | pEGFR_tot | 0 | log | pEGFR total |\n", - "| plot2 | pAkt_tot | 300 | lin | pAkt total and pS6 total|\n", - "| plot2 | pS6_tot | 305 | lin | pAkt total and pS6 total |\n" - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "visualization_file_path = folder + \"/visuSpecs/Fujita_visuSpec_2.tsv\"\n", - "\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - " visualization_files=visualization_file_path,\n", - ")\n", - "\n", - "petab.visualize.plot_problem(petab_problem=pp);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also plot individual datasets:\n", - "\n", - "| plotId |\tplotTypeSimulation | plotTypeData |\tdatasetId |\txValues |\n", - "|---|---|---|---|---|\n", - "| plot1\t| LinePlot | provided | model1_data1_pEGFR_tot | time |\n", - "| plot2\t| LinePlot | provided | model1_data2_pEGFR_tot | time |\n", - "| plot2\t| LinePlot | provided | model1_data3_pEGFR_tot | time |\n", - "| plot3\t| LinePlot | provided | model1_data4_pEGFR_tot | time |\n", - "| plot3\t| LinePlot | provided | model1_data5_pEGFR_tot | time |\n", - "| plot3\t| LinePlot | provided | model1_data6_pEGFR_tot | time |" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "visualization_file_path = (\n", - " folder + \"/visuSpecs/Fujita_visuSpec_individual_datasets.tsv\"\n", - ")\n", - "\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - " visualization_files=visualization_file_path,\n", - ")\n", - "\n", - "petab.visualize.plot_problem(petab_problem=pp);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "visualization_file_path = folder + \"/visuSpecs/Fujita_visuSpec_datasetIds.tsv\"\n", - "\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - " visualization_files=visualization_file_path,\n", - ")\n", - "\n", - "petab.visualize.plot_problem(petab_problem=pp);" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "As you can see, with the VS file you have an opportunity to plot each dataset individually or groups of datasets. Refer to the [PEtab documentation](https://petab.readthedocs.io/en/latest/documentation_data_format.html) for descriptions of all possible settings. If you have any questions or encounter some problems, please create an issue. We will be happy to help!" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.9" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/doc/example/example_visualization_without_visspec.ipynb b/doc/example/example_visualization_without_visspec.ipynb deleted file mode 100644 index 5bbbbeb0..00000000 --- a/doc/example/example_visualization_without_visspec.ipynb +++ /dev/null @@ -1,188 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualization without visualization specification file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this example let us consider some visualisation possibilities of PEtab. In particluar, let us focus on the options that do not require detailed specifications. If you want to have more control over what will be plotted, refer to this [example](https://github.com/PEtab-dev/PEtab/blob/pl_visualization/doc/example/example_visualization_with_visspec.ipynb), describing usage of the visualization table.\n", - "\n", - "Let's plot measurements corresponding to the ones of the models from the Benchmark collection [Fujita_SciSignal2010](https://github.com/Benchmarking-Initiative/Benchmark-Models-PEtab/tree/master/Benchmark-Models/Fujita_SciSignal2010)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import petab\n", - "from petab.visualize import plot_data_and_simulation\n", - "\n", - "folder = \"example_Fujita/\"\n", - "data_file_path = folder + \"Fujita_measurementData.tsv\"\n", - "condition_file_path = folder + \"Fujita_experimentalCondition.tsv\"\n", - "observables_file_path = folder + \"Fujita_observables.tsv\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plotting PEtab problem directly\n", - "For plotting a PEtab problem directly you can use the `plot_problem()` function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# load PEtab problem from files\n", - "pp = petab.Problem.from_files(\n", - " measurement_file=data_file_path,\n", - " condition_file=condition_file_path,\n", - " observable_files=observables_file_path,\n", - ")\n", - "# Alternatively, from yaml file\n", - "# pp = petab.Problem.from_yaml(folder + \"Fujita.yaml\")\n", - "\n", - "# plot measurements\n", - "ax = petab.visualize.plot_problem(petab_problem=pp)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As nothing was specified regarding what should be plotted, the defaults were used. Namely, it was assumed that measurements are time series data, and they were grouped by observables.\n", - "\n", - "You can specify how many subplot there should be and what should be plotted on each of them. It can easily be done by providing `grouping_list`, which by default specifies, which observables should be plotted on a paricular plot. The value of `grouping_list` should be a list of lists, each sublist corresponds to a separate plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ax = petab.visualize.plot_problem(\n", - " petab_problem=pp, grouping_list=[[\"pEGFR_tot\"], [\"pAkt_tot\", \"pS6_tot\"]]\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another option is to specify which simulation conditions should be plotted." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ax = petab.visualize.plot_problem(\n", - " petab_problem=pp,\n", - " grouping_list=[\n", - " [\"model1_data1\"],\n", - " [\"model1_data2\", \"model1_data3\"],\n", - " [\"model1_data4\", \"model1_data5\", \"model1_data6\"],\n", - " ],\n", - " group_by=\"simulation\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, measurements can be grouped by datasetIds. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ax = petab.visualize.plot_problem(\n", - " petab_problem=pp,\n", - " grouping_list=[\n", - " [\n", - " \"model1_data1_pEGFR_tot\",\n", - " \"model1_data2_pEGFR_tot\",\n", - " \"model1_data3_pEGFR_tot\",\n", - " \"model1_data4_pEGFR_tot\",\n", - " \"model1_data5_pEGFR_tot\",\n", - " \"model1_data6_pEGFR_tot\",\n", - " ],\n", - " [\n", - " \"model1_data1_pAkt_tot\",\n", - " \"model1_data2_pAkt_tot\",\n", - " \"model1_data3_pAkt_tot\",\n", - " \"model1_data4_pAkt_tot\",\n", - " \"model1_data5_pAkt_tot\",\n", - " \"model1_data6_pAkt_tot\",\n", - " ],\n", - " [\n", - " \"model1_data1_pS6_tot\",\n", - " \"model1_data2_pS6_tot\",\n", - " \"model1_data3_pS6_tot\",\n", - " \"model1_data4_pS6_tot\",\n", - " \"model1_data5_pS6_tot\",\n", - " \"model1_data6_pS6_tot\",\n", - " ],\n", - " ],\n", - " group_by=\"dataset\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To summarize, measurements can be grouped by observables, simulation conditions and datasets with the `plot_problem()` function. This can be specified by setting the value of `group_by` parameter to `'observable'` (default), `'simulation'` or `'dataset'` and by providing corresponding ids in `grouping_list`, which is a list of lists. Each sublist specifies a separate plot and its elements are either simulation condition IDs or observable IDs or the dataset IDs." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - }, - "pycharm": { - "stem_cell": { - "cell_type": "raw", - "metadata": { - "collapsed": false - }, - "source": [] - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/petab/core.py b/petab/core.py index 05deb161..0e7b7dad 100644 --- a/petab/core.py +++ b/petab/core.py @@ -71,7 +71,7 @@ def write_simulation_df(df: pd.DataFrame, filename: Union[str, Path]) -> None: def get_visualization_df( - visualization_file: Union[str, Path, pd.DataFrame, None] + visualization_file: Union[str, Path, pd.DataFrame, None] ) -> Union[pd.DataFrame, None]: """Read PEtab visualization table @@ -254,7 +254,7 @@ def flatten_timepoint_specific_output_overrides( Arguments: petab_problem: - PEtab problem to work on + PEtab problem to work on. Modified in place. """ new_measurement_dfs = [] new_observable_dfs = [] @@ -277,22 +277,21 @@ def flatten_timepoint_specific_output_overrides( for field, hyperparameter_type, target in [ (NOISE_PARAMETERS, "noiseParameter", NOISE_FORMULA), (OBSERVABLE_PARAMETERS, "observableParameter", OBSERVABLE_FORMULA), + (OBSERVABLE_PARAMETERS, "observableParameter", NOISE_FORMULA), ]: - if field in measurements: - hyperparameter_replacement_id = ( - get_hyperparameter_replacement_id( - hyperparameter_type=hyperparameter_type, - observable_replacement_id=observable_replacement_id, - ) - ) - hyperparameter_id = mappings[field][ - hyperparameter_replacement_id - ] - observable[target] = re.sub( - hyperparameter_id, - hyperparameter_replacement_id, - observable[target], - ) + if field not in measurements: + continue + + hyperparameter_replacement_id = get_hyperparameter_replacement_id( + hyperparameter_type=hyperparameter_type, + observable_replacement_id=observable_replacement_id, + ) + hyperparameter_id = mappings[field][hyperparameter_replacement_id] + observable[target] = re.sub( + hyperparameter_id, + hyperparameter_replacement_id, + observable[target], + ) measurements[OBSERVABLE_ID] = observable_replacement_id new_measurement_dfs.append(measurements) @@ -306,7 +305,7 @@ def flatten_timepoint_specific_output_overrides( def unflatten_simulation_df( simulation_df: pd.DataFrame, petab_problem: "petab.problem.Problem", -) -> None: +) -> pd.DataFrame: """Unflatten simulations from a flattened PEtab problem. A flattened PEtab problem is the output of applying diff --git a/petab/version.py b/petab/version.py index 8a232560..e999c286 100644 --- a/petab/version.py +++ b/petab/version.py @@ -1,2 +1,2 @@ """PEtab library version""" -__version__ = "0.2.5" +__version__ = "0.2.6" diff --git a/petab/visualize/plotter.py b/petab/visualize/plotter.py index 90cab85e..b6ea702a 100644 --- a/petab/visualize/plotter.py +++ b/petab/visualize/plotter.py @@ -123,12 +123,23 @@ def generate_lineplot( replicates = np.stack( measurements_to_plot.data_to_plot.repl.values ) + # sorts according to ascending order of conditions + cond, replicates = zip( + *sorted( + zip( + measurements_to_plot.conditions, + replicates + ) + ) + ) + replicates = np.stack(replicates) + if replicates.ndim == 1: replicates = np.expand_dims(replicates, axis=1) # plot first replicate p = ax.plot( - measurements_to_plot.conditions, + cond, replicates[:, 0], linestyle="-.", marker="x", @@ -138,7 +149,7 @@ def generate_lineplot( # plot other replicates with the same color ax.plot( - measurements_to_plot.conditions, + cond, replicates[:, 1:], linestyle="-.", marker="x", diff --git a/petab/visualize/plotting.py b/petab/visualize/plotting.py index 2990f528..c257387f 100644 --- a/petab/visualize/plotting.py +++ b/petab/visualize/plotting.py @@ -29,7 +29,7 @@ NumList = List[int] # The default figure size -DEFAULT_FIGSIZE = (20, 15) +DEFAULT_FIGSIZE = [20, 15] # also for type hints # TODO: split into dataplot and subplot level dicts? diff --git a/tests/test_petab.py b/tests/test_petab.py index 89053fb4..b8368805 100644 --- a/tests/test_petab.py +++ b/tests/test_petab.py @@ -353,36 +353,32 @@ def test_flatten_timepoint_specific_output_overrides(): OBSERVABLE_FORMULA: [ "observableParameter1_obs1 + observableParameter2_obs1" ], - NOISE_FORMULA: ["noiseParameter1_obs1"], + NOISE_FORMULA: [ + "(observableParameter1_obs1 + observableParameter2_obs1) * noiseParameter1_obs1" + ], } ) observable_df.set_index(OBSERVABLE_ID, inplace=True) + # new observable IDs (obs${i_obs}_${i_obsParOverride}_${i_noiseParOverride}_${i_condition}) + obs1_1_1_1 = "obs1__obsParOverride1_1_0__noiseParOverride1__condition1" + obs1_2_1_1 = "obs1__obsParOverride2_1_0__noiseParOverride1__condition1" + obs1_2_2_1 = "obs1__obsParOverride2_1_0__noiseParOverride2__condition1" observable_df_expected = pd.DataFrame( data={ - OBSERVABLE_ID: [ - "obs1__obsParOverride1_1_0__noiseParOverride1__condition1", - "obs1__obsParOverride2_1_0__noiseParOverride1__condition1", - "obs1__obsParOverride2_1_0__noiseParOverride2__condition1", - ], + OBSERVABLE_ID: [obs1_1_1_1, obs1_2_1_1, obs1_2_2_1], OBSERVABLE_FORMULA: [ - "observableParameter1_obs1__obsParOverride1_1_0__" - "noiseParOverride1__condition1 + observableParameter2_obs1" - "__obsParOverride1_1_0__noiseParOverride1__condition1", - "observableParameter1_obs1__obsParOverride2_1_0__noiseParOverride1" - "__condition1 + observableParameter2_obs1__obsParOverride2_1_0" - "__noiseParOverride1__condition1", - "observableParameter1_obs1__obsParOverride2_1_0" - "__noiseParOverride2__condition1 + observableParameter2_obs1__" - "obsParOverride2_1_0__noiseParOverride2__condition1", + f"observableParameter1_{obs1_1_1_1} + observableParameter2_{obs1_1_1_1}", + f"observableParameter1_{obs1_2_1_1} + observableParameter2_{obs1_2_1_1}", + f"observableParameter1_{obs1_2_2_1} + observableParameter2_{obs1_2_2_1}", ], NOISE_FORMULA: [ - "noiseParameter1_obs1__obsParOverride1_1_0__" - "noiseParOverride1__condition1", - "noiseParameter1_obs1__obsParOverride2_1_0__" - "noiseParOverride1__condition1", - "noiseParameter1_obs1__obsParOverride2_1_0__" - "noiseParOverride2__condition1", + f"(observableParameter1_{obs1_1_1_1} + observableParameter2_{obs1_1_1_1})" + f" * noiseParameter1_{obs1_1_1_1}", + f"(observableParameter1_{obs1_2_1_1} + observableParameter2_{obs1_2_1_1})" + f" * noiseParameter1_{obs1_2_1_1}", + f"(observableParameter1_{obs1_2_2_1} + observableParameter2_{obs1_2_2_1})" + f" * noiseParameter1_{obs1_2_2_1}", ], } ) @@ -418,12 +414,7 @@ def test_flatten_timepoint_specific_output_overrides(): measurement_df_expected = pd.DataFrame( data={ - OBSERVABLE_ID: [ - "obs1__obsParOverride1_1_0__noiseParOverride1__condition1", - "obs1__obsParOverride2_1_0__noiseParOverride1__condition1", - "obs1__obsParOverride2_1_0__noiseParOverride2__condition1", - "obs1__obsParOverride2_1_0__noiseParOverride2__condition1", - ], + OBSERVABLE_ID: [obs1_1_1_1, obs1_2_1_1, obs1_2_2_1, obs1_2_2_1], SIMULATION_CONDITION_ID: [ "condition1", "condition1", @@ -472,8 +463,12 @@ def test_flatten_timepoint_specific_output_overrides(): is False ) - assert problem.observable_df.equals(observable_df_expected) is True - assert problem.measurement_df.equals(measurement_df_expected) is True + pd.testing.assert_frame_equal( + problem.observable_df, observable_df_expected + ) + pd.testing.assert_frame_equal( + problem.measurement_df, measurement_df_expected + ) assert petab.lint_problem(problem) is False @@ -591,8 +586,12 @@ def test_flatten_timepoint_specific_output_overrides_special_cases(): is False ) - assert problem.observable_df.equals(observable_df_expected) is True - assert problem.measurement_df.equals(measurement_df_expected) is True + pd.testing.assert_frame_equal( + problem.observable_df, observable_df_expected + ) + pd.testing.assert_frame_equal( + problem.measurement_df, measurement_df_expected + ) assert petab.lint_problem(problem) is False @@ -842,13 +841,14 @@ def test_get_required_parameters_for_parameter_table(petab_problem): # as part of the proportional error model. assert "observableParameter1_obs1" in noise_placeholders - required_parameters_for_parameter_table = \ + 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 (