1. Key Concepts and the VOI Pipeline
A plain-language primer and step-by-step walkthrough
Source:vignettes/vira-01-concepts.Rmd
vira-01-concepts.RmdThis vignette defines the core concepts of Value of Information (VOI)
analysis and walks through how the vira package computes
VOI step-by-step.
If you are new to VOI, start with the Key Concepts and Glossary below. If you want to see how the code is structured, skip to The VOI Pipeline Walkthrough.
The core question: is it worth collecting more data?
Every field ecologist eventually faces this situation: you have a management decision to make, you are uncertain about something important, and someone suggests running a survey or study first. The question is simple — is the information worth the cost?
Value of Information (VOI) analysis answers that question formally. The key idea is:
Information is only worth collecting if it could change what you would do. If you would make the same decision regardless of the survey result, the survey has zero value — no matter how uncertain you are.
Glossary
Terms used throughout these vignettes
EVPI (Expected Value of Perfect Information) The most you should ever pay for a study. Specifically: the average improvement in outcome you would get if you somehow knew the true state of the world before deciding, rather than making your best guess now. Think of it as the ceiling on any study’s worth.
EVSI (Expected Value of Sample Information) Like EVPI, but for a real, imperfect study. Always EVPI because real studies are not perfect.
Property 1 — dominant action A situation where one action is best (or tied for best) regardless of the true state of the world. Example: “protect the parcel” is the right call whether the species is present or absent, because protection never makes things worse. When a dominant action exists, information cannot change what you should do, so EVPI = 0.
Property 2 — no safe fallback A situation where every action is the wrong choice in at least one state of the world. Example: fishing the estuary is wrong when the fish are offshore, and fishing offshore is wrong when they are in the estuary. No choice is “safe”. Here, uncertainty is genuinely costly and EVPI can be large.
Risk aversion A preference for a certain, moderate outcome over a gamble with the same expected value. A risk-averse manager would rather guarantee 500 fish than have a 50/50 chance of 200 or 800 fish (which averages to 500). We describe the degree of risk aversion with a single number (gamma):
- : risk-neutral (only expected value matters)
- : risk-averse (bad outcomes are penalised extra)
- : risk-seeking (drawn to variance, like a gambler)
Certainty equivalent (CE) The guaranteed outcome a risk-averse manager considers just as good as an uncertain gamble. If a manager would accept 430 certain fish rather than face a 50/50 gamble of 200 or 800 fish, their certainty equivalent for that gamble is 430. EVPI under risk aversion is always expressed as the gain in CE from getting perfect information.
CRRA utility The specific mathematical formula used to model risk aversion in these vignettes. You do not need to know the formula. Think of it as a dial: turning up makes the manager increasingly averse to bad outcomes. The vignettes use it to ask “what if the manager cared more about avoiding disasters — how does that change the value of information?”
Running example: the chytrid frog decision table
Every case study in this package is built around a decision table (or outcome matrix): a grid showing what outcome results from each action under each possible state of the world.
We use the translocation problem from Canessa et al. (2015). A manager must decide whether to translocate frogs to a new site. The outcome depends on whether chytrid fungus is present at the destination.
V <- matrix(
c(55, 135, # translocate: bad if chytrid present, good if absent
100, 100), # no action: safe regardless
nrow = 2, byrow = TRUE,
dimnames = list(
c("translocate", "no_action"),
c("chytrid_present", "chytrid_absent")
)
)
p_prior <- c(0.5, 0.5) # 50 % prior probability chytrid is presentThe outcome matrix V is the central object. Row =
action, column = state of the world:
V
#> chytrid_present chytrid_absent
#> translocate 55 135
#> no_action 100 100“No action” produces 100 frogs regardless of chytrid status — it is the safe fallback. “Translocate” is better when chytrid is absent (135 vs 100) but worse when chytrid is present (55 vs 100).
The prior probability that chytrid is present is 50 %. A risk-neutral manager computes:
- EV(translocate) = 0.5 × 55 + 0.5 × 135 = 95 frogs
- EV(no action) = 0.5 × 100 + 0.5 × 100 = 100 frogs
So the manager chooses “no action” without a test.
Notation
Throughout these vignettes we follow the ecological VOI convention of Runge et al. (2011) and Bennett et al. (2018):
| Symbol | Meaning | R variable |
|---|---|---|
| A state of the world (e.g., chytrid present / absent) | column of V
|
|
| Prior probability of state | p_prior |
|
| The -th possible survey outcome | row index | |
| Posterior probability of state after observing outcome | p_posterior[k, ] |
|
| Marginal probability of observing outcome (before the survey) | p_outcome[k] |
The EVSI formula is:
is the weight on each post-survey world; it is the marginal (pre-survey) probability that the survey returns result . In the health-economics VOI literature this quantity is written with as the data variable; both conventions are in common use.
The VOI Pipeline Walkthrough
vira computes Value of Information (VOI) as a sequence
of small, inspectable steps rather than a single opaque function. This
lets you:
- Inspect intermediate results — see posteriors, chosen actions, and utility distributions before they collapse to a single number.
- Swap in any component — use a custom objective function, a risk-averse utility, or a non-standard summariser without rewriting everything else.
- Understand what drives VOI — by seeing where the certainty/uncertainty gap opens up, you can reason about why information is (or is not) valuable.
The six pipeline functions and what they compute:
| Step | Function | Input → Output |
|---|---|---|
| 0 | voi_problem() |
Raw outcomes + beliefs → problem object |
| 1 | transform_to_utility() |
Outcomes → utility units |
| 2 | optimize_action() |
Utilities → best action per scenario |
| 3 | calculate_utility_dist() |
Best actions → per-outcome utility values |
| 4 | summarize_utility() |
Per-outcome utilities → scalar EU |
| 5 | transform_to_values() |
Scalar EU → certainty equivalents |
| 6 | calculate_value_info() |
CE certainty − CE uncertainty = VOI |
Let’s walk through each step in R.
Step 0 — voi_problem(): declare the problem
voi_problem() packages the outcome matrix and prior
beliefs into a single voi_problem object. For EVPI (perfect
information), no posterior is needed — the function fills in the
identity matrix automatically (each experiment outcome reveals exactly
one state with certainty).
problem <- voi_problem(V, p_prior)
#> ℹ VOI problem: 2 actions, 2 states, 2 experiment outcomes.At this point the object contains:
str(problem)
#> List of 4
#> $ V : num [1:2, 1:2] 55 100 135 100
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:2] "translocate" "no_action"
#> .. ..$ : chr [1:2] "chytrid_present" "chytrid_absent"
#> $ p_prior : num [1:2] 0.5 0.5
#> $ p_posterior: num [1:2, 1:2] 1 0 0 1
#> $ p_outcome : num [1:2] 0.5 0.5
#> - attr(*, "class")= chr "voi_problem"-
V— the (2 × 2) outcome matrix you supplied. -
p_prior— the prior,c(0.5, 0.5). -
p_posterior— filled automatically for EVPI: a 2 × 2 identity matrix where row 1 means “chytrid is definitely present” and row 2 means “chytrid is definitely absent”. -
p_outcome— marginal probability of each experiment outcome, , set equal top_priorfor EVPI, soc(0.5, 0.5).
Key concept. EVPI imagines an oracle that tells you the true state before you decide. Each row of
p_posterioris the belief you’d hold if the oracle gave you that message. For EVPI those rows are degenerate (one-hot); for EVSI they are the Bayesian updates from a real survey.
Step 1 — transform_to_utility(): map outcomes to
preferences
This step applies a utility function to every cell of V,
producing U. Under risk neutrality (the default) outcomes
equal utilities, so U = V.
problem <- problem |> transform_to_utility()
#> ℹ Utility: identity (risk-neutral).
problem$U
#> chytrid_present chytrid_absent
#> translocate 55 135
#> no_action 100 100The matrix is unchanged because we have not specified a utility function. If we later pass a CRRA utility function here, this is the only step that changes — everything downstream is identical.
Why does this step exist? Risk preferences operate on outcomes before they are averaged. A 50 % chance of 55 frogs and a 50 % chance of 135 frogs is not equivalent to 95 frogs for a risk-averse manager. Separating utility transformation from probability-weighting makes the distinction explicit.
Step 2 — optimize_action(): find the best action in
each scenario
For every row of p_posterior (i.e. every possible
experiment outcome), this step asks: “If I held these beliefs, which
action would I choose?”
It also finds the best action under the prior — the choice you’d make without any survey.
problem <- problem |> optimize_action()
#> ℹ Optimizing: action changes in 1/2 experiment outcomes.
cat("Best action without survey: ", problem$a_uncertainty, "\n")
#> Best action without survey: 2 2
cat("Best action after outcome 1: ", problem$a_certainty[1],
"(chytrid present)\n")
#> Best action after outcome 1: 2 (chytrid present)
cat("Best action after outcome 2: ", problem$a_certainty[2],
"(chytrid absent)\n")
#> Best action after outcome 2: 1 (chytrid absent)Action index 2 = “no action”; action index 1 = “translocate”. So:
- Without a survey — we choose “no action” (EV: 0.5×100 + 0.5×100 = 100 > 0.5×55 + 0.5×135 = 95).
- If told chytrid is present — still “no action” (100 > 55).
- If told chytrid is absent — switch to “translocate” (135 > 100).
The survey is worth something because it changes our action in one out of the two scenarios. If both posteriors led to the same action, VOI would be zero.
A formatted summary of this is available via
decision_table():
decision_table(problem)
#>
#> ── Decision table ──
#>
#> action chytrid_present chytrid_absent EV_prior best_action?
#> translocate 55 135 95
#> no_action 100 100 100 YESStep 3 — calculate_utility_dist(): evaluate chosen
actions across states
Now we know which action to take in each scenario. This step evaluates the expected utility of that action under the corresponding posterior beliefs.
For experiment outcome (with posterior and chosen action ):
And for the no-survey action :
problem <- problem |> calculate_utility_dist()
#> ℹ Utility dist: certainty mean = 117.5, uncertainty mean = 100.
cat("EU under certainty (per outcome):", problem$U_certainty, "\n")
#> EU under certainty (per outcome): 100 135
cat("EU under uncertainty (per outcome):", problem$U_uncertainty, "\n")
#> EU under uncertainty (per outcome): 100 100- Outcome 1 (chytrid present, prob = 0.5): both actions give the same EU because “no action” is optimal in both the with- and without-survey scenario — no gain here.
- Outcome 2 (chytrid absent, prob = 0.5): certainty gives 135 (translocate) vs uncertainty gives 100 (no action) — this is where the gap opens up.
Step 4 — summarize_utility(): collapse to a scalar
This step takes the per-outcome EU vectors and weights them by
p_outcome — the marginal probability
of each experiment outcome — to produce a single number for each
condition.
problem <- problem |> summarize_utility()
#> ℹ Summary: EU certainty = 117.5, EU uncertainty = 100.
cat("EU certainty: ", problem$EU_certainty, "\n")
#> EU certainty: 117.5
cat("EU uncertainty: ", problem$EU_uncertainty, "\n")
#> EU uncertainty: 100
cat("Raw gap (utility units):", problem$EU_certainty - problem$EU_uncertainty, "\n")
#> Raw gap (utility units): 17.5The raw gap is already 17.5. Under risk neutrality, utility = value, so this is also the EVPI in frog units.
Step 5 — transform_to_values(): back to outcome units
(certainty equivalents)
Under risk neutrality this step is a no-op: EV = EU.
Under risk aversion, the inverse utility function converts the scalar EU
back into the certainty equivalent — the guaranteed frog count
the manager would accept in place of the gamble.
problem <- problem |> transform_to_values()
#> ℹ Inverse transform: identity (utility = value units).Step 6 — calculate_value_info(): subtract to get
VOI
problem <- problem |> calculate_value_info()
#> ✔ VOI = 17.5 (original units (certainty equivalent)).
cat("EVPI:", problem$value_info, problem$voi_units, "\n")
#> EVPI: 17.5 original units (certainty equivalent)The EVPI of 17.5 frogs matches Canessa et al. (2015). It is worth paying up to 17.5 frogs-worth of survey cost to learn the disease status before deciding.
Full pipeline at a glance
All six steps can be chained without storing intermediate objects:
voi_problem(V, p_prior) |>
transform_to_utility() |> # Step 1: outcomes → utilities
optimize_action() |> # Step 2: best action per scenario
calculate_utility_dist() |> # Step 3: EU of chosen actions
summarize_utility() |> # Step 4: collapse to scalar
transform_to_values() |> # Step 5: back to outcome units
calculate_value_info() # Step 6: certainty gap = VOI
#> ℹ VOI problem: 2 actions, 2 states, 2 experiment outcomes.
#> ℹ Utility: identity (risk-neutral).
#> ℹ Optimizing: action changes in 1/2 experiment outcomes.
#> ℹ Utility dist: certainty mean = 117.5, uncertainty mean = 100.
#> ℹ Summary: EU certainty = 117.5, EU uncertainty = 100.
#> ℹ Inverse transform: identity (utility = value units).
#> ✔ VOI = 17.5 (original units (certainty equivalent)).
#>
#> ── VOI Problem ──
#>
#> • Actions: 2
#> • States: 2
#> • Experiment outcomes: 2
#> • Best action (no survey): no_action
#> • VOI (original units (certainty equivalent)): 17.5Why risk aversion matters for VOI
A risk-neutral manager cares only about the average outcome. A risk-averse manager also cares about variability — they dislike bad outcomes more than they enjoy equally good ones.
This changes the value of information in two important ways:
When there is a safe fallback (Property 1): A risk-averse manager locks onto the safe action even more strongly. Because “no action” guarantees 100 frogs, a risk-averse manager prefers it even more than a risk-neutral one would. Information becomes less valuable as risk aversion increases — the manager is happy with the safe floor and has no need to gamble.
When there is no safe fallback (Property 2): Every action exposes the manager to bad outcomes in some states. Risk aversion makes those bad outcomes feel even worse — so the manager is even more eager to learn the true state before deciding. Information becomes more valuable as risk aversion increases.
Pipeline under risk aversion
Under risk aversion, only Steps 1 and 5 in the pipeline change. Step 1 bends the utility scale; Step 5 applies the inverse to recover certainty equivalents. Steps 2–4 and 6 are identical:
rp <- risk_preference("CRRA", param = 1, val_min = 0, val_max = 200,
outcome_name = "frogs", maximize = TRUE)
fns <- use_risk_preference(rp)
voi_problem(V, p_prior) |>
transform_to_utility(fns$utility) |> # Step 1: CRRA utility
optimize_action() |> # Steps 2-4: unchanged
calculate_utility_dist() |>
summarize_utility() |>
transform_to_values(fns$inv_utility) |> # Step 5: back to frogs via inverse
calculate_value_info()
#> ℹ VOI problem: 2 actions, 2 states, 2 experiment outcomes.
#> ℹ Utility: values mapped through utility function.
#> ℹ Optimizing: action changes in 1/2 experiment outcomes.
#> ℹ Utility dist: certainty mean = -0.5431, uncertainty mean = -0.6931.
#> ℹ Summary: EU certainty = -0.5431, EU uncertainty = -0.6931.
#> ℹ Inverse transform: CE certainty = 116.1895, CE uncertainty = 100.
#> ✔ VOI = 16.1895 (original units (certainty equivalent)).
#>
#> ── VOI Problem ──
#>
#> • Actions: 2
#> • States: 2
#> • Experiment outcomes: 2
#> • Best action (no survey): no_action
#> • VOI (original units (certainty equivalent)): 16.1895The EVPI under risk aversion is lower than 17.5 frogs because the safe “no action” choice (100 frogs regardless of state) is increasingly attractive to a risk-averse manager, leaving less room for information to help.
What changes for EVSI and EVPXI?
The pipeline is identical. Only voi_problem()
changes:
| VOI type | p_posterior |
p_outcome |
|---|---|---|
| EVPI | identity matrix (auto-filled) |
p_prior (auto-filled) |
| EVSI | Bayesian posteriors from survey | Survey outcome probs |
| EVPXI | Posteriors from partial information source | Source outcome probs |
Once voi_problem() is constructed, Steps 1–6 are
unchanged. See vignettes 3 (EVSI) and 4 (EVPXI) for fully worked
examples.
Where to go next
| Vignette | Scenario | Key lesson |
|---|---|---|
| 1. Key Concepts and the VOI Pipeline | Translocation under uncertainty | Introduction to the core concepts and 6-step R pipeline |
| 2. Calculating EVPI | Chytrid frog translocation | Fast EVPI calculation using calculate_evpi();
sensitivity to risk aversion |
| 3. Calculating EVSI | Chytrid frog translocation | EVSI pipeline with imperfect surveys; comparison to EVPI |
| 4. EVPXI — Runge et al. (2011) | Whooping crane management | Partial EVPI per hypothesis; ranking which uncertainties drive the decision |
| 5. Elicitation, Sensitivity, and Validation | Risk preference elicitation | Bayesian adaptive elicitation; how elicitation uncertainty propagates into VOI; robot-agent calibration |
| 6. Bennett et al. (2018) | Species protection (single parcel and multispecies) | Property 1 per unit; budget forces Property 2; extinction risk under risk aversion |
| 7. Validation against Davis et al. (2019) | TURF fishery social-ecological model | EVPXI pipeline validated against published MATLAB results; machine-epsilon agreement |
| 8. Mäntyniemi et al. (2009) | Herring stock recruitment | Property 2: no safe option large EVPI, grows with risk aversion |