Using GDM SCORE bands (consensus reaching phase)¶
Here, we illustrate how to use the SCORE bands based GDM implementation in DESDEO. This method assumes that the analyst has generated a set of Pareto optimal solutions and that the decision makers will choose from among these solutions. No new solutions must be generated.
First, we import the necessary modules and load the data.
from copy import deepcopy as copy
import polars as pl
from desdeo.gdm.score_bands import SCOREBandsGDMConfig, score_bands_gdm
from desdeo.tools.score_bands import KMeansOptions, SCOREBandsConfig, plot_score
# data = pl.read_csv("https://raw.githubusercontent.com/light-weaver/iterative-pareto-representer/refs/heads/main/data/forest.csv")
data = pl.read_csv("../../datasets/dmitry_forest_problem_non_dom_solns.csv")
objs = data.columns
descriptive_names = {
"Rev": "Harvest Revenue",
"HA": "Habitat Availability",
"Carb": "Carbon Storage",
"DW": "Deadwood Volume",
}
units = {"Rev": "million €", "HA": "(unitless)", "Carb": "thousand Mg C", "DW": "thousand m³"}
data.head()
| Rev | HA | Carb | DW |
|---|---|---|---|
| f64 | f64 | f64 | f64 |
| 152.580066 | 20208.082787 | 3879.395281 | 209.184407 |
| 114.740953 | 19197.839047 | 4179.235633 | 218.137467 |
| 134.561958 | 19028.754571 | 4100.362863 | 217.745341 |
| 110.57816 | 17804.39651 | 4397.186981 | 199.641147 |
| 168.399967 | 19569.240864 | 3989.666422 | 212.227667 |
We can now configure the consensus reaching phase of GDM SCORE bands.
We choose to use the K-Means clustering method (with 4 clusters) to classify the solutions into bands. We also modify the distance_parameter and interval_size
from their default values. You can read more details about these parameters in the
documentation of the SCOREBandsConfig and SCOREBandsGDMConfig classes.
Once the configuration is done, we can run the GDM SCORE bands method.
score_config = SCOREBandsGDMConfig(
score_bands_config=SCOREBandsConfig(
dimensions=objs,
clustering_algorithm=KMeansOptions(n_clusters=4),
distance_parameter=0.3,
interval_size=0.9,
descriptive_names=descriptive_names,
units=units,
),
from_iteration=None,
)
# First iteration
state = score_bands_gdm(data=data, config=score_config, state=[], votes=None)
iteration = 1
# Doing this step fixes the parameters like axis positions to the ones
# used in the first iteration.
# Useful when we want to keep the axis positions same across iterations.
score_config.score_bands_config = state[-1].score_bands_result.options
score_config.score_bands_config.axis_positions = state[-1].score_bands_result.axis_positions
# If you want to change clustering options between iterations:
score_config = copy(score_config)
# score_config.score_bands_config.clustering_algorithm = DimensionClusterOptions(n_clusters=n_clusters, dimension_name="Rev")
# score_config.score_bands_config.clustering_algorithm = KMeansOptions(n_clusters=n_clusters)
def plot(iteration):
"""Just a convenience function to plot results from different iterations."""
if iteration > len(state):
raise ValueError(f"Iteration {iteration} has not been computed yet.")
if state[iteration - 1].previous_iteration is None:
text = ", the initial iteration"
else:
text = f", continuing from {state[iteration - 1].previous_iteration}"
print(f"Results from iteration {iteration}{text}:")
return plot_score(
(
data.with_row_index(name="index")
.filter(pl.col("index").is_in(state[iteration - 1].relevant_ids))
.drop("index")
),
state[iteration - 1].score_bands_result,
)
plot(iteration=1).show(renderer="notebook", include_plotlyjs="cdn")
Results from iteration 1, the initial iteration:
Now, the individual decision makers can cast their votes for the bands they prefer. Depending on the iteration, we can either choose all bands that received votes or only the bands that received a minimum number of votes. More sophisticated voting mechanisms may be implemented in the future. We can then run further iterations of the consensus reaching phase until a termination criterion is met. We also have the option to go back to a previous iteration to either change the votes or to modify the configuration parameters (e.g., changing the seed of the clustering algorithm).
votes = {"Kaisa": 1, "Bekir": 1, "Juuso": 3}
score_config = copy(score_config) # Create a new config for the new iteration
score_config.from_iteration = iteration # Continue from the previous iteration
# Continue from some previous iteration
# score_config.from_iteration = 2
# "Continuing" from previous iteration can also be used to recluster the same data again
# with different clustering parameters.
if iteration == 1:
score_config.minimum_votes = 1 # (default)
else:
score_config.minimum_votes = 2 # Increase minimum votes needed to select a band
iteration = iteration + 1
state = score_bands_gdm(data=data, config=score_config, state=state, votes=votes)
plot(iteration=2).show(renderer="notebook", include_plotlyjs="cdn")
Results from iteration 2, continuing from 1: