def lagged_choices_probs_template(n_lagged_choices, choices): to_concat = [] for i in range(1, n_lagged_choices + 1): probs = np.random.uniform(size=len(choices)) probs = normalize_probabilities(probs) for j, choice in enumerate(choices): ind = (f"lagged_choice_{i}_{choice}", "constant") dat = [probs[j], f"Probability of choice {choice} being lagged choice {i}"] to_concat.append(_base_row(ind, dat)) return pd.concat(to_concat, axis=0, sort=False)
def observable_prob_template(observables): to_concat = [] for i in range(len(observables)): probs = np.random.uniform(size=observables[i]) probs = normalize_probabilities(probs) for j in range(observables[i]): ind = (f"observable_observable_{i}_{j}", "probability") dat = [probs[j], f"Probability of observable {i} being level choice {j}"] to_concat.append(_base_row(ind, dat)) out = pd.concat(to_concat, axis=0, sort=False) return out
def _parse_probabilities_or_logit_coefficients(params, regex_for_levels): r"""Parse probabilities or logit coefficients of parameter groups. Some parameters form a group to specify a distribution. The parameters can either be probabilities from a probability mass function. For example, see the specification of initial years of schooling in the extended model of Keane and Wolpin (1997). On the other hand, parameters and their corresponding covariates can form the inputs of a :func:`scipy.specical.softmax` which generates the probability mass function. This distribution can be more complex. Internally, probabilities are also converted to logit coefficients to align the interfaces. To convert probabilities to the appropriate multinomial logit (softmax) coefficients, use a constant for covariates and note that the sum in the denominator is equal for all probabilities and, thus, can be treated as a constant. The following formula shows that the multinomial coefficients which produce the same probability mass function are equal to the logs of probabilities. .. math:: p_i &= \frac{e^{x_i \beta_i}}{\sum_j e^{x_j \beta_j}} \ &= \frac{e^{\beta_i}}{\sum_j e^{\beta_j}} \ log(p_i) &= \beta_i - \log(\sum_j e^{\beta_j}) \ &= \beta_i - C Raises ------ ValueError If probabilities and multinomial logit coefficients are mixed. Warnings -------- The user is warned if the discrete probabilities of a probability mass function do not sum to one. """ mask = ( params.index.get_level_values("category") .str.extract(regex_for_levels, expand=False) .notna() ) n_parameters = mask.sum() # If parameters for initial experiences are specified, the parameters can either # be probabilities or multinomial logit coefficients. if n_parameters: # Work on subset. sub = params.loc[mask].copy() levels = sub.index.get_level_values("category").str.extract( regex_for_levels, expand=False ) levels = pd.to_numeric(levels, errors="ignore") unique_levels = sorted(levels.unique()) n_probabilities = (sub.index.get_level_values("name") == "probability").sum() # It is allowed to specify the shares of initial experiences as # probabilities. Then, the probabilities are replaced with their logs to # recover the probabilities with a multinomial logit model. if n_probabilities == len(unique_levels) == n_parameters: if sub.sum() != 1: warnings.warn( f"The probabilities for parameter group {regex_for_levels} do not " "sum to one.", category=UserWarning, ) sub = normalize_probabilities(sub) # Clip at the smallest representable number to prevent -infinity for log(0). sub = np.log(np.clip(sub, 1 / MAX_FLOAT, None)) sub = sub.rename(index={"probability": "constant"}, level="name") elif n_probabilities > 0: raise ValueError( "Cannot mix probabilities and multinomial logit coefficients for the " f"parameter group: {regex_for_levels}." ) # Drop level 'category' from :class:`pd.MultiIndex`. s = sub.droplevel(axis="index", level="category") # Insert parameters for every level of initial experiences. container = {level: s.loc[levels == level] for level in unique_levels} # If no parameters are provided, return `None` so that the default is handled # outside the function. else: container = None return container
def _get_initial_shares(num_groups): """We simply need a valid request for the shares of types summing to one.""" shares = np.random.uniform(size=num_groups) shares = normalize_probabilities(shares) return shares