Skip to main content

A fast, Python implementation of an MLE estimator of the multichoice logit model.

Project description

Multe: Multichoice Logit Estimation

Python Version License: MIT Tests PyPI version

A Python library for estimating discrete choice models where agents can select either single alternatives or unordered pairs of alternatives.

This implements the model as described in Ophem, H.V., Stam, P. and Praag, B.V., 1999. Multichoice Logit: Modeling Incomplete Preference Rankings of Classical Concerts. Journal of Business & Economic Statistics, 17(1), pp.117-128.

Built by Thomas Monk, London School of Economics.

Citation

If you use this package, please cite it as:

@misc{monk2025multe,
  author = {Thomas Monk},
  title = {multe},
  howpublished = {\url{https://github.com/tmonk/multe}},
  year = {2025}
}

Installation

Install from PyPI:

pip install multe

Or install from source for development:

git clone https://github.com/tmonk/multe.git
cd multe
pip install -e .

Quick Start

from multe import MultichoiceLogit, simulate_choices

# N: Number of observations
# J: Number of alternatives
# K: Number of covariates

X, choices, true_beta = simulate_choices(N=1000, J=4, K=3, seed=123)

model = MultichoiceLogit(num_alternatives=4, num_covariates=3)
result = model.fit(X, choices).get_result()
print(result.summary())

See examples/quickstart.py for a ready-to-run script or examples/matrix_example.py for the matrix workflow and deeper walkthrough.

Data Format

  • choices (recommended): Length-N list of either int (single choice) or (s, t) tuples (dual choice, unordered).
  • X: Covariates of shape (N, K).
  • Each agent must have exactly one choice entry.
  • Need matrices for another workflow? Jump to Matrix inputs (y_single/y_dual) below.

Model

The full model derivation is available here.

Agents choose from J alternatives. The utility for agent i and alternative j is:

U_ij = V_ij + epsilon_ij
V_ij = X_i * beta_j
epsilon_ij ~ Gumbel(0, 1)

Each agent makes either:

  • Single choice: Select the alternative with maximum utility
  • Dual choice: Select the top two alternatives as an unordered pair

The mixing probability between single and dual choices is determined by the parameter mix_ratio in simulation.

Probabilities

Single choice (multinomial logit):

P(choose j) = exp(V_ij) / sum_k exp(V_ik)

Dual choice (inclusion-exclusion principle):

P(choose {s,t}) = P(U_s > max_k≠s U_k ∪ U_t > max_k≠t U_k)

Identification

The model fixes beta_0 = 0 for identification, so it estimates (J-1) × K parameters.

Performance

Fully vectorized implementation for fast estimation and simulation:

Estimation:

  • ~10ms per 1000 observations (optimization)
  • Example: N=10,000, J=5, K=4 completes in ~0.1s

Recommended optimizer: L-BFGS-B

Run benchmarks: python examples/benchmark.py

API Reference

  • MultichoiceLogit(num_alternatives, num_covariates) – model class.
  • fit(X, choices, **kwargs) – choices-first MLE.
  • get_result(standard_errors=None)ModelResult snapshot with summary().
  • predict_proba(X, flat_beta=None) – single/dual choice probabilities.
  • log_likelihood_contributions(X, y_single, y_dual, flat_beta=None) – per-observation log-likelihoods (convert via parse_choices if starting from choices).
  • parse_choices(choices, J) – convert choice list to (y_single, y_dual).
  • simulate_choices(N, J, K, true_beta=None, mix_ratio=0.5, seed=42, rng=None, dtype=np.float64) – generate (X, choices, true_beta); true_beta shape is (J-1, K) (first alternative fixed to zero).

Interpreting the output

The inference table shows one row per alternative (alt) and covariate (k): the estimated coefficient, its standard error, z-score, and p-value.

Example (immigration attitudes):

  • alt1 = “less immigration”, alt2 = “stay the same”, alt3 = “more immigration”.
  • k0 = non-EU migrant share, k1 = unemployment rate.
  • A row alt=1, k=0, coef=-0.26 means higher non-EU share is associated with lower likelihood of choosing “less immigration”. Each row reads the same way for every attitude option and predictor.

Matrix inputs (y_single/y_dual)

The choices-first flow is easiest to use. If you already have matrices:

  • y_single: Binary matrix (N, J) where y_single[i, j] = 1 if agent i chose alternative j.
  • y_dual: Binary tensor (N, J, J) where y_dual[i, s, t] = 1 if agent i chose the unordered pair {s, t} with s < t. Sparse CSR (shape N × J², row-major flattening) and tuple index inputs (rows, s, t) are also supported.
  • Convert from choices with parse_choices(choices, J).

Matrix workflow example:

from multe import MultichoiceLogit, parse_choices, simulate_choices, simulate_data

X, choices, _ = simulate_choices(N=1000, J=4, K=3, seed=42)
y_single, y_dual = parse_choices(choices, J=4)
model = MultichoiceLogit(num_alternatives=4, num_covariates=3)
model.fit_matrix(X, y_single=y_single, y_dual=y_dual)

Matrix-focused utilities:

  • parse_choices(choices, J) – Convert a list of choices (ints or (s, t) tuples) into y_single, y_dual.
  • simulate_data(N, J, K, true_beta=None, mix_ratio=0.5, seed=42, rng=None, dtype=np.float64) – Generate synthetic data following the RUM framework and return (X, y_single, y_dual, true_beta).
  • fit_matrix(X, y_single, y_dual, **kwargs) – Fit via MLE when you already have matrices.
  • gradient(flat_beta, X, y_single, y_dual) – Public analytical gradient; accepts dense, sparse, or tuple dual inputs.
  • compute_standard_errors(X, y_single, y_dual, flat_beta=None, epsilon=None) – Numerical Hessian standard errors (uses fitted params by default).
  • log_likelihood_contributions(X, y_single, y_dual, flat_beta=None) – Per-observation log-likelihoods.

Acknowledgements

Many thanks to Alan Manning for his guidance and support with this project.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multe-4.tar.gz (29.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multe-4-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

File details

Details for the file multe-4.tar.gz.

File metadata

  • Download URL: multe-4.tar.gz
  • Upload date:
  • Size: 29.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for multe-4.tar.gz
Algorithm Hash digest
SHA256 71bf17edd3020ee05a890d5010222be261ccc1126dcead77d8ee8ddea58959b2
MD5 b18a4d7b6afb5e7157ab6799643e9faf
BLAKE2b-256 98020de71424f349b69fe80eae2be81e7eedd5e71708e25f0674e1e022b1624c

See more details on using hashes here.

Provenance

The following attestation bundles were made for multe-4.tar.gz:

Publisher: publish.yml on tmonk/multe

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file multe-4-py3-none-any.whl.

File metadata

  • Download URL: multe-4-py3-none-any.whl
  • Upload date:
  • Size: 17.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for multe-4-py3-none-any.whl
Algorithm Hash digest
SHA256 7310e34735e0009cb9be74500c3c85dcdf330c5a56266fcb3222e76be04d9a86
MD5 2a71cb128f3691444fe5e83f692435e7
BLAKE2b-256 605afdb169f4e727c776d39046ef2017ee79c2710a8054cdc4eaf05ea0be108f

See more details on using hashes here.

Provenance

The following attestation bundles were made for multe-4-py3-none-any.whl:

Publisher: publish.yml on tmonk/multe

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page