Sensitivity analysis: what the tornado chart can and cannot tell you

Why the dollar-swing bars are hard to interpret, and six other ways to ask which uncertainties matter

← Back to the tornado chart Dollar-swing definition (docs)

The Advanced Model page ends with a tornado chart: one bar per input, showing how much the mean cost differs between draws where that input is in its top 10% and draws where it is in its bottom 10%. It is easy to read and, as a first pass, useful. It is also easy to over-read. Several of the inputs move together by construction, the 10% cut is arbitrary, the mean is pulled around by the right tail, and the question the chart answers (“what do costs look like in the worlds where this input is high?”) is not the question people usually have in mind (“what happens to costs if this input changes?”). Neither is the question that matters for research prioritization (“would learning this change what we would do?”).

This page works through those problems with live numbers and then runs the alternatives on the same scenario. Everything below is computed from the same 30,000-draw simulation the main page uses, except the Shapley table, which is precomputed offline for the default scenario. The methods and text were drafted with AI assistance and have not been independently reviewed; the code is in sensitivity-methods.mjs with tests, and we welcome corrections via Hypothes.is annotations.

1. What a bar on the tornado chart is

For each input \(x\), the chart sorts the 30,000 draws by \(x\), takes the top 10% and the bottom 10% (3,000 draws each), and reports the difference in mean unit cost between the two groups:

\[\text{Swing}(x) = \overline{\text{cost}}\,\big|\,x \in \text{top 10\%} \;-\; \overline{\text{cost}}\,\big|\,x \in \text{bottom 10\%}.\]

That is all. There is no re-simulation, no holding anything else fixed, and no model of how \(x\) enters the cost equation. It is a summary of the joint distribution the simulation happened to produce.

2. Why the bars are hard to interpret

2.1 The inputs move together by construction

The chart lists inputs side by side as if each were an independent dial. In the engine they are not:

Coupling in the engine What it does to the bars
Process mode → density and media-use multiplier. Each draw first picks fed-batch, perfusion or continuous, then draws density and the multiplier from that mode’s range (fed-batch 5–30 g/L with multiplier 1–2; perfusion 30–150 g/L, 1–5; continuous 50–200 g/L, 0.5–3). The top density decile is almost entirely continuous and perfusion draws; the bottom decile is fed-batch. The density bar is partly a process-mode bar, and it is entangled with the multiplier bar.
Maturity → adoption, financing and equipment. One latent maturity draw shifts the hydrolysate and cheap-GF adoption probabilities, the supplemental-protein regime, WACC and the custom-reactor share. The maturity bar bundles all of those channels. It overlaps with the Media $/L, GF price, GF quantity and WACC bars, which already carry the regime effects it induces.
Regime switches inside the mixtures. “Media $/L” mixes a hydrolysate regime and a pharma-grade regime; “GF price” and “GF quantity” mix a cheap and an expensive regime, and both switch together. The GF price and GF quantity bars share the same coin flip, so they overlap heavily. Each mixture bar is a regime effect plus a within-regime effect.
Deterministic identities. Fresh media L/kg = 1000 / density × multiplier; GF cost = quantity × price. Removed from the chart for this reason, but the parents still co-determine cost in a multiplicative way.

The consequence is that a bar answers “what do costs look like in the worlds where this input is high?” and those worlds differ from the low-input worlds in other respects too. Bars cannot be added, and no bar isolates “the effect of” its input. The rank correlations among the displayed inputs, computed from the current draws, show the couplings directly:

Spearman rank correlations between the displayed inputs across the 30,000 draws. Red: move together; blue: move in opposite directions. Correlations near zero mean the engine samples the pair independently in this scenario; they say nothing about whether the pair is really independent.

2.2 Some uncertainties are probably correlated in ways the model does not encode

The couplings above are the ones the engine builds in. There are others it leaves out. Media cost per litre and achievable cell density are plausibly linked (denser cultures need richer, more expensive media, at least in fed-batch systems), but the engine samples them independently within a process mode, so the joint distribution the chart summarizes puts weight on combinations an expert might rule out — very cheap media with very high density, for example. The chart cannot detect this; it only summarizes the draws it is given. The beliefs form asks for media cost per kilogram of biomass, rather than $/L and density separately, partly so that respondents can integrate this interaction themselves. The Limits page discusses it further.

2.3 “Top 10% versus bottom 10%” and “mean cost” are choices

Nothing in the method fixes the 10% cut or the mean. Comparing the top and bottom 5% gives bigger bars (more extreme worlds); comparing the top and bottom halves gives smaller ones. The mean cost is dominated by the right tail (in the default scenario the mean is about $73/kg against a median of $43/kg), so a bar mostly reports what happens in the expensive tail. If the question is whether cultivated chicken can get under $25/kg, the relevant statistic is a threshold probability, and that ranks the inputs differently. Try it:

