The Portland Trail Blazers needed to develop multigame ticket packages that could appeal to former purchasers in a setting of declining attendance. The decision involves four linked product dimensions: number of games, price per seat per game, seat location, and promotional benefit. Package offers must also comply with seat-specific minimum prices and account for the cost of delivering seats and promotions.
The case describes 960 contacted former multigame or season-ticket purchasers, 204 valid survey respondents, and 20 conjoint tasks per respondent. The analytical workbook supplies 16 aggregate part-worth estimates across four attributes, together with reported seat allocations, promotional costs, and pricing constraints. The project evaluates those estimates as the empirical preference input; it develops a decision model for designing and comparing ticket offers.
The central research question is:
Which feasible ticket-package designs combine strong stated consumer appeal with defensible unit economics, and how should management prioritize offers when preferences and cost assumptions are uncertain?
The project progresses from source-data validation to consumer-preference measurement, monetary trade-offs, constrained package design, sensitivity analysis, and an actionable management decision. Each stage preserves the distinction between observed source inputs, derived model outputs, and explicitly assumed scenarios.
| Research stage | Main question | Analytical contribution |
|---|---|---|
| 01. Data input, validation and EDA | What evidence is available and internally consistent? | Validated 16-level part-worth and cost input table; documented design and constraints |
| 02. Conjoint utility and importance | What features drive stated preference? | Level rankings, range-based attribute importance and additive package utility |
| 03. WTP and attribute trade-offs | What is the utility-equivalent value of feature improvements? | Nonlinear price slopes, reference-dependent WTP and promotion cost–value comparisons |
| 04. Package design optimization | Which offers satisfy policy and lead on each objective? | 240-profile design space, 180 feasible offers, contribution scenarios and Pareto comparisons |
| 05. Model validation and sensitivity | How sensitive are the conclusions to inputs and assumptions? | Independent arithmetic audits, hypothetical utility shocks, 18 cost scenarios and frontier persistence |
| 06. Decision synthesis | Which offers should be considered, tested and monitored? | Evidence-weighted recommendations, operational decision rules and a research agenda |
The synthesis reads the validated attribute-level dataset from Module 1 and the analytical tables exported by Modules 2–5. The source workbook remains the primary numerical reference. The output files are deterministic transformations or explicitly identified scenario calculations based on that reference.
partworth <- utils::read.csv(file.path(project_root, "data", "processed", "partworth_levels.csv"),
stringsAsFactors = FALSE, check.names = FALSE)
importance <- read_output("02_attribute_importance.csv")
price_steps <- read_output("03_price_utility_intervals.csv")
promotion_values <- read_output("03_promotion_incremental_values.csv")
packages <- read_output("04_feasible_package_economics.csv")
leaders05 <- read_output("05_baseline_objective_leaders.csv")
stability <- read_output("05_utility_ranking_stability_summary.csv")
cost_scenarios <- read_output("05_cost_scenario_leaders.csv")
frontier_persistence <- read_output("05_pareto_persistence.csv")
validation_checks <- read_output("05_internal_validation_checks.csv")
assert(all(c("attribute", "level_id", "product_level", "utility", "cost_usd") %in% names(partworth)),
"Validated part-worth schema is incomplete")
assert(all(c("attribute", "importance_pct") %in% names(importance)),
"Module 2 importance schema is incomplete")
assert(all(c("level_id", "delta_u_vs_none", "incremental_cost_usd", "wtp_full_range_usd") %in% names(promotion_values)),
"Module 3 promotion valuation schema is incomplete")
required_pkg <- c("package_id", "games_id", "price_id", "seat_id", "promo_id", "games_n",
"price_usd", "seat_cost_usd", "promo_cost_usd", "utility",
"revenue_per_package", "contribution_A", "contribution_B")
assert(all(required_pkg %in% names(packages)), "Module 4 package economics schema is incomplete")
assert(all(c("objective", "package_id") %in% names(leaders05)), "Module 5 leader schema is incomplete")
assert(all(c("scenario_id", "leader_package", "per_game_leader") %in% names(cost_scenarios)),
"Module 5 economic scenario schema is incomplete")
assert(all(c("package_id", "frontier_scenarios", "frontier_share") %in% names(frontier_persistence)),
"Module 5 frontier schema is incomplete")
assert(nrow(partworth) == 16L && nrow(packages) == 180L, "Unexpected source level or feasible package count")
assert(nrow(cost_scenarios) == 18L, "Expected 18 cost scenarios")
assert(!anyDuplicated(partworth[c("attribute", "level_id")]), "Duplicate source attribute levels")
assert(!anyDuplicated(packages$package_id), "Duplicate feasible package identifiers")
assert(all(is.finite(as.numeric(partworth$utility))), "Invalid source utility")
assert(abs(sum(as.numeric(importance$importance_pct)) - 100) < 0.05,
"Attribute importance does not sum to 100 percent")
assert(all(packages$price_usd[packages$seat_id == 4] >= 60) &&
all(packages$price_usd[packages$seat_id == 3] >= 25),
"One or more packages violate the seat-price policy")
input_inventory <- data.frame(
evidence = c("Original attribute levels", "Conjoint attributes", "Theoretical profiles",
"Feasible package profiles", "Economic sensitivity scenarios"),
count = c(nrow(partworth), length(unique(partworth$attribute)),
prod(as.integer(table(partworth$attribute))), nrow(packages), nrow(cost_scenarios)))
assert(input_inventory$count[3] == 240L, "The source attribute design must generate 240 profiles")
knitr::kable(input_inventory, caption = "Verified evidence inventory")| evidence | count |
|---|---|
| Original attribute levels | 16 |
| Conjoint attributes | 4 |
| Theoretical profiles | 240 |
| Feasible package profiles | 180 |
| Economic sensitivity scenarios | 18 |
Results and interpretation. The source design contains 16 attribute levels and generates 240 possible packages; 180 satisfy the two seat-price restrictions. The 18 economic scenarios are sensitivity assumptions, not additional survey observations. This separation is essential when interpreting model-based recommendations.
The additive conjoint score for package \(j\) is the sum of its four part-worths:
\[U_j=u_{\mathrm{games},j}+u_{\mathrm{price},j}+u_{\mathrm{seat},j}+u_{\mathrm{promotion},j}.\]
The range-based importance of attribute \(a\) is \(100R_a/\sum_b R_b\), where \(R_a\) is the difference between its highest and lowest part-worth. A utility improvement can be expressed as a reference-dependent price equivalent by dividing it by the magnitude of a selected price–utility slope. This price equivalent summarizes the estimated preference model; it is not a recorded payment.
For \(G_j\) games, price \(P_j\) per game, seat allocation \(S_j\) per game, and promotional cost \(M_j\), the project compares two conditional accounting-contribution conventions:
\[C_j^{(A)}=G_j(P_j-S_j)-M_j\quad\text{(promotion once per package)},\] \[C_j^{(B)}=G_j(P_j-S_j-M_j)\quad\text{(promotion once per game)}.\]
Both quantities are modeled contribution per package sold, before other operating costs and without an estimated number of sales. The case’s seat-cost figures are treated as reported allocations; the project does not assume that all allocations represent incremental cash expenditure.
The code ranks the attributes using the utility ranges calculated in Module 2. The figure reports their share of the total observed range and is therefore specific to the levels included in the survey design.
importance <- importance |>
mutate(importance_pct = as.numeric(importance_pct)) |>
arrange(desc(importance_pct))
knitr::kable(importance |> select(attribute, importance_pct), digits = 2,
caption = "Relative importance derived from the workbook part-worths")| attribute | importance_pct |
|---|---|
| Ticket Location | 39.49 |
| Ticket Price | 37.58 |
| Number of Games | 11.78 |
| Promotional Item | 11.15 |
p_importance <- ggplot(importance, aes(x = reorder(attribute, importance_pct), y = importance_pct)) +
geom_col(fill = "#315F7D", width = 0.65) +
geom_text(aes(label = sprintf("%.2f%%", importance_pct)), hjust = -0.12, size = 3.6) +
coord_flip() +
scale_y_continuous(limits = c(0, max(importance$importance_pct) * 1.22),
expand = expansion(mult = c(0, 0))) +
labs(x = NULL, y = "Share of total part-worth range (%)",
title = "Seat location and price dominate the modeled preference range") +
theme_minimal(base_size = 11)
print(p_importance)Results and interpretation. Seat location contributes 39.49% of the total part-worth range and ticket price contributes 37.58%. Together, these two dimensions explain 77.07% of the modeled range. Number of games and promotional benefits remain relevant, particularly when their utility gains can be delivered at low cost.
Managerial implication. Begin package differentiation with seat and price combinations. Use game counts and promotions to refine the value proposition, rather than assuming that a promotional giveaway can compensate for every seat or price disadvantage.
The code identifies the most and least preferred level within each attribute and calculates their difference. Within-attribute differences have a clear interpretation because the same utility normalization applies to both compared levels.
partworth <- partworth |>
mutate(level_id = as.integer(level_id), utility = as.numeric(utility),
cost_usd = as.numeric(cost_usd))
level_extremes <- partworth |>
group_by(attribute) |>
summarise(best_level = product_level[which.max(utility)],
best_utility = max(utility),
worst_level = product_level[which.min(utility)],
worst_utility = min(utility),
utility_spread = max(utility) - min(utility), .groups = "drop") |>
arrange(desc(utility_spread))
knitr::kable(level_extremes, digits = 5,
caption = "Strongest and weakest preference levels within each attribute")| attribute | best_level | best_utility | worst_level | worst_utility | utility_spread |
|---|---|---|---|---|---|
| Ticket Location | 200 level, midcourt | 1.01148 | 300 level, behind the baskets | -0.73169 | 1.74317 |
| Ticket Price | 15 | 0.65646 | 60 | -1.00257 | 1.65903 |
| Number of Games | 6 | 0.24383 | 10 | -0.27640 | 0.52023 |
| Promotional Item | Hot dog and soda with each ticket | 0.17428 | No promotional item | -0.31786 | 0.49214 |
Results and interpretation. The preferred seat is 200 level, midcourt, while the preferred package length is 6. These findings favor six-game offers and targeted seat improvements as candidates for product development. They describe the aggregate model and should be evaluated alongside price feasibility and unit economics.
The source workbook reports separate part-worths for four ticket prices. Rather than imposing one universal price coefficient, the project measures the utility gain from each adjacent price reduction. The comparison demonstrates why a monetary equivalent depends on the selected reference interval.
prices <- partworth |>
filter(attribute == "Ticket Price") |>
mutate(price_usd = as.numeric(gsub("[^0-9.]", "", product_level))) |>
arrange(price_usd)
assert(nrow(prices) == 4L && all(is.finite(prices$price_usd)),
"Four numeric ticket prices are required")
price_differences <- data.frame(
higher_price = prices$price_usd[-1],
lower_price = prices$price_usd[-nrow(prices)],
utility_gain_from_reduction = prices$utility[-nrow(prices)] - prices$utility[-1],
price_reduction_usd = diff(prices$price_usd)) |>
mutate(utility_gain_per_dollar = utility_gain_from_reduction / price_reduction_usd)
assert(all(price_differences$utility_gain_from_reduction > 0),
"Source price utilities must decrease with price")
knitr::kable(price_differences, digits = 5,
caption = "Utility gained from adjacent price reductions")| higher_price | lower_price | utility_gain_from_reduction | price_reduction_usd | utility_gain_per_dollar |
|---|---|---|---|---|
| 25 | 15 | 0.43635 | 10 | 0.04364 |
| 35 | 25 | 0.09411 | 10 | 0.00941 |
| 60 | 35 | 1.12857 | 25 | 0.04514 |
p_price <- ggplot(prices, aes(price_usd, utility)) +
geom_line(color = "#315F7D", linewidth = 0.9) +
geom_point(color = "#315F7D", size = 3) +
scale_x_continuous(breaks = prices$price_usd) +
labs(x = "Ticket price per seat per game (USD)", y = "Estimated price part-worth",
title = "Price sensitivity varies across the studied price levels") +
theme_minimal(base_size = 11)
print(p_price)Results and interpretation. A price cut from $25 to $15 improves utility by 0.43635, compared with 0.09411 for a cut from $35 to $25. The difference shows that equal-sized discounts can produce different modeled preference gains. Management should therefore evaluate specific price points and feature combinations, rather than relying on a constant price-response assumption.
The code joins the promotional utilities and costs already calculated in Module 3. Each promotion is compared with the no-promotion alternative, holding the other package attributes fixed. The endpoint-slope monetary equivalent is shown as a reference-dependent consumer valuation, alongside the workbook’s reported cost.
promo_summary <- promotion_values |>
mutate(across(c(delta_u_vs_none, incremental_cost_usd, wtp_full_range_usd), as.numeric)) |>
select(level_id, product_level, delta_u_vs_none, incremental_cost_usd, wtp_full_range_usd) |>
arrange(desc(delta_u_vs_none))
knitr::kable(promo_summary, digits = 3,
caption = "Incremental promotion utility, modeled monetary equivalent and reported cost")| level_id | product_level | delta_u_vs_none | incremental_cost_usd | wtp_full_range_usd |
|---|---|---|---|---|
| 2 | Hot dog and soda with each ticket | 0.492 | 3.25 | 13.349 |
| 1 | Priority for home playoff tickets | 0.443 | 0.00 | 12.015 |
| 4 | $20 gift certificate for popular local restaurant | 0.335 | 10.00 | 9.080 |
| 3 | Trail Blazer apparel (hat, jersey, etc.) | 0.319 | 12.00 | 8.665 |
| 5 | No promotional item | 0.000 | 0.00 | 0.000 |
assert(any(promo_summary$level_id == 2L), "Hot dog and soda promotion is missing")
hotdog <- promo_summary |> filter(level_id == 2L) |> slice(1)Results and interpretation. Hot dog and soda adds 0.49214 utility relative to no promotion, at a reported direct cost of $3.25. Its full-range price-equivalent value is approximately $13.35. This comparison supports testing a low-cost, high-perceived-value benefit, subject to fulfillment frequency, capacity and operational feasibility. The difference between modeled valuation and reported cost is not a profit estimate.
Managerial implication. Prioritize promotions by incremental preference benefit, delivery cost and operational feasibility. A zero-reported-cost benefit may still carry capacity or opportunity costs; paid merchandise may be less attractive when its direct cost is high relative to the utility improvement.
The case requires 200-level midcourt tickets to be priced at $60 or above and 300-level midcourt tickets at $25 or above. The full factorial contains 240 profiles; these restrictions exclude 60, leaving 180 feasible packages. The code rechecks these policies before selecting any management candidate.
feasibility_summary <- data.frame(
category = c("Theoretical profiles", "Policy-compliant profiles", "Excluded profiles"),
count = c(240L, nrow(packages), 240L - nrow(packages)))
assert(all(packages$price_usd[packages$seat_id == 4L] >= 60),
"Premium midcourt seat minimum price violation")
assert(all(packages$price_usd[packages$seat_id == 3L] >= 25),
"300-level midcourt minimum price violation")
knitr::kable(feasibility_summary, caption = "Permitted package design space")| category | count |
|---|---|
| Theoretical profiles | 240 |
| Policy-compliant profiles | 180 |
| Excluded profiles | 60 |
Interpretation. The best unconstrained combination is not necessarily available for sale. Screening against the actual seat-price policy is therefore a prerequisite for every recommendation.
The project reports separate leaders for consumer utility, modeled contribution per package, and modeled contribution per game. The latter distinction matters because a longer package can create greater contribution per sale without being the most preferred offer. This section obtains the leaders from Module 5 and joins them to the complete Module 4 package attributes.
objective_labels <- c(
utility = "Consumer utility",
contribution_A = "Contribution per package: promotion once",
contribution_B = "Contribution per package: promotion per game",
contribution_A_per_game = "Contribution per game: promotion once",
contribution_B_per_game = "Contribution per game: promotion per game")
objective_summary <- leaders05 |>
filter(objective %in% names(objective_labels)) |>
select(objective, package_id) |>
left_join(packages |> select(all_of(required_pkg)), by = "package_id") |>
mutate(decision_objective = unname(objective_labels[objective])) |>
select(decision_objective, package_id, games_n, price_usd, seat_id, promo_id,
utility, contribution_A, contribution_B)
assert(nrow(objective_summary) == 5L && !anyNA(objective_summary$utility),
"Five validated objective leaders are required")
knitr::kable(objective_summary, digits = 3,
caption = "Different objectives identify potentially different offers")| decision_objective | package_id | games_n | price_usd | seat_id | promo_id | utility | contribution_A | contribution_B |
|---|---|---|---|---|---|---|---|---|
| Consumer utility | PKG077 | 6 | 25 | 3 | 2 | 0.796 | 38.75 | 22.5 |
| Contribution per package: promotion once | PKG012 | 10 | 60 | 1 | 1 | -1.886 | 500.00 | 500.0 |
| Contribution per package: promotion per game | PKG012 | 10 | 60 | 1 | 1 | -1.886 | 500.00 | 500.0 |
| Contribution per game: promotion once | PKG011 | 6 | 60 | 1 | 1 | -1.365 | 300.00 | 300.0 |
| Contribution per game: promotion per game | PKG011 | 6 | 60 | 1 | 1 | -1.365 | 300.00 | 300.0 |
preference_leader <- objective_summary |>
filter(decision_objective == "Consumer utility") |> slice(1)
assert(nrow(preference_leader) == 1L, "A unique preference-leader row is required")Results and interpretation. The top utility profile is PKG077, consisting of 6 games at $25.00 per game, seat level 3, and promotion level 2. Its additive utility is 0.79558. The comparison makes clear why a consumer-preference leader is a candidate for testing, while contribution-oriented alternatives may be more appropriate when management’s objective is unit economics.
The code constructs a concise shortlist from three evidence channels: the utility winner, the baseline contribution leaders, and the profiles most frequently on the utility–contribution Pareto frontier across Module 5’s 18 cost scenarios. This is a transparent screening rule rather than a forecast-based optimization. Package identifiers and all metrics are read from the computed outputs.
frontier_persistence <- frontier_persistence |>
mutate(frontier_scenarios = as.integer(frontier_scenarios),
frontier_share = as.numeric(frontier_share))
frontier_top <- frontier_persistence |>
arrange(desc(frontier_scenarios), desc(frontier_share), desc(utility), package_id) |>
slice_head(n = 5)
shortlist_ids <- unique(c(preference_leader$package_id,
objective_summary$package_id,
frontier_top$package_id))
shortlist <- packages |>
filter(package_id %in% shortlist_ids) |>
left_join(frontier_persistence |>
select(package_id, frontier_scenarios, frontier_share), by = "package_id") |>
mutate(selection_reason = vapply(package_id, function(id) {
reasons <- c(
if (id == preference_leader$package_id) "Preference leader" else NULL,
if (id %in% objective_summary$package_id[objective_summary$decision_objective != "Consumer utility"])
"Accounting objective leader" else NULL,
if (id %in% frontier_top$package_id) "Persistent Pareto candidate" else NULL)
paste(reasons, collapse = "; ")
}, character(1))) |>
arrange(desc(utility), package_id)
assert(nrow(shortlist) >= 1L && !anyDuplicated(shortlist$package_id),
"Management shortlist must contain unique feasible profiles")
shortlist_display <- shortlist |>
select(package_id, games_n, price_usd, seat_id, promo_id,
utility, contribution_A, contribution_B, frontier_scenarios, selection_reason)
knitr::kable(shortlist_display, digits = 3,
caption = "Evidence-based shortlist for operational review and market testing")| package_id | games_n | price_usd | seat_id | promo_id | utility | contribution_A | contribution_B | frontier_scenarios | selection_reason |
|---|---|---|---|---|---|---|---|---|---|
| PKG077 | 6 | 25 | 3 | 2 | 0.796 | 38.75 | 22.5 | 18 | Preference leader; Persistent Pareto candidate |
| PKG029 | 6 | 25 | 3 | 1 | 0.746 | 42.00 | 42.0 | 18 | Persistent Pareto candidate |
| PKG080 | 6 | 35 | 3 | 2 | 0.701 | 98.75 | 82.5 | 18 | Persistent Pareto candidate |
| PKG032 | 6 | 35 | 3 | 1 | 0.652 | 102.00 | 102.0 | 18 | Persistent Pareto candidate |
| PKG033 | 10 | 35 | 3 | 1 | 0.132 | 170.00 | 170.0 | 18 | Persistent Pareto candidate |
| PKG011 | 6 | 60 | 1 | 1 | -1.365 | 300.00 | 300.0 | 0 | Accounting objective leader |
| PKG012 | 10 | 60 | 1 | 1 | -1.886 | 500.00 | 500.0 | 18 | Accounting objective leader |
Results and interpretation. The shortlist contains 7
distinct feasible designs. Its selection criteria preserve different
management objectives instead of collapsing consumer utility and
contribution into an arbitrary composite score. The
frontier_scenarios column reports the number of assumed
cost scenarios in which each profile is nondominated; it is
not an estimated probability of market success.
Module 5 perturbs the 16 source part-worths at explicitly selected magnitudes and reranks the same 180 feasible packages. The simulation evaluates whether a preferred offer remains a leading candidate under hypothetical changes in model inputs. The table below presents the saved simulation summary without treating the perturbations as estimated sampling uncertainty.
assert(nrow(stability) > 0L, "Module 5 ranking stability output is empty")
knitr::kable(stability, digits = 4,
caption = "Hypothetical preference-perturbation results from Module 5")| stress_delta | iterations | winner_retention_rate | mean_rank_correlation | median_top10_overlap | mean_top10_overlap |
|---|---|---|---|---|---|
| 0.01 | 1000 | 1.000 | 0.9998 | 1.0 | 0.9708 |
| 0.03 | 1000 | 0.981 | 0.9987 | 0.9 | 0.9326 |
| 0.05 | 1000 | 0.857 | 0.9967 | 0.9 | 0.8969 |
Interpretation. Winner retention, top-ten overlap and rank correlation characterize different aspects of stability: the first concerns the exact top-ranked profile, the second concerns the candidate set, and the third concerns the broader ranking. For practical decisions, stable membership in a high-quality shortlist can be more useful than insisting on one unchanging winner.
The economic stress test crosses three seat-cost multipliers, three promotional-cost multipliers, and two fulfillment conventions. The resulting 18 scenarios provide an operational view of which contribution leaders change when accounting assumptions change.
cost_summary <- data.frame(
metric = c("Cost scenarios", "Distinct contribution-per-package leaders",
"Distinct contribution-per-game leaders",
"Distinct persistent Pareto candidates"),
result = c(nrow(cost_scenarios),
length(unique(cost_scenarios$leader_package)),
length(unique(cost_scenarios$per_game_leader)),
sum(frontier_persistence$frontier_scenarios > 0L)))
knitr::kable(cost_summary, caption = "Decision sensitivity across economic assumptions")| metric | result |
|---|---|
| Cost scenarios | 18 |
| Distinct contribution-per-package leaders | 1 |
| Distinct contribution-per-game leaders | 1 |
| Distinct persistent Pareto candidates | 22 |
Results and interpretation. Across 18 scenarios, 1 distinct offers lead on contribution per package, and 1 lead on contribution per game. The exercise clarifies which cost assumptions should be verified before an offer is scaled. A scenario is a specified accounting possibility, not a likelihood-weighted forecast.
The scatterplot positions each shortlisted package by consumer utility and modeled contribution per package under the once-per-package promotion convention. It visualizes the management trade-off without forcing utility units and dollars into a single numerical objective.
p_shortlist <- ggplot(packages, aes(x = utility, y = contribution_A)) +
geom_point(color = "grey80", size = 1.4, alpha = 0.6) +
geom_point(data = shortlist, color = "#246A73", size = 2.8) +
geom_text(data = shortlist,
aes(label = package_id), check_overlap = TRUE, vjust = -0.7, size = 3) +
labs(x = "Aggregate additive utility",
y = "Modeled contribution per package (USD; promotion once)",
title = "Management shortlist across consumer appeal and unit economics",
subtitle = "Each point is a feasible design; labeled points meet the shortlist criteria") +
theme_minimal(base_size = 11)
print(p_shortlist)Interpretation. Designs higher on the vertical axis offer greater conditional contribution per sale; designs farther right have higher modeled utility. A package that is attractive on both dimensions is a strong testing candidate. The plot does not account for conversion rates, market size, seat inventory or substitution between packages.
Seat location and price account for the largest shares of the observed utility range. Management should use seat–price combinations as the primary structure for offer differentiation, while using package length and promotions to create targeted value. The nonlinear price response indicates that a $10 discount can have substantially different utility effects depending on the starting price. Discount policies should therefore be evaluated at the specific proposed price points.
Decision rule. Retain the case’s minimum-price restrictions, evaluate the incremental utility of each permitted change, and compare the resulting offer with alternatives that improve seats, game selection or benefits at a manageable delivery cost.
The aggregate model favors six games over three and ten. A six-game package therefore provides a defensible starting configuration for a preference-led offer. Longer packages may still lead on contribution per sale because they include more paid games; management should distinguish consumer appeal, contribution per package, and contribution per game when assessing package length.
Decision rule. Include the six-game preference leader in the pilot, alongside at least one economically attractive alternative from the shortlist. Evaluate actual conversion, incremental attendance and seat availability before expanding the assortment.
Hot dog and soda delivers a relatively large modeled utility improvement at a modest reported direct cost. Its attractiveness depends on whether the benefit is redeemed once per package or for every game. Playoff priority may have no direct cost in the workbook but can impose inventory and opportunity costs. Merchandise and gift certificates require the same incremental-benefit-versus-fulfillment assessment.
Decision rule. Specify redemption frequency and expected utilization for each promotional benefit. Obtain operational cost confirmation and compare the offer’s incremental utility with a feasible alternative at the same ticket price.
The preference leader, contribution leaders and repeatedly Pareto-efficient profiles serve different managerial objectives. The final shortlist makes these trade-offs explicit. A smaller set of differentiated packages also makes a prospective experiment easier to interpret and administer.
Decision rule. Advance shortlisted offers only after checking seat inventory, promotional fulfillment, sales-channel implementation and minimum-price compliance. Where the preference and economic rankings disagree, test the competing offers rather than assuming one objective dominates the other.
A pilot can evaluate whether stated preferences translate into observed ticket purchases. Assign eligible customers to a small set of policy-compliant package offers, maintain consistent marketing exposure, and measure the commercial outcomes that the aggregate conjoint table cannot identify.
pilot_plan <- data.frame(
stage = c("1. Operational screening", "2. Offer design", "3. Controlled launch",
"4. Measurement", "5. Decision review"),
action = c(
"Confirm inventory, minimum prices, allocated versus incremental seat costs, and promotion redemption rules",
"Select a small, differentiated set from the evidence-based shortlist and document the offer components",
"Use a randomized or otherwise explicitly controlled offer comparison where operationally feasible",
"Track exposure, purchases, conversion, revenue, incremental contribution, redemption and attendance",
"Re-estimate demand and contribution with observed outcomes; retain, revise or discontinue each offer"),
evidence_needed = c(
"Seat inventory and finance/operations cost data",
"Final package descriptions and consistent eligibility rules",
"Offer assignment, dates, channels and customer eligibility",
"Transaction records, redemption and fulfillment costs",
"Observed incremental performance and uncertainty estimates"),
stringsAsFactors = FALSE)
knitr::kable(pilot_plan, caption = "Operational roadmap from conjoint screening to empirical validation")| stage | action | evidence_needed |
|---|---|---|
| 1. Operational screening | Confirm inventory, minimum prices, allocated versus incremental seat costs, and promotion redemption rules | Seat inventory and finance/operations cost data |
| 2. Offer design | Select a small, differentiated set from the evidence-based shortlist and document the offer components | Final package descriptions and consistent eligibility rules |
| 3. Controlled launch | Use a randomized or otherwise explicitly controlled offer comparison where operationally feasible | Offer assignment, dates, channels and customer eligibility |
| 4. Measurement | Track exposure, purchases, conversion, revenue, incremental contribution, redemption and attendance | Transaction records, redemption and fulfillment costs |
| 5. Decision review | Re-estimate demand and contribution with observed outcomes; retain, revise or discontinue each offer | Observed incremental performance and uncertainty estimates |
Interpretation. The pilot adds the behavioral evidence required to estimate conversion and incremental economics. Tracking incremental contribution across actual sales, rather than contribution per hypothetical package sold, is the appropriate next step toward a commercially validated product decision.
The project yields five connected conclusions:
The project integrates conjoint preference measurement, price-equivalent attribute trade-offs, exhaustive constrained enumeration, conditional unit-economics calculations, Pareto analysis, and sensitivity testing in one reproducible workflow. The combined framework converts a set of aggregate part-worth utilities into concrete, auditable product-design decisions.
Its most important methodological feature is the separation of three layers of evidence: source estimates (the workbook’s part-worths and reported costs), deterministic implications (importance, feasible rankings and accounting calculations), and scenario assumptions (hypothetical utility perturbations and cost multipliers). This structure makes it possible to trace each recommendation to the evidence and decision rules that support it.
The available workbook provides aggregate part-worths, rather than respondent-level choice tasks or transaction outcomes. Consequently, the analysis cannot estimate customer-level preference heterogeneity, infer standard errors or confidence intervals for the supplied part-worths, validate held-out choice predictions, or forecast purchase probabilities and market shares. The utility-perturbation experiments explore assumed parameter changes; they are not a substitute for statistical uncertainty estimated from respondent data.
The economic analysis reports conditional contribution per package sold under two promotional fulfillment conventions. Actual profit requires observed demand, incremental costs, inventory and capacity constraints, cannibalization effects, and a defined commercial time horizon. Range-based attribute importance is also dependent on the specific attribute levels included in the conjoint design.
Future research should estimate a choice model from respondent-level tasks, assess customer segments and uncertainty, calibrate preferences against observed purchase behavior, and integrate inventory-constrained revenue management. A prospective offer experiment can then test whether the shortlist improves actual conversion, attendance and incremental contribution.
The final checks verify the source design, policy compliance, utility arithmetic, accounting formulas, and links to earlier module outputs. These checks protect the final decision tables against accidental changes in data types, join keys or calculation conventions.
lookup <- function(attribute, ids) {
tab <- partworth |> filter(.data$attribute == .env$attribute) |>
select(level_id, utility)
tab$utility[match(ids, tab$level_id)]
}
recomputed_utility <- lookup("Number of Games", packages$games_id) +
lookup("Ticket Price", packages$price_id) +
lookup("Ticket Location", packages$seat_id) +
lookup("Promotional Item", packages$promo_id)
assert(all(is.finite(recomputed_utility)), "Utility joins contain missing values")
revenue_check <- packages$games_n * packages$price_usd
contribution_A_check <- packages$games_n *
(packages$price_usd - packages$seat_cost_usd) - packages$promo_cost_usd
contribution_B_check <- packages$games_n *
(packages$price_usd - packages$seat_cost_usd - packages$promo_cost_usd)
checks <- data.frame(
check = c("Source levels equal 16", "Theoretical combinations equal 240",
"Feasible packages equal 180", "All seat-price restrictions satisfied",
"Additive utility reconciles", "Package revenue reconciles",
"Contribution A reconciles", "Contribution B reconciles",
"Preference leader matches highest utility", "All 18 cost scenarios present"),
passed = c(nrow(partworth) == 16L, prod(as.integer(table(partworth$attribute))) == 240L,
nrow(packages) == 180L,
all(packages$price_usd[packages$seat_id == 4L] >= 60) &&
all(packages$price_usd[packages$seat_id == 3L] >= 25),
max(abs(recomputed_utility - packages$utility)) < 1e-7,
max(abs(revenue_check - packages$revenue_per_package)) < 1e-7,
max(abs(contribution_A_check - packages$contribution_A)) < 1e-7,
max(abs(contribution_B_check - packages$contribution_B)) < 1e-7,
abs(preference_leader$utility - max(packages$utility)) < 1e-7,
nrow(cost_scenarios) == 18L))
assert(all(checks$passed), paste("Final validation failed:",
paste(checks$check[!checks$passed], collapse = "; ")))
knitr::kable(checks, caption = "Final research synthesis validation")| check | passed |
|---|---|
| Source levels equal 16 | TRUE |
| Theoretical combinations equal 240 | TRUE |
| Feasible packages equal 180 | TRUE |
| All seat-price restrictions satisfied | TRUE |
| Additive utility reconciles | TRUE |
| Package revenue reconciles | TRUE |
| Contribution A reconciles | TRUE |
| Contribution B reconciles | TRUE |
| Preference leader matches highest utility | TRUE |
| All 18 cost scenarios present | TRUE |
Results and interpretation. All 10 final checks must pass before the report exports its management tables. The validation covers arithmetic and feasibility; the separate empirical research agenda addresses the prediction and demand questions that require new observations.
The exported files provide a compact record of the final evidence, shortlist, recommendations and validation status. Each table is derived from the same verified inputs used in the report.
recommendations <- data.frame(
priority = c(1, 2, 3, 4, 5),
recommendation = c(
"Use seat-price combinations as the primary offer design lever",
"Pilot a six-game, high-utility offer alongside an economic alternative",
"Evaluate promotions by incremental utility, redemption frequency and cost",
"Compare utility, contribution and Pareto persistence before launch",
"Validate conversion and incremental contribution in a controlled pilot"),
supporting_evidence = c(
"Module 2 attribute importance and Module 3 price response",
"Module 2 game-count preference and Module 4 feasible objective leaders",
"Module 3 promotion valuation and Module 5 economic scenarios",
"Modules 4–5 objective leaders and scenario frontiers",
"Module 5 validation limits and Module 6 implementation plan"),
stringsAsFactors = FALSE)
research_evidence <- data.frame(
conclusion = c("Seat and price are the dominant modeled attributes",
"Price sensitivity varies by interval",
"Promotion value depends on the cost and price reference",
"The optimal offer depends on the objective",
"Cost assumptions can change the accounting decision",
"Market demand and realized profit require field evidence"),
evidence_type = c("Workbook-derived", "Workbook-derived", "Workbook-derived plus accounting assumptions",
"Deterministic model implication", "Assumption-based sensitivity",
"Empirical evidence gap"),
supporting_module = c("02", "03", "03", "04", "05", "05–06"),
stringsAsFactors = FALSE)
save_table(input_inventory, "06_evidence_inventory.csv")
save_table(level_extremes, "06_preference_level_summary.csv")
save_table(price_differences, "06_price_response_summary.csv")
save_table(promo_summary, "06_promotion_value_summary.csv")
save_table(objective_summary, "06_objective_leader_comparison.csv")
save_table(shortlist_display, "06_management_shortlist.csv")
save_table(cost_summary, "06_cost_sensitivity_summary.csv")
save_table(pilot_plan, "06_pilot_implementation_plan.csv")
save_table(recommendations, "06_managerial_recommendations.csv")
save_table(research_evidence, "06_research_evidence_matrix.csv")
save_table(checks, "06_final_validation.csv")
save_figure(p_importance, "06_attribute_importance.png", 9, 5.5)
save_figure(p_price, "06_price_utility_curve.png", 9, 5.5)
save_figure(p_shortlist, "06_management_shortlist_tradeoff.png", 10, 6)
manifest <- data.frame(type = c(rep("CSV", 11), rep("PNG", 3)),
filename = c("06_evidence_inventory.csv", "06_preference_level_summary.csv",
"06_price_response_summary.csv", "06_promotion_value_summary.csv",
"06_objective_leader_comparison.csv", "06_management_shortlist.csv",
"06_cost_sensitivity_summary.csv", "06_pilot_implementation_plan.csv",
"06_managerial_recommendations.csv", "06_research_evidence_matrix.csv",
"06_final_validation.csv", "06_attribute_importance.png",
"06_price_utility_curve.png", "06_management_shortlist_tradeoff.png"))
knitr::kable(manifest, caption = "Final research and management deliverables")| type | filename |
|---|---|
| CSV | 06_evidence_inventory.csv |
| CSV | 06_preference_level_summary.csv |
| CSV | 06_price_response_summary.csv |
| CSV | 06_promotion_value_summary.csv |
| CSV | 06_objective_leader_comparison.csv |
| CSV | 06_management_shortlist.csv |
| CSV | 06_cost_sensitivity_summary.csv |
| CSV | 06_pilot_implementation_plan.csv |
| CSV | 06_managerial_recommendations.csv |
| CSV | 06_research_evidence_matrix.csv |
| CSV | 06_final_validation.csv |
| PNG | 06_attribute_importance.png |
| PNG | 06_price_utility_curve.png |
| PNG | 06_management_shortlist_tradeoff.png |
The conjoint evidence supports a disciplined approach to multigame ticket design: start with the seat, price and package features customers value; enforce the organization’s pricing policies; compare permitted offers on both preference and conditional economics; and test a small, robust shortlist in the market.
The preferred six-game offer provides a consumer-oriented starting point, while alternative contribution leaders and persistently efficient designs provide economically motivated comparisons. The strongest management recommendation is therefore a validated, multiobjective shortlist followed by a controlled commercial pilot, rather than immediate commitment to a single mathematically highest-scoring package.
This framework links consumer preference analysis to pricing, operational feasibility and evidence-based product decisions. It provides a transparent basis for management action today and a concrete research path toward demand estimation and inventory-aware revenue optimization.
Table 1. Utility score data.,
Product Analysis, Willingness to pay and
Attribute Tradeoff worksheets. Primary numerical inputs and
case calculations.