Examples#
SARP / WARP Consistency#
Test whether a user’s menu choices form a consistent ranking:
from prefgraph import MenuChoiceLog, validate_menu_warp, validate_menu_sarp
log = MenuChoiceLog(
menus=[
frozenset({0, 1, 2}), # chose 0 from {Pizza, Burger, Salad}
frozenset({1, 2, 3}), # chose 1 from {Burger, Salad, Pasta}
frozenset({0, 3}), # chose 0 from {Pizza, Pasta}
frozenset({0, 1, 3}), # chose 0 from {Pizza, Burger, Pasta}
],
choices=[0, 1, 0, 0],
item_labels=["Pizza", "Burger", "Salad", "Pasta"],
)
warp = validate_menu_warp(log)
sarp = validate_menu_sarp(log)
print(f"WARP: {warp.is_consistent} SARP: {sarp.is_consistent}")
WARP: True SARP: True
Detecting Violations#
A WARP violation: choosing differently from the same pair.
from prefgraph import MenuChoiceLog, validate_menu_warp
log = MenuChoiceLog(
menus=[frozenset({0, 1}), frozenset({0, 1})],
choices=[0, 1],
)
result = validate_menu_warp(log)
print(f"WARP: {result.is_consistent} Violations: {result.violations}")
WARP: False Violations: [(0, 1)]
Houtman-Maks Efficiency#
How many observations to remove to restore consistency:
from prefgraph import MenuChoiceLog, compute_menu_efficiency
log = MenuChoiceLog(
menus=[frozenset({0, 1}), frozenset({0, 1}), frozenset({1, 2}), frozenset({0, 2})],
choices=[0, 1, 1, 0],
)
result = compute_menu_efficiency(log)
print(f"Efficiency: {result.efficiency_index:.2f}")
print(f"Removed: {result.removed_observations}")
Efficiency: 0.75
Removed: [1]
Ordinal Utility Recovery#
Recover a preference ranking from consistent choices:
from prefgraph import MenuChoiceLog, fit_menu_preferences
log = MenuChoiceLog(
menus=[frozenset({0, 1, 2}), frozenset({1, 2}), frozenset({0, 2})],
choices=[0, 1, 0],
)
result = fit_menu_preferences(log)
print(f"Preference order: {result.preference_order}")
print(f"Utility values: {result.utility_values}")
Preference order: [0, 1, 2]
Utility values: [3. 2. 1.]
Limited Attention (WARP-LA)#
Test whether violations can be explained by inattention rather than irrationality:
from prefgraph import MenuChoiceLog, test_warp_la
log = MenuChoiceLog(
menus=[frozenset({0, 1, 2}), frozenset({0, 1}), frozenset({1, 2}), frozenset({0, 2})],
choices=[0, 0, 1, 2],
)
result = test_warp_la(log)
print(f"WARP(LA): {result.satisfies_warp_la}")
WARP(LA): True
Stochastic Choice (RUM)#
Fit a random utility model to choice frequency data:
from prefgraph import StochasticChoiceLog, fit_random_utility_model
log = StochasticChoiceLog(
menus=[frozenset({0, 1, 2}), frozenset({0, 1}), frozenset({1, 2})],
choice_frequencies=[
{0: 60, 1: 30, 2: 10},
{0: 70, 1: 30},
{1: 55, 2: 45},
],
item_labels=["Apple", "Banana", "Cherry"],
)
result = fit_random_utility_model(log, model_type="logit")
print(f"Log-likelihood: {result.log_likelihood:.2f}")
print(f"Satisfies IIA: {result.satisfies_iia}")
Log-likelihood: -89.34
Satisfies IIA: True
Risk Preferences#
Classify risk attitudes from lottery choices:
import numpy as np
from prefgraph import RiskChoiceLog, compute_risk_profile
log = RiskChoiceLog(
safe_values=np.array([50.0, 40.0, 30.0, 20.0, 10.0]),
risky_outcomes=np.array([[100.0, 0.0]] * 5),
risky_probabilities=np.array([[0.5, 0.5]] * 5),
choices=np.array([False, False, False, True, True]),
)
result = compute_risk_profile(log)
print(f"Risk category: {result.risk_category}")
print(f"Consistency: {result.consistency_score:.0%}")
Risk category: risk_averse
Consistency: 100%
Context Effects (Decoy Detection)#
Detect whether adding items shifts choice probabilities:
from prefgraph import StochasticChoiceLog, detect_decoy_effect
log = StochasticChoiceLog(
menus=[
frozenset({0, 1, 2}),
frozenset({0, 1, 2, 3}), # decoy added
frozenset({0, 1}),
frozenset({1, 2}),
],
choice_frequencies=[
{0: 30, 1: 45, 2: 25},
{0: 22, 1: 38, 2: 35, 3: 5},
{0: 40, 1: 60},
{1: 55, 2: 45},
],
total_observations_per_menu=[100, 100, 100, 100],
item_labels=["Basic", "Standard", "Premium", "Decoy"],
)
result = detect_decoy_effect(log, threshold=0.05)
print(f"Decoy effect: {result.has_decoy_effect}")
print(f"Magnitude: {result.magnitude:.1%}")
Decoy effect: True
Magnitude: 10.0%
Ranking and Pairwise Comparison#
Fit a Bradley-Terry model from pairwise data:
from prefgraph import fit_bradley_terry
# (winner, loser, count)
comparisons = [
(0, 1, 15), (1, 0, 5),
(0, 2, 18), (2, 0, 2),
(1, 2, 12), (2, 1, 8),
]
result = fit_bradley_terry(comparisons, method="mle")
print(f"Ranking: {result.ranking}")
for item, score in sorted(result.scores.items(), key=lambda x: -x[1]):
print(f" Item {item}: {score:.3f}")
Ranking: [0, 1, 2]
Item 0: 1.523
Item 1: 0.412
Item 2: 0.000