Skip to main content

partworth

partworth estimates hierarchical Bayes (HB) models for choice-based conjoint (CBC) studies in Python. It is built on PyMC.

It covers the whole workflow in one package: data checks, HB estimation, respondent-level part-worths, Sawtooth-style output files, a market simulator and willingness to pay (WTP). The validation scripts and their results are in the repository.

Status: pre-alpha. The API may change.

What you can do with it

flowchart TB
    subgraph S1["1. Your survey"]
        direction LR
        A["CBC answers<br/>(long format)"]
        B["Design from ChoiceDesign<br/>(wide format)"]
        A ~~~ B
    end
    subgraph S2["2. Fit one model"]
        direction LR
        C["ChoiceData<br/>checks the data"] --> D["HierarchicalMNL<br/>HB estimation on PyMC"] --> E["Diagnostics<br/>R-hat, ESS, divergences"]
    end
    subgraph S3["3. Read the results"]
        direction LR
        F["Part-worths<br/>for each respondent"]
        G["Importances and RLH"]
        H["Sawtooth-style CSV"]
        F ~~~ G ~~~ H
    end
    subgraph S4["4. Answer business questions"]
        direction LR
        I["Which product wins?<br/>shares with<br/>credible intervals"]
        J["What if we change<br/>the price?<br/>price curves"]
        K["How much will<br/>people pay?<br/>WTP for a feature"]
        I ~~~ J ~~~ K
    end
    S1 --> S2 --> S3 --> S4

Steps 1 to 3 replace the usual "estimate in one tool, export, simulate in a spreadsheet" work. Step 4 uses all posterior draws, so every share, price curve and WTP comes with its uncertainty.

Why partworth

  • Sawtooth CBC/HB is commercial and runs outside Python. partworth gives the same kind of outputs (raw utilities, zero-centred diffs, importances, RLH) in an open Python package.
  • pymc-marketing's MixedLogit is a general choice model. partworth is built for CBC studies: long-format survey data, correlated random coefficients, a negative price coefficient for every respondent, and a simulator with credible intervals.
  • Each result can be checked. The repository compares partworth with R bayesm on public data and tests that it recovers known parameters.

What works now

  • Long-format choice data with checks, tasks of different sizes and a none option
  • Import of wide designs, such as the output of ChoiceDesign
  • Effects, dummy and linear coding, with an optional sign constraint (for price)
  • Hierarchical MNL with correlated coefficients (LKJ prior), respondent covariates and the nutpie sampler
  • Part-worths, zero-centred diffs, importances, RLH and convergence diagnostics
  • Sawtooth-style utilities CSV (read and write)
  • Market simulator with credible intervals, price curves and WTP

Not planned: experimental design (use ChoiceDesign), latent class, MaxDiff and ACBC.

Installation

pip install "partworth[nutpie,datasets]"

nutpie adds the faster sampler. datasets adds the reader for the public camera data.

It needs Python 3.12 or later.

