def boxcox(cls, no_of_etas): assignments = ModelStatements() for i in range(1, no_of_etas + 1): symbol = S(f'etab{i}') expression = (exp(S(f'eta{i}')) ** S(f'theta{i}') - 1) / (S(f'theta{i}')) assignment = Assignment(symbol, expression) assignments.append(assignment) return cls('boxcox', assignments, 'lambda')
def test_reassign(): s1 = Assignment(S('G'), sympy.Integer(3)) s2 = Assignment(S('M'), sympy.Integer(2)) s3 = Assignment(S('Z'), sympy.Integer(23) + S('M')) s4 = Assignment(S('KA'), S('X') + S('Y')) s = ModelStatements([s1, s2, s3, s4]) s.reassign(S('M'), S('x') + S('y')) assert s == ModelStatements([s1, Assignment('M', S('x') + S('y')), s3, s4]) s5 = Assignment('KA', S('KA') + S('Q') + 1) s = ModelStatements([s1, s2, s3, s4, s5]) s.reassign(S('KA'), S('F')) assert s == ModelStatements([s1, s2, s3, Assignment('KA', S('F'))])
def john_draper(cls, no_of_etas): assignments = ModelStatements() for i in range(1, no_of_etas + 1): symbol = S(f'etad{i}') eta = S(f'eta{i}') theta = S(f'theta{i}') expression = sign(eta) * (((abs(eta) + 1) ** theta - 1) / theta) assignment = Assignment(symbol, expression) assignments.append(assignment) return cls('johndraper', assignments, 'lambda')
def test_add_parameters_and_statements(pheno_path, param_new, statement_new, buf_new): model = Model(pheno_path) pset = model.parameters pset.append(param_new) model.parameters = pset sset = model.statements # Insert new statement before ODE system. new_sset = ModelStatements() for s in sset: if isinstance(s, ODESystem): new_sset.append(statement_new) new_sset.append(s) model.statements = new_sset model.update_source() rec = ( f'$PK\n' f'IF(AMT.GT.0) BTIME=TIME\n' f'TAD=TIME-BTIME\n' f'TVCL=THETA(1)*WGT\n' f'TVV=THETA(2)*WGT\n' f'IF(APGR.LT.5) TVV=TVV*(1+THETA(3))\n' f'CL=TVCL*EXP(ETA(1))\n' f'V=TVV*EXP(ETA(2))\n' f'S1=V\n' f'{buf_new}\n\n' ) assert str(model.get_pred_pk_record()) == rec
def tdist(cls, no_of_etas): assignments = ModelStatements() for i in range(1, no_of_etas + 1): symbol = S(f'etat{i}') eta = S(f'eta{i}') theta = S(f'theta{i}') num_1 = eta ** 2 + 1 denom_1 = 4 * theta num_2 = (5 * eta ** 4) + (16 * eta ** 2 + 3) denom_2 = 96 * theta ** 2 num_3 = (3 * eta ** 6) + (19 * eta ** 4) + (17 * eta ** 2) - 15 denom_3 = 384 * theta ** 3 expression = eta * (1 + (num_1 / denom_1) + (num_2 / denom_2) + (num_3 / denom_3)) assignment = Assignment(symbol, expression) assignments.append(assignment) return cls('tdist', assignments, 'df')
def test_add_statements(pheno_path, statement_new, buf_new): model = Model(pheno_path) sset = model.statements assert len(sset) == 15 # Insert new statement before ODE system. new_sset = ModelStatements() for s in sset: if isinstance(s, ODESystem): new_sset.append(statement_new) new_sset.append(s) model.statements = new_sset model.update_source() assert len(model.statements) == 16 parser = NMTranParser() stream = parser.parse(str(model)) assert str(model.control_stream) == str(stream) rec_ref = ( f'$PK\n' f'IF(AMT.GT.0) BTIME=TIME\n' f'TAD=TIME-BTIME\n' f'TVCL=THETA(1)*WGT\n' f'TVV=THETA(2)*WGT\n' f'IF(APGR.LT.5) TVV=TVV*(1+THETA(3))\n' f'CL=TVCL*EXP(ETA(1))\n' f'V=TVV*EXP(ETA(2))\n' f'S1=V\n' f'{buf_new}\n\n' ) rec_mod = str(model.control_stream.get_records('PK')[0]) assert rec_ref == rec_mod
def _assign_statements(self): s = [] self.nodes = [] for statement in self.root.all('statement'): for node in statement.children: if node.rule == 'assignment': name = str(node.variable).upper() expr = ExpressionInterpreter().visit(node.expression) ass = Assignment(name, expr) s.append(ass) self.nodes.append(statement) elif node.rule == 'logical_if': logic_expr = ExpressionInterpreter().visit( node.logical_expression) try: assignment = node.assignment except NoSuchRuleException: pass else: name = str(assignment.variable).upper() expr = ExpressionInterpreter().visit( assignment.expression) # Check if symbol was previously declared else_val = sympy.Integer(0) for prevass in s: if prevass.symbol.name == name: else_val = sympy.Symbol(name) break pw = sympy.Piecewise((expr, logic_expr), (else_val, True)) ass = Assignment(name, pw) s.append(ass) self.nodes.append(statement) elif node.rule == 'block_if': interpreter = ExpressionInterpreter() blocks = [] # [(logic, [(symb1, expr1), ...]), ...] symbols = OrderedSet() first_logic = interpreter.visit( node.block_if_start.logical_expression) first_block = node.block_if_start first_symb_exprs = [] for ifstat in first_block.all('statement'): for assign_node in ifstat.all('assignment'): name = str(assign_node.variable).upper() first_symb_exprs.append( (name, interpreter.visit(assign_node.expression))) symbols.add(name) blocks.append((first_logic, first_symb_exprs)) else_if_blocks = node.all('block_if_elseif') for elseif in else_if_blocks: logic = interpreter.visit(elseif.logical_expression) elseif_symb_exprs = [] for elseifstat in elseif.all('statement'): for assign_node in elseifstat.all('assignment'): name = str(assign_node.variable).upper() elseif_symb_exprs.append( (name, interpreter.visit( assign_node.expression))) symbols.add(name) blocks.append((logic, elseif_symb_exprs)) else_block = node.find('block_if_else') if else_block: else_symb_exprs = [] for elsestat in else_block.all('statement'): for assign_node in elsestat.all('assignment'): name = str(assign_node.variable).upper() else_symb_exprs.append( (name, interpreter.visit( assign_node.expression))) symbols.add(name) piecewise_logic = True if len(blocks[0][1]) == 0 and not else_if_blocks: # Special case for empty if piecewise_logic = sympy.Not(blocks[0][0]) blocks.append((piecewise_logic, else_symb_exprs)) for symbol in symbols: pairs = [] for block in blocks: logic = block[0] for cursymb, expr in block[1]: if cursymb == symbol: pairs.append((expr, logic)) pw = sympy.Piecewise(*pairs) ass = Assignment(symbol, pw) s.append(ass) self.nodes.append(statement) statements = ModelStatements(s) return statements
def test_remove_symbol_definition(): s1 = Assignment(S('KA'), S('X') + S('Y')) s2 = Assignment(S('Z'), sympy.Integer(23) + S('M')) s3 = Assignment(S('M'), sympy.Integer(2)) s4 = Assignment(S('G'), sympy.Integer(3)) s = ModelStatements([s4, s3, s2, s1]) s.remove_symbol_definitions([S('Z')], s1) assert s == ModelStatements([s4, s1]) s1 = Assignment(S('K'), sympy.Integer(16)) s2 = Assignment(S('CL'), sympy.Integer(23)) s3 = Assignment(S('CL'), S('CL') + S('K')) s4 = Assignment(S('G'), S('X') + S('K')) s = ModelStatements([s1, s2, s3, s4]) s.remove_symbol_definitions([S('CL')], s4) assert s == ModelStatements([s1, s4]) s1 = Assignment(S('K'), sympy.Integer(16)) s2 = Assignment(S('CL'), sympy.Integer(23)) s3 = Assignment(S('CL'), S('CL') + S('K')) s4 = Assignment(S('G'), S('X') + S('K')) s5 = Assignment(S('KR'), S('CL')) s = ModelStatements([s1, s2, s3, s4, s5]) s.remove_symbol_definitions([S('CL')], s4) assert s == ModelStatements([s1, s2, s3, s4, s5]) s1 = Assignment(S('K'), sympy.Integer(16)) s2 = Assignment(S('CL'), sympy.Integer(23)) s3 = Assignment(S('CL'), S('CL') + S('K')) s4 = Assignment(S('G'), S('X')) s = ModelStatements([s1, s2, s3, s4]) s.remove_symbol_definitions([S('CL'), S('K')], s4) assert s == ModelStatements([s4])
def add_iov(model, occ, list_of_parameters=None, eta_names=None): """ Adds IOVs to :class:`pharmpy.model`. Initial estimate of new IOVs are 10% of the IIV eta it is based on. Parameters ---------- model : Model Pharmpy model to add new IOVs to. occ : str Name of occasion column. list_of_parameters : str, list List of names of parameters and random variables. Accepts random variable names, parameter names, or a mix of both. eta_names: str, list Custom names of new etas. Must be equal to the number of input etas times the number of categories for occasion. """ rvs, pset, sset = model.random_variables, model.parameters, model.statements list_of_parameters = _format_input_list(list_of_parameters) etas = _get_etas(model, list_of_parameters, include_symbols=True) categories = _get_occ_levels(model.dataset, occ) if eta_names and len(eta_names) != len(etas) * len(categories): raise ValueError( f'Number of provided names incorrect, need {len(etas) * len(categories)} names.' ) elif len(categories) == 1: raise ValueError(f'Only one value in {occ} column.') iovs, etais = ModelStatements(), ModelStatements() for i, eta in enumerate(etas, 1): omega_name = str( next(iter(eta.sympy_rv.pspace.distribution.free_symbols))) omega = S(f'OMEGA_IOV_{i}') # TODO: better name pset.append(Parameter(str(omega), init=pset[omega_name].init * 0.1)) iov = S(f'IOV_{i}') values, conditions = [], [] for j, cat in enumerate(categories, 1): if eta_names: eta_name = eta_names[j - 1] else: eta_name = f'ETA_IOV_{i}{j}' eta_new = RandomVariable.normal(eta_name, 'iov', 0, omega) rvs.append(eta_new) values += [S(eta_new.name)] conditions += [Eq(cat, S(occ))] expression = Piecewise(*zip(values, conditions)) iovs.append(Assignment(iov, sympy.sympify(0))) iovs.append(Assignment(iov, expression)) etais.append(Assignment(S(f'ETAI{i}'), eta.symbol + iov)) sset.subs({eta.name: S(f'ETAI{i}')}) iovs.extend(etais) iovs.extend(sset) model.random_variables, model.parameters, model.statements = rvs, pset, iovs return model
def update_statements(model, old, new, trans): trans['NaN'] = int(data.conf.na_rep) main_statements = ModelStatements() error_statements = ModelStatements() new_odes = new.ode_system if new_odes is not None: old_odes = old.ode_system if new_odes != old_odes: update_ode_system(model, old_odes, new_odes) after_odes = False for s in new: if isinstance(s, ODESystem): after_odes = True elif after_odes: error_statements.append(s) else: main_statements.append(s) main_statements.subs(trans) rec = model.get_pred_pk_record() rec.statements = main_statements error = model._get_error_record() if error: if len(error_statements) > 0: error_statements.pop(0) # Remove the link statement error_statements.subs(trans) error.statements = error_statements error.is_updated = True rec.is_updated = True
def test_repr_html(): s1 = Assignment(S('KA'), S('X') + S('Y')) stats = ModelStatements([s1]) html = stats._repr_html_() assert 'X + Y' in html
def add_covariate_effect(model, parameter, covariate, effect, operation='*'): """ Adds covariate effect to :class:`pharmpy.model`. The following effects have templates: - Linear function for continuous covariates (*lin*) - Function: .. math:: \\text{coveff} = 1 + \\text{theta} * (\\text{cov} - \\text{median}) - Init: 0.001 - Upper: - If median of covariate equals minimum: :math:`100,000` - Otherwise: :math:`\\frac{1}{\\text{median} - \\text{min}}` - Lower: - If median of covariate equals maximum: :math:`-100,000` - Otherwise: :math:`\\frac{1}{\\text{median} - \\text{max}}` - Linear function for categorical covariates (*cat*) - Function: - If covariate is most common category: .. math:: \\text{coveff} = 1 - For each additional category: .. math:: \\text{coveff} = 1 + \\text{theta} - Init: :math:`0.001` - Upper: :math:`100,000` - Lower: :math:`-100,000` - Piecewise linear function/"hockey-stick", continuous covariates only (*piece_lin*) - Function: - If cov <= median: .. math:: \\text{coveff} = 1 + \\text{theta1} * (\\text{cov} - \\text{median}) - If cov > median: .. math:: \\text{coveff} = 1 + \\text{theta2} * (\\text{cov} - \\text{median}) - Init: :math:`0.001` - Upper: - For first state: :math:`\\frac{1}{\\text{median} - \\text{min}}` - Otherwise: :math:`100,000` - Lower: - For first state: :math:`-100,000` - Otherwise: :math:`\\frac{1}{\\text{median} - \\text{max}}` - Exponential function, continuous covariates only (*exp*) - Function: .. math:: \\text{coveff} = \\exp(\\text{theta} * (\\text{cov} - \\text{median})) - Init: - If lower > 0.001 or upper < 0.001: :math:`\\frac{\\text{upper} - \\text{lower}}{2}` - If estimated init is 0: :math:`\\frac{\\text{upper}}{2}` - Otherwise: :math:`0.001` - Upper: - If min - median = 0 or max - median = 0: :math:`100` - Otherwise: .. math:: \\min(\\frac{\\log(0.01)}{\\text{min} - \\text{median}}, \\frac{\\log(100)}{\\text{max} - \\text{median}}) - Lower: - If min - median = 0 or max - median = 0: :math:`0.01` - Otherwise: .. math:: \\max(\\frac{\\log(0.01)}{\\text{max} - \\text{median}}, \\frac{\\log(100)}{\\text{min} - \\text{median}}) - Power function, continuous covariates only (*pow*) - Function: .. math:: \\text{coveff} = (\\frac{\\text{cov}}{\\text{median}})^\\text{theta} - Init: :math:`0.001` - Upper: :math:`100,000` - Lower: :math:`-100` Parameters ---------- model : Model Pharmpy model to add covariate effect to. parameter : str Name of parameter to add covariate effect to. covariate : str Name of covariate. effect : str Type of covariate effect. May be abbreviated covariate effect (see above) or custom. operation : str, optional Whether the covariate effect should be added or multiplied (default). """ sset = model.statements if S(f'{parameter}{covariate}') in sset.free_symbols: warnings.warn('Covariate effect already exists') return model statistics = dict() statistics['mean'] = _calculate_mean(model.dataset, covariate) statistics['median'] = _calculate_median(model.dataset, covariate) statistics['std'] = _calculate_std(model.dataset, covariate) covariate_effect = _create_template(effect, model, covariate) thetas = _create_thetas(model, parameter, effect, covariate, covariate_effect.template) param_statement = sset.find_assignment(parameter) index = sset.index(param_statement) covariate_effect.apply(parameter, covariate, thetas, statistics) effect_statement = covariate_effect.create_effect_statement( operation, param_statement) statements = ModelStatements() statements += [ s for s in covariate_effect.statistic_statements if s not in sset ] statements.append(covariate_effect.template) statements.append(effect_statement) previous_effect = sset.find_assignment(parameter) cov_possible = [ f'{parameter}{col_name}' for col_name in model.dataset.columns ] if previous_effect.expression.args and all( arg.name in cov_possible for arg in previous_effect.expression.args if str(arg) != parameter): effect_statement.expression = effect_statement.expression.subs( {parameter: previous_effect.expression}) sset.remove(previous_effect) for i, statement in enumerate(statements, 1): sset.insert(index + i, statement) model.statements = sset return model