Bar colour gives the sign (red: high input goes with higher cost or a lower chance of clearing the threshold; green: the opposite). Black whiskers are ±2 Monte Carlo standard errors. For the threshold statistics, green means a high value of the input goes with a higher probability of cheap cultivated meat.

2.4 Small bars are noise

Each bar is a difference between two sample means of 3,000 draws with a standard deviation around $100/kg, so its standard error is a few dollars per kilogram. In the default scenario, WACC, asset life, plant capacity and utilization all have bars of $1–5/kg, which is within noise; the chart on the main page nonetheless draws and labels them. The whiskers above show the same problem for other statistics.

2.5 Two different questions: learning an input versus setting it

The chart, and every statistic in this section, conditions on the input: it compares worlds in which the input happens to be high with worlds in which it happens to be low. Under the model’s dependence structure, that is the right way to answer “if we learned this input’s value, what would we expect cost to be?”, because learning that density is high also tells you the process is probably continuous, and the conditional statistic carries that along.

It is the wrong way to answer “if this input were changed while everything else stayed as it is, what would happen to cost?”, which is what an engineer improving a process or a funder backing a technology usually means. That needs an intervention: re-run the simulation with the input pinned. When inputs are independent, the two answers coincide; here they do not (Section 3.5 shows the gap for the inputs the engine lets us pin). Neither answer is more correct in general; they are answers to different questions, and the chart only gives the first.

2.6 None of this is research value

The September review makes the point directly: a large bar can have low information value if every plausible value of the input supports the same decision, and a small bar can matter if the decision is on a knife edge. Deciding what to study requires specifying a choice (fund a medium improvement, obtain a scale-up measurement, support deployment) and asking whether plausible evidence would change it. None of the statistics on this page do that. They describe the model’s uncertainty; they do not price it.

3. Alternatives, run on this scenario

3.1 Conditional-mean profiles: where in the range the cost moves

Instead of two tails, look at all ten deciles. For each input, the red line is the mean cost within each decile of that input, the dashed blue line is the median, and the band is the 10th–90th percentile of cost within the decile. Steep, straight profiles are what the tornado bar assumes; curved profiles show that the swing comes from one end of the range; flat profiles with wide bands are inputs the bar ranks by noise.

Log scale on cost. Deciles are equal-count bins of the input’s own distribution (decile 1 is the lowest 10%, decile 10 the highest), so the horizontal axis is not linear in the input’s units.

3.2 Expected uncertainty after learning one input

This is the closest thing to the question “how much would our uncertainty shrink if we pinned this input down?” Split the draws into 30 equal-count bins of the input, measure the spread of cost within each bin, and average. The share of variance removed, \(\eta^2 = 1 - \mathbb{E}[\operatorname{Var}(\text{cost} \mid x)]/\operatorname{Var}(\text{cost})\), is the first-order Sobol index when inputs are independent; under dependence it also counts the information the input carries about correlated inputs, so the shares can add up to more than 100%. The 80% interval width is the more robust companion, because the variance of a heavy-tailed cost is driven by a few extreme draws.

3.3 Rank regression: partial adjustment for co-movement

Regress the rank of cost on the ranks of all displayed inputs at once. The standardized coefficients (SRRC) describe each input’s monotone association with cost holding the other listed inputs at their ranks, which is a partial correction for the overlap problem: an input whose association runs entirely through other listed inputs gets a coefficient near zero. The \(R^2\) says how much of the rank variation the listed inputs explain additively; the remainder is unlisted inputs (other variable costs, reactor and plant-factor draws, supplemental proteins, fixed costs) plus interactions and non-monotone effects. It is still a description of the joint sample, not an intervention.

3.4 Interventions: pin one input and re-run

For the inputs the engine exposes as ranges, we can set the input to a single value and re-simulate with the same seed, so every other draw is unchanged. The set-points are the mean of the input within the bottom and top 10% tails, which makes the intervention comparable with the chart’s conditional contrast. The difference between the two columns is the dependence structure at work.

In the default scenario the density intervention is larger than the conditional contrast. Setting density to the bottom-tail value for every draw, including perfusion draws with high media-use multipliers, produces media volumes the engine never generates on its own (low density with a fed-batch multiplier is what the conditional bottom tail contains). So the intervention breaks a coupling that is there for a physical reason, and its answer is not “the effect of density” either. When inputs are structurally linked, the honest statement is that the effect of one input is only defined relative to what is assumed about the others.

3.5 Shapley effects: a variance split that respects dependence

