Skip to content
Browse files

Support for simulation initials, params as DataFrames (#416)

* Support for simulation initials, params as DataFrames

Add support for supplying simulation initial conditions and parameter
values as pandas DataFrames. Previously this was handled by implict
conversion to numpy array, which might lose the column order (i.e.,
initials species or parameters get swapped).

Users must now supply a DataFrame, numpy array, or list
for initials and param_values.
  • Loading branch information...
alubbock committed Feb 20, 2019
1 parent 75355a5 commit 967b986ba6fa121b9196988253b384ccbf2d8141
Showing with 50 additions and 6 deletions.
  1. +29 −6 pysb/simulator/
  2. +21 −0 pysb/tests/
@@ -330,6 +330,14 @@ def _process_incoming_initials(self, new_initials):
if new_initials is None:
return None

# If new_initials is a pandas dataframe, convert to a dict
if pd and isinstance(new_initials, pd.DataFrame):
new_initials = new_initials.to_dict(orient='list')

# If new_initials is a list, convert to numpy array
if isinstance(new_initials, list):
new_initials = np.array(new_initials, copy=False)

# Check if new_initials is a dict, and if so validate the keys
# (ComplexPatterns)
if isinstance(new_initials, dict):
@@ -354,9 +362,7 @@ def _process_incoming_initials(self, new_initials):
if not np.isfinite(val).all():
raise ValueError('Please check initial {} for non-finite '
if not isinstance(new_initials, np.ndarray):
new_initials = np.array(new_initials, copy=False)
elif isinstance(new_initials, np.ndarray):
# if new_initials is a 1D array, convert to a 2D array of length 1
if len(new_initials.shape) == 1:
new_initials = np.resize(new_initials, (1, len(new_initials)))
@@ -368,6 +374,11 @@ def _process_incoming_initials(self, new_initials):
if not np.isfinite(new_initials).all():
raise ValueError('Please check initials array '
'for non-finite values')
raise ValueError(
'Implicit conversion of data type "{}" is not '
'supported. Please supply initials as a numpy array, list, '
'or a pandas DataFrame.'.format(type(new_initials)))

if n_sims > 1:
if not self._supports['multi_initials']:
@@ -451,6 +462,15 @@ def param_values(self, new_params):
def _process_incoming_params(self, new_params):
if new_params is None:
return None

# Convert pandas dataframe to dictionary
if pd and isinstance(new_params, pd.DataFrame):
new_params = new_params.to_dict(orient='list')

# If new_params is a list, convert to numpy array
if isinstance(new_params, list):
new_params = np.array(new_params)

if isinstance(new_params, dict):
n_sims = 1
if len(new_params) > 0:
@@ -467,9 +487,7 @@ def _process_incoming_params(self, new_params):
if len(val) != n_sims:
raise ValueError("all arrays in params dictionary "
"must be equal length")
if not isinstance(new_params, np.ndarray):
new_params = np.array(new_params)
elif isinstance(new_params, np.ndarray):
# if new_params is a 1D array, convert to a 2D array of length 1
if len(new_params.shape) == 1:
new_params = np.resize(new_params, (1, len(new_params)))
@@ -478,6 +496,11 @@ def _process_incoming_params(self, new_params):
if new_params.shape[1] != len(self._model.parameters):
raise ValueError("new_params must be the same length as "
raise ValueError(
'Implicit conversion of data type "{}" is not '
'supported. Please supply parameters as a numpy array, list, '
'or a pandas DataFrame.'.format(type(new_params)))

# Check whether simulator supports multiple param_values
if n_sims > 1 and not self._supports['multi_param_values']:
@@ -6,6 +6,7 @@
from pysb.simulator import ScipyOdeSimulator
from pysb.examples import robertson, earm_1_0
import unittest
import pandas as pd

class TestScipySimulatorBase(object):
@@ -112,6 +113,18 @@ def test_y0_as_dictionary_with_bound_species(self):
self.mon('B')(b=None): 0})
assert np.allclose(simres.observables['AB_complex'][0], 100)

def test_y0_as_dataframe(self):
initials_dict = {self.mon('A')(a=None): [0],
self.mon('B')(b=1) % self.mon('A')(a=1): [100],
self.mon('B')(b=None): [0]}
initials_df = pd.DataFrame(initials_dict)
simres =
assert np.allclose(simres.observables['AB_complex'][0], 100)

def test_y0_as_pandas_series(self):

def test_y0_non_numeric_value(self):
"""Test y0 with non-numeric value."""
@@ -123,6 +136,14 @@ def test_param_values_as_dictionary(self):
# kbindAB=0 should ensure no AB_complex is produced.
assert np.allclose(simres.observables["AB_complex"], 0)

def test_param_values_as_dataframe(self):
simres ={'kbindAB': [0]}))
assert np.allclose(simres.observables['AB_complex'], 0)

def test_param_values_as_pandas_series(self):

def test_param_values_as_list_ndarray(self):
"""Test param_values as a list and ndarray."""
orig_param_values = self.sim.param_values

0 comments on commit 967b986

Please sign in to comment.
You can’t perform that action at this time.