Skip to contents

Calculate how frequently each planning-unit/action assignment is selected across the stored solutions in a solutionset-class object.

Usage

selection_frequency(x)

Arguments

x

A solutionset-class object returned by solve.

Value

A data.frame with one row per planning-unit/action pair and the following columns:

  • pu: planning-unit identifier;

  • action: action identifier or name;

  • n_selected: number of stored solutions in which the planning-unit/action pair is selected;

  • n_solutions: total number of stored solutions considered;

  • frequency: proportion of stored solutions in which the pair is selected.

Details

Selection frequency is calculated at the planning-unit/action level. This is the canonical decision representation used by this function because it preserves differences between solutions that select the same planning unit but assign different actions.

For each planning-unit/action pair, the frequency is:

$$ f_{ia} = \frac{\sum_{s \in S} x_{ias}} {|S|}, $$

where \(x_{ias}\) equals one when planning unit \(i\) receives action \(a\) in solution \(s\), and zero otherwise.

The result is computed over all stored solutions in the supplied SolutionSet. To calculate frequencies for only a subset of solutions, first use solution_filter or solution_unique.

For simple conservation-planning problems without explicit actions, selected planning units are represented using the canonical action name "conservation".

Selection frequency measures recurrence across the supplied solutions. It should not automatically be interpreted as formal irreplaceability because it depends on the solutions included, their sampling across objective space, and whether duplicate or dominated solutions have been retained.

Examples

pu <- data.frame(
  id = 1:4,
  cost = c(1, 2, 3, 4)
)

features <- data.frame(
  id = 1:2,
  name = c("sp1", "sp2")
)

dist_features <- data.frame(
  pu = c(1, 1, 2, 3, 4),
  feature = c(1, 2, 2, 1, 2),
  amount = c(5, 2, 3, 4, 1)
)

actions <- data.frame(
  id = c("conservation", "restoration")
)

effects <- data.frame(
  action = rep(actions$id, each = 2),
  feature = rep(features$id, times = 2),
  multiplier = c(
    1.0, 1.0,
    1.5, 1.5
  )
)

problem <- create_problem(
  pu = pu,
  features = features,
  dist_features = dist_features,
  cost = "cost"
) |>
  add_actions(
    actions = actions,
    cost = c(
      conservation = 1,
      restoration = 2
    )
  ) |>
  add_effects(
    effects = effects,
    effect_type = "after"
  ) |>
  add_constraint_targets_relative(0.05) |>
  add_objective_min_cost(alias = "cost") |>
  add_objective_max_benefit(alias = "benefit") |>
  set_method_weighted_sum(
    aliases = c("cost", "benefit"),
    runs = run_grid(
      n = 5,
      include_extremes = TRUE
    ),
    normalize_weights = TRUE
  )

if (requireNamespace("rcbc", quietly = TRUE)) {
  problem <- set_solver_cbc(
    problem,
    verbose = FALSE
  )

  solutions <- solve(problem)

  # Frequency across all stored solutions
  frequency <- selection_frequency(solutions)
  frequency

  # Restrict the analysis to non-dominated solutions
  if (requireNamespace("moocore", quietly = TRUE)) {
    nondominated_solutions <- solution_filter(
      solutions,
      feasible_only = TRUE,
      nondominated = TRUE
    )

    selection_frequency(nondominated_solutions)
  }

  # Give each distinct decision configuration the same weight
  unique_solutions <- solution_unique(
    solutions,
    by = "decisions"
  )

  unique_frequency <- selection_frequency(
    unique_solutions
  )

  unique_frequency
}
#>   pu       action n_selected n_solutions frequency
#> 1  1 conservation          1           4      0.25
#> 2  1  restoration          3           4      0.75
#> 3  2 conservation          0           4      0.00
#> 4  2  restoration          2           4      0.50
#> 5  3 conservation          0           4      0.00
#> 6  3  restoration          2           4      0.50
#> 7  4 conservation          0           4      0.00
#> 8  4  restoration          1           4      0.25