On macOS 26 or later, PyTensor 3.3.3 or earlier can fail with ld: library 'd64' not found. Set PYTENSOR_FLAGS=cxx= to avoid it. The fix is merged in PyTensor (pymc-devs/pytensor#2432) but not yet released.

Quick look

This example uses simulated data, so you can paste it as it is.

import pandas as pd
import partworth as pw
from partworth.datasets import simulate_cbc

# 300 respondents, 12 tasks, 3 products + "none" per task.
# mu: true population means of the coded coefficients (price on the log scale).
data, truth = simulate_cbc(
    300, 12, 3,
    {"brand": ["A", "B", "C"], "size": ["S", "M", "L"], "price": [2.0, 2.5, 3.0, 3.5]},
    price="price", mu=[0.4, -0.1, -0.3, 0.1, -0.5, -1.0], sd=0.6, seed=1,
)
print(data.describe())                       # how often each level was shown and chosen

# more tuning and draws than the defaults: the SD of "none" mixes slowly (see the guide)
result = pw.HierarchicalMNL(data).fit(draws=2000, tune=3000, chains=4, random_seed=1)
print(result.diagnostics().report())         # R-hat, ESS, divergences, what to try

result.part_worths()                         # raw utilities, one column per level
result.part_worths("zc_diffs")               # zero-centred diffs
result.importances()
result.to_sawtooth_csv("utilities.csv")

scenario = pd.DataFrame({"product": ["Ours", "Theirs"], "brand": ["A", "B"],
                         "size": ["M", "M"], "price": [3.0, 2.5]})
sim = pw.MarketSimulator(result)
print(sim.shares(scenario))                  # shares with 90% credible intervals
print(sim.price_curve(scenario, product="Ours", grid=[2.0, 2.5, 3.0, 3.5]))
print(pw.wtp(result, attribute="size", from_level="S", to_level="L"))

For your own data, build ChoiceData from a long table (one row per respondent, task and alternative):

data = pw.ChoiceData.from_long(
    df,                                        # your table
    attributes={"brand": pw.Categorical(["A", "B", "C"]),
                "price": pw.Linear(sign="negative")},
    none="is_none",                            # optional "no purchase" rows
)

examples/retail_survey.ipynb is a notebook on a dummy retail survey. It goes from the survey table to shares, prices and profit. A Japanese version is in examples/retail_survey_ja.ipynb. examples/camera.py runs the same steps on public data. docs/guide.md explains the choices you make along the way.

Validation

Each script in validation/ writes its report to validation/results/.

  • Parameter recovery (recovery_report.md). 300 simulated respondents and 12 tasks. Respondent-level 90% intervals covered 88.7% of the true values. 5 of 6 population means and 6 of 6 SDs were inside their 90% intervals. The price mean (log scale) was just outside. The convergence targets were met. Correlations reached R-hat 1.013 and ESS 399, just short of the targets. pytest --run-slow runs the same fit and checks.
  • Comparison with R bayesm (camera_report.md). bayesm::rhierMnlRwMixture on the public camera data. Respondent-level posterior means correlate at 0.997 or more for every coefficient. The hold-out hit rate is 0.729 for partworth and 0.730 for bayesm. Population means differ by up to 0.21 posterior SDs. Our target was 0.1, so this target is not met. The two packages use different priors.
  • Simulation-based calibration (sbc_report.md). 200 simulated data sets, 8 population parameters. The smallest uniformity p-value was 0.017. None was below 0.006, the threshold after correcting for 8 tests. Earlier runs showed low p-values for different parameters each time. 15 of the 200 fits had a population ESS below 99, which weakens the check.
  • Performance (performance.md). 300 respondents took 74 s and 1,000 respondents took 290 s on one laptop.

Data and licences

partworth is licensed under Apache-2.0. The camera data belongs to the R package bayesm, which is GPL. partworth downloads it from CRAN on first use and never includes it. No bayesm code is used. The comparison runs R in a separate process.

All examples use public or simulated data.

How this is built

Design decisions are in docs/adr/. Parts of the code were written with an AI coding assistant. The model, the priors and the validation targets are the author's decisions. The ADRs record them.

Licence

Apache License 2.0. See LICENSE.

Metadata

Release files for partworth 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for partworth 0.0.1
File Size Uploaded Provenance Actions
partworth-0.0.1.tar.gz 1.2 MB Without provenance Details for partworth-0.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for partworth 0.0.1
File Interpreter ABI Platform Actions
partworth-0.0.1-py3-none-any.whl Python 3 none any Details for partworth-0.0.1-py3-none-any.whl

Total release size: 1.2 MB

Release files / partworth-0.0.1.tar.gz

Download URL partworth-0.0.1.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
ff83acc33e18f4fabb6ae666840456b89acc06e7acc92c73e2cb1438121127db
BLAKE2b-256 checksum
How to use checksums
a8f3f374952826fd78c0cfd792016e2e724d1712d552b9257c03e25694ab7331
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / partworth-0.0.1-py3-none-any.whl

Download URL partworth-0.0.1-py3-none-any.whl
Size 36.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
023bb1e2b0aaabae6c83c5d05d04289deb45b2564a2a97e9c0fe625487c7a25e
BLAKE2b-256 checksum
How to use checksums
dbe248da3fe8daa38b95f720c5db7b0c272ec55102fb3c28b5e9a08cf9289ba3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for partworth 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page