Sobol indices split the output variance into shares attributable to each input, but the classical decomposition requires independent inputs, which we do not have. Shapley effects (Owen 2014; Song, Nelson and Staum 2016; Iooss and Prieur 2019) are the standard answer to that problem. They borrow the Shapley value from cooperative game theory: treat each input as a player, define the “value” of any group of inputs as the variance they jointly explain, \(c(u) = \operatorname{Var}(Y) - \mathbb{E}[\operatorname{Var}(Y \mid X_u)]\), and give each input the average of its marginal contribution over all orders in which the inputs could be added. The result is a set of shares that sum to the explained variance even when inputs are dependent, with each input’s share lying between its first-order and total-effect Sobol index. So the answer to “is this relevant here?” is yes: it is precisely the method built for dependent inputs like ours.

Two practical limits. First, computing \(\mathbb{E}[\operatorname{Var}(Y \mid X_u)]\) for every subset \(u\) needs conditional distributions of the inputs, which the engine does not expose; we use the given-data estimator of Broto, Bachoc and Depecker (2020), which approximates the conditional variance by the variance of cost among each draw’s nearest neighbours in the subset’s coordinates. That estimator degrades quickly as the number of inputs grows, because neighbourhoods in many dimensions are wide: with all eleven inputs it returns negative shares and explains only about half the variance. It behaves well for the six inputs that carry nearly all the variance, on log cost, with two independent subsamples agreeing closely. Second, the shares depend on how the inputs are carved up. “Media $/L including the hydrolysate regime” is one input here; split it into the regime switch and the within-regime price and the attribution changes.

Reproduce with node scripts/sensitivity-shapley.mjs, which writes sensitivity-shapley-2026-09.json with the full-sample statistics for every input alongside the runs.

3.6 What is still not done

  • Sobol indices on independent primitive inputs. The engine draws about twenty-five independent primitives (the mode-selection uniform, the Beta noise on each adoption probability, the within-regime lognormals, the reactor, plant-factor and fixed-cost draws, and so on). A classical Sobol decomposition on those would be exact and cheap with the pick-freeze scheme, but the engine would need to accept the primitives as inputs, and most primitives are not quantities anyone would set out to research (“the uniform draw that picks the process mode” is not a research target). Attributing to the displayed, aggregated inputs is more meaningful and less clean; that trade-off is inherent, not an implementation gap.
  • Multi-factor dependence. All of the above takes the single latent maturity factor as given. The structural comparison on the main page shows what independent maturity channels do to the headline numbers; rerunning this page’s statistics under that setting is one click away via the Advanced page link.
  • Value of information. The decision-relevant version of this analysis (expected value of perfect or sample information for a specified choice) needs a decision and a loss function. We have neither on the site yet.

4. Summary

Method Question it answers Handles the engine’s dependence? Unit Where
Tornado (tail contrast, mean) In worlds where \(x\) is high, how much higher is mean cost than in worlds where it is low? Describes the joint sample; bars overlap and cannot be added $/kg Main page; Section 2.3 with other tails and statistics
Threshold contrast Same, for P(cost below a target) Same percentage points Section 2.3
Conditional profiles How does cost change across the whole range of \(x\)? Same $/kg by decile Section 3.1
Expected uncertainty after learning \(x\) (η², interval width) If we learned \(x\), how much narrower would the cost distribution be? Yes, in the “learning” sense: counts information carried by correlated inputs share; $/kg Section 3.2
Rank regression (SRRC) Monotone association with cost, adjusting for the other listed inputs Partially, among listed inputs SD units Section 3.3
Intervention contrast If \(x\) were set to a value, everything else unchanged, what would cost be? Breaks the dependence by design; only for pinnable inputs $/kg Section 3.4
Shapley effects How should the variance be split among inputs so the shares add up? Yes, by construction; estimator is the weak point share of variance Section 3.5 (default scenario, offline)
Sobol on primitives, value of information Exact variance split; decision relevance Not implemented

The practical reading for this model, in the default scenario: cell density and the growth-factor inputs dominate every measure, media $/L and the media-use multiplier come next, and the financing, scale, timing and utilization inputs are within noise on every measure. The methods disagree about the ordering and size of the middle group, which is the honest result: the middle group’s ranking depends on which question you ask.

Estimators: sensitivity-methods.mjs; tests: tests/sensitivity-methods.test.mjs; offline run: scripts/sensitivity-shapley.mjs. References: Owen, A. B. (2014) “Sobol’ indices and Shapley value,” SIAM/ASA J. Uncertainty Quantification; Song, E., Nelson, B. L. and Staum, J. (2016) “Shapley effects for global sensitivity analysis,” SIAM/ASA JUQ; Iooss, B. and Prieur, C. (2019) “Shapley effects for sensitivity analysis with correlated inputs,” Int. J. Uncertainty Quantification; Broto, B., Bachoc, F. and Depecker, M. (2020) “Variance reduction for estimation of Shapley effects and adaptation to unknown input distribution,” SIAM/ASA JUQ; Saltelli et al. (2008) Global Sensitivity Analysis: The Primer.