def test_normalize_parameter_df(): """Check parameters.normalize_parameter_df.""" parameter_df = pd.DataFrame({ PARAMETER_ID: ['par0', 'par1', 'par2'], PARAMETER_SCALE: [LOG10, LOG10, LIN], NOMINAL_VALUE: [1e-2, 1e-3, 1e-4], ESTIMATE: [1, 1, 0], LOWER_BOUND: [1e-5, 1e-6, 1e-7], UPPER_BOUND: [1e5, 1e6, 1e7] }).set_index(PARAMETER_ID) actual = petab.normalize_parameter_df(parameter_df) expected = parameter_df.copy(deep=True) expected[PARAMETER_NAME] = parameter_df.reset_index()[PARAMETER_ID] expected[INITIALIZATION_PRIOR_TYPE] = [PARAMETER_SCALE_UNIFORM] * 3 expected[INITIALIZATION_PRIOR_PARAMETERS] = ["-5;5", "-6;6", "1e-7;1e7"] expected[OBJECTIVE_PRIOR_TYPE] = [PARAMETER_SCALE_UNIFORM] * 3 expected[OBJECTIVE_PRIOR_PARAMETERS] = ["-5;5", "-6;6", "1e-7;1e7"] # check ids assert list(actual.index.values) == list(expected.index.values) # check if basic columns match for col in PARAMETER_DF_COLS[1:]: if col in [INITIALIZATION_PRIOR_PARAMETERS, OBJECTIVE_PRIOR_PARAMETERS]: continue assert ((actual[col] == expected[col]) | (actual[col].isnull() == expected[col].isnull())).all() # check if prior parameters match for col in [INITIALIZATION_PRIOR_PARAMETERS, OBJECTIVE_PRIOR_PARAMETERS]: for (_, actual_row), (_, expected_row) in \ zip(actual.iterrows(), expected.iterrows()): actual_pars = tuple([float(val) for val in actual_row[col].split(';')]) expected_pars = tuple([float(val) for val in expected_row[col].split(';')]) assert actual_pars == expected_pars # check is a projection actual2 = petab.normalize_parameter_df(actual) assert ((actual == actual2) | (actual.isnull() == actual2.isnull())) \ .all().all() # check is valid petab petab.check_parameter_df(actual)
def get_scales(parameter_df: pd.DataFrame) -> Tuple[dict, dict]: """Unravel whether the priors and evaluations are on or off scale. Only the `parameterScale...` priors are on-scale, the other priors are on linear scale. Parameters ---------- parameter_df: The PEtab parameter data frame. Returns ------- prior_scales, scaled_scales: Scales for each parameter on prior and evaluation level. """ # fill in objective prior columns parameter_df = petab.normalize_parameter_df(parameter_df) prior_scales = {} scaled_scales = {} for _, row in parameter_df.reset_index().iterrows(): if row[C.ESTIMATE] == 0: continue prior_scales[row[C.PARAMETER_ID]] = ( row[C.PARAMETER_SCALE] if row[C.OBJECTIVE_PRIOR_TYPE] in SCALED_PRIORS else C.LIN ) scaled_scales[row[C.PARAMETER_ID]] = row[C.PARAMETER_SCALE] return prior_scales, scaled_scales
def get_parameter_names(self, target_scale: str = 'prior'): """Get meaningful parameter names, corrected for target scale.""" parameter_df = petab.normalize_parameter_df( self.petab_problem.parameter_df ) # scale if target_scale == C.LIN: target_scales = {key: C.LIN for key in self.prior_scales} elif target_scale == 'prior': target_scales = self.prior_scales elif target_scale == 'scaled': target_scales = self.scaled_scales else: raise ValueError(f"Did not recognize target scale {target_scale}") names = {} for _, row in parameter_df.reset_index().iterrows(): if row[C.ESTIMATE] == 0: continue key = row[C.PARAMETER_ID] name = str(key) if C.PARAMETER_NAME in parameter_df: if not petab.is_empty(row[C.PARAMETER_NAME]): name = str(row[C.PARAMETER_NAME]) target_scale = target_scales[key] if target_scale != C.LIN: # mini check whether the name might indicate the scale already if not name.startswith("log"): name = target_scale + "(" + name + ")" names[key] = name return names
def create_prior(self) -> pyabc.Distribution: """ Create prior. Returns ------- prior: A valid pyabc.Distribution for the parameters to estimate. """ # add default values parameter_df = petab.normalize_parameter_df( self.petab_problem.parameter_df) prior_dct = {} # iterate over parameters for _, row in parameter_df.reset_index().iterrows(): # check whether we can ignore if not self.fixed_parameters and row[petab.C.ESTIMATE] == 0: # ignore fixed parameters continue if not self.free_parameters and row[petab.C.ESTIMATE] == 1: # ignore free parameters continue # pyabc currently only knows objective priors, no # initialization priors prior_type = row[petab.C.OBJECTIVE_PRIOR_TYPE] pars_str = row[petab.C.OBJECTIVE_PRIOR_PARAMETERS] prior_pars = tuple([float(val) for val in pars_str.split(';')]) # create random variable from table entry if prior_type in [ petab.C.PARAMETER_SCALE_UNIFORM, petab.C.UNIFORM ]: lb, ub = prior_pars rv = pyabc.RV('uniform', lb, ub - lb) elif prior_type in [ petab.C.PARAMETER_SCALE_NORMAL, petab.C.NORMAL ]: mean, std = prior_pars rv = pyabc.RV('norm', mean, std) elif prior_type in [ petab.C.PARAMETER_SCALE_LAPLACE, petab.C.LAPLACE ]: mean, scale = prior_pars rv = pyabc.RV('laplace', mean, scale) elif prior_type == petab.C.LOG_NORMAL: mean, std = prior_pars rv = pyabc.RV('lognorm', mean, std) elif prior_type == petab.C.LOG_LAPLACE: mean, scale = prior_pars rv = pyabc.RV('loglaplace', mean, scale) else: raise ValueError(f"Cannot handle prior type {prior_type}.") prior_dct[row[petab.C.PARAMETER_ID]] = rv # create prior distribution prior = pyabc.Distribution(**prior_dct) return prior
def get_bounds( parameter_df: pd.DataFrame, target_scale: str, prior_scales: dict, scaled_scales: dict, use_prior: bool, ) -> dict: """Get bounds. Parameters ---------- parameter_df: The PEtab parameter data frame. target_scale: Scale to get the nominal parameters on. Can be 'lin', 'scaled', or 'prior'. prior_scales: Prior scales. scaled_scales: On-scale scales. use_prior: Whether to use prior overrides if of type uniform. Returns ------- bounds: Dictionary with a (lower, upper) tuple per parameter. """ parameter_df = petab.normalize_parameter_df(parameter_df) # scale if target_scale == C.LIN: target_scales = {key: C.LIN for key in prior_scales} elif target_scale == 'prior': target_scales = prior_scales elif target_scale == 'scaled': target_scales = scaled_scales else: raise ValueError(f"Did not recognize target scale {target_scale}") # extract bounds bounds = {} for _, row in parameter_df.reset_index().iterrows(): if row[C.ESTIMATE] == 0: # ignore fixed parameters continue key = row[C.PARAMETER_ID] # from lower and upper bound lower, upper = row[C.LOWER_BOUND], row[C.UPPER_BOUND] origin_scale = C.LIN # from prior prior_type = row[C.OBJECTIVE_PRIOR_TYPE] if use_prior and prior_type in [C.UNIFORM, C.PARAMETER_SCALE_UNIFORM]: pars_str = row[C.OBJECTIVE_PRIOR_PARAMETERS] lower, upper = tuple(float(val) for val in pars_str.split(';')) if prior_type == C.PARAMETER_SCALE_UNIFORM: origin_scale = row[C.PARAMETER_SCALE] # convert to target scale lower = rescale( lower, origin_scale=origin_scale, target_scale=target_scales[key] ) upper = rescale( upper, origin_scale=origin_scale, target_scale=target_scales[key] ) bounds[key] = (lower, upper) return bounds
def create_prior(parameter_df: pd.DataFrame) -> pyabc.Distribution: """Create prior. Note: The prior generates samples according to `OBJECTIVE_PRIOR_TYPE` and `OBJECTIVE_PRIOR_PARAMETERS`. These samples are only scaled if the prior type is `PARAMETER_SCALE_...`, otherwise unscaled. The model has to take care of converting samples accordingly. Parameters ---------- parameter_df: The PEtab parameter data frame. Returns ------- prior: A valid pyabc.Distribution for the parameters to estimate. """ # add default values parameter_df = petab.normalize_parameter_df(parameter_df) prior_dct = {} # iterate over parameters for _, row in parameter_df.reset_index().iterrows(): if row[C.ESTIMATE] == 0: # ignore fixed parameters continue # pyabc currently only knows objective priors, no # initialization priors prior_type = row[C.OBJECTIVE_PRIOR_TYPE] pars_str = row[C.OBJECTIVE_PRIOR_PARAMETERS] prior_pars = tuple(float(val) for val in pars_str.split(';')) # create random variable from table entry if prior_type in [C.PARAMETER_SCALE_UNIFORM, C.UNIFORM]: lb, ub = prior_pars # scipy pars are location, width rv = pyabc.RV('uniform', loc=lb, scale=ub - lb) elif prior_type in [C.PARAMETER_SCALE_NORMAL, C.NORMAL]: mean, std = prior_pars # scipy pars are mean, std rv = pyabc.RV('norm', loc=mean, scale=std) elif prior_type in [C.PARAMETER_SCALE_LAPLACE, C.LAPLACE]: mean, b = prior_pars # scipy pars are loc=mean, scale=b rv = pyabc.RV('laplace', loc=mean, scale=b) elif prior_type == C.LOG_NORMAL: mean, std = prior_pars # petab pars are mean, std of the underlying normal distribution # scipy pars are s, loc, scale where s = std, scale = exp(mean) # as a simple calculation shows rv = pyabc.RV('lognorm', s=std, loc=0, scale=np.exp(mean)) elif prior_type == C.LOG_LAPLACE: mean, b = prior_pars # petab pars are mean, b of the underlying laplace distribution # scipy pars are c, loc, scale where c = 1 / b, scale = exp(mean) # as a simple calculation shows rv = pyabc.RV('loglaplace', c=1 / b, scale=np.exp(mean)) else: raise ValueError(f"Cannot handle prior type {prior_type}.") prior_dct[row[C.PARAMETER_ID]] = rv # create prior distribution prior = pyabc.Distribution(**prior_dct) return prior