Choice-based conjoint (CBC) is a survey technique for measuring how customers trade off product attributes when forced to pick between alternatives. Each respondent works through a sequence of tasks; in every task they see a small set of competing product concepts and choose the one they would actually buy. Analysing the pattern of choices across tasks tells you how much each attribute level — brand, price, feature, packaging — contributes to preference, and lets you simulate share-of-preference across hypothetical market scenarios.
CBC complements profile-rating conjoint, which collects scored evaluations of one profile at a time. CBC's forced-choice format is closer to a real purchase decision and is the right tool when you want utility estimates and share simulations rather than absolute ratings. For the broader methodology arc and how the two approaches compare, see Setting up a conjoint study.
In MX8, the recommended implementation for CBC is the built-in conjoint_question method.
Bayesian reporting
CBC supports model-based reporting that estimates each respondent's preference for each attribute level from their choices across tasks. In a cross-tab, set Style to:
- Utility Scores (
discrete_choice_utility_scores) — a score per attribute level showing how strongly it drives choice. - Simulated Share (
discrete_choice_simulated_share) — the predicted share of respondents who would pick each option.
Include every attribute you want broken out in utility or share outputs via required_tags, since this controls which conjoint attributes are carried into reporting rows.
For the estimator, weighted aggregation, exports, and uncertainty methodology, see Utility and simulated share methodology. For broader context on CBC vs profile-rating conjoint, see Setting up a conjoint study.
Tip: Use the Conjoint Planner to design attributes/levels and generate a concept pool and task-set structure you can paste into the code below.
Why Use conjoint_question
conjoint_question handles the hard parts that were previously hand-built:
- Assigns one task set per respondent using least-fill balancing to keep per-task-set exposure roughly even.
- Presents each task as a select question — a single-choice pick from the task's concepts.
- Adds conjoint metadata (
conjoint_task_set,conjoint_task) to each task response. - Supports both dict-backed concepts and media-backed concepts.
Method Signature
s.conjoint_question(
question,
concepts,
task_sets,
concept_id_tag="id",
required_tags=None,
dont_know_option="",
randomize=False,
image_label_field=None,
show_image_label=True,
image_size=None,
tags=None,
)
Step 1: Create the Survey
from survey import Survey
s = Survey(**globals())
Step 2: Define Concepts
Each concept must have a unique id (or your custom concept_id_tag). Add tags you want reported with each selected option.
concepts = [
{"id": "A", "brand": "Apple", "color": "Black", "network": "5G", "price": "$800"},
{"id": "B", "brand": "Apple", "color": "Blue", "network": "5G", "price": "$1000"},
{"id": "C", "brand": "Samsung", "color": "Black", "network": "4G", "price": "$600"},
{"id": "D", "brand": "Samsung", "color": "Silver", "network": "5G", "price": "$800"},
]
Step 3: Define Task Sets
task_sets maps task-set ids to ordered tasks, where each task is a list of concept ids.
task_sets = {
"TS001": [["A", "C"], ["B", "D"], ["A", "D"], ["B", "C"]],
"TS002": [["A", "B"], ["C", "D"], ["A", "C"], ["B", "D"]],
}
Step 4: Ask the Conjoint Block
choices = s.conjoint_question(
question="Which product would you choose?",
concepts=concepts,
task_sets=task_sets,
required_tags=["brand", "color", "network", "price"],
dont_know_option="None of these",
randomize=True,
tags={"study": "smartphone_cbc"},
)
s.complete()
Response Behavior
- The returned object is a dictionary-like response keyed by
"<task_set_id>_<task_number>". - Each task response includes:
- The selected concept id.
required_tagsvalues for dict-backed concepts.- Block tags (for example,
study). conjoint_task_setandconjoint_tasktags.
- If
dont_know_optionis selected, it is treated as a fixed non-concept option (no concept tags attached).
Full Example
from survey import Survey
s = Survey(**globals())
s.note(
"You will see a series of product choices. In each task, select the option you would most likely buy."
)
concepts = [
{"id": "A", "brand": "Apple", "color": "Black", "network": "5G", "price": "$800"},
{"id": "B", "brand": "Apple", "color": "Blue", "network": "5G", "price": "$1000"},
{"id": "C", "brand": "Samsung", "color": "Black", "network": "4G", "price": "$600"},
{"id": "D", "brand": "Samsung", "color": "Silver", "network": "5G", "price": "$800"},
]
task_sets = {
"TS001": [["A", "C"], ["B", "D"], ["A", "D"], ["B", "C"]],
"TS002": [["A", "B"], ["C", "D"], ["A", "C"], ["B", "D"]],
}
s.conjoint_question(
question="Which product would you choose?",
concepts=concepts,
task_sets=task_sets,
required_tags=["brand", "color", "network", "price"],
dont_know_option="None of these",
randomize=True,
tags={"study": "smartphone_cbc"},
)
s.complete()