Skip to contents

L’objectif de ce tutoriel est d’introduire quelques unes des principales fonctions du packages c3t.

Commençons par créer une grille factice de taille 4L par 5 qui servira d’exemple pour l’utilisation des différentes fonctions. On peut via la fonction gen_grid choisir le nombre d’individus présents sur la grille, si certaines cases doivent être vides et si certaines doivent être plus peuplées que les autres (être des métropoles).

Un contexte sera généra pour chaque individu. Le nombre de variables quantatitatives et qualitatives peut être choisi. Ici on choisit de prendre 2 variables normales centrées réduites.

set.seed(123L)
x <- 4L
y <- 5L
nbIndividuals <- 100L
nbCasesVides <- 3L
nbMetropolises <- 2L
nbVariablesQuant <- 2L
grille <- gen_grid(x, y, nbIndividuals = nbIndividuals,
                   nbMinEmptyZones = nbCasesVides,
                   nbMetropolises = nbMetropolises,
                   nbQuantitatives = nbVariablesQuant)

data <- grille$context
individus <- grille$repartition$nbIndividuals
contiguite <- grille$contiguity

Régionalisation Ascendante Hiérarchique (RAH)

Dans l’optique de trouver une solution faisable à un problème de régionalisation avec éventuelle contrainte de taille min / et ou max, la fonction AHR peut être utilisée. Celle-ci permet d’appliquer un algorithme de Classification Ascendante Hiérarchique modifié afin de respecter les contraintes de contiguïté et taille maximale. Cette fonction va appliquer une ou plusieurs itérations de CAH en fonction des paramétrisations données par l’utilisateur.

Plusieurs paramètres sont disponibles. Pour plus de détails voir help("AHR"). Comme pour la CAH classique la fonction prend une distance de liaison (linkage). Plusieurs distances sont déjà implémentées dans le package, et sont visibles via l’appel suivant :

available_linkages()
#> [1] "Single"    "Complete"  "Centroid"  "Average"   "Medoid"    "Hausdorff"

Une distance de liaison peut également être proposée par l’utilisateur sous la forme d’une fonction prenant en argument une matrice de distances entre les éléments d’un premier cluster et les éléments d’un autre.

Plusieurs distances de liaison peut être indiquées.

Contrainte de fusion

Lorsqu’il y a une contrainte de taille min, l’algorithme n’assure pas que celle-ci sera vérifiée contrairement aux autres contraintes. Des paramètres permettent de perturber le parcours de l’arbre afin de faciliter la vérification.

fusionConstraint indique s’il faut appliquer une contrainte sur le choix de la fusion et si oui laquelle. Les possibilités implémentées sont les suivantes :

available_fusion_constraints()
#> [1] NA             "singletonMin" "singletonAny" "minMin"       "sizePenalty"

NA signifiant qu’aucune contrainte n’est ajoutée.

fusionConstraintMode définit comment la contrainte de fusion (si elle existe) doit être appliquée.

available_fusion_modes()
#> [1] "deterministic" "random"

Plusieurs contraintes et modes de fusions peuvent être demandées.

Situation initiale

Plutôt que de démarrer du bas de l’arbre, c’est-à-dire de la partition triviale (1 élément = 1 cluster), il est possible de démarrer de plus profond et peut-être d’obtenir de meilleur résultat d’un point de vue global ou du point de vue du respect de l’éventuelle contrainte de taille minimale.

L’utilisateur peut choisir s’il souhaite ne démarrer que du bas de l’arbre (de manière classique) ou également essayer de démarrer à partir d’une ou plusieurs partitions initiales générées aléatoirement. C’est ce que permet le paramètre nbTries, indiquant le nombre de situations initiales à tester, dont la classique.propFusionsInit indique à quel point les partitions obtenues aléatoirement doivent être récupérées profondément dans l’arbre.

Critères

Un ou plusieurs critères de classification peuvent être calculés sur les différentes solutions faisables, si elles existent. Plusieurs critères sont disponibles :

available_criteria()
#> [1] "CHI"        "Dunn"       "Silhouette"

Le critère du score de silhouette n’est disponible que si le package cluster est installé.

Pour cet exemple nous choisissons le critère “CHI”, c’est-à-dire l’indice de Calinski-Harabasz. Celui-ci nécessite qu’un contexte soit donné et que toutes ses variables soient quantitatives.

critere <- "CHI"

Application

Appliquons l’algorithme sur nous données. Pour cet exemple nous fixons deux contraintes de taille non-triviale (i.e. qui ne sont pas systématiquement vérifiées) et utilisons la distance euclidienne.

m <- 5.0
M <- 40.0
d <- "euclidean"

L’algorithme utilise de l’aléatoire pour la génération de partitions initiales et éventuellement l’application de contraintes de fusion. Nous fixons donc préalablement une graine aléatoire.

set.seed(123L)
resRAH <- AHR(contiguity = contiguite,
              d = d, data = data,
              sizes = individus,
              m = m, M = M,
              linkages = "saut_min",
              criteria = critere,
              fusionConstraints = available_fusion_constraints(),
              fusionConstraintModes = available_fusion_modes(),
              parallel = FALSE)
#>  Starting time: 2023-11-28 11:58:30.315877
#>  45 AHC to evaluate
#> → 432 non-trivial regionalisations obtained
#>  56 feasible partitions obtained
#> → 18 redundancies have been removed.
#> → Calculation of the CHI criterion
#> → Execution time: 4.23357248306274

La fonction renvoie une liste de 3 tibble. Les solutions faisables sont contenues dans results :

resRAH$results %>%
  select(nbClusters, partition, CHI) %>%
  head()
#> # A tibble: 6 × 3
#>   nbClusters partition      CHI
#>        <int> <named list> <dbl>
#> 1          3 <int [20]>    2.39
#> 2          4 <int [20]>    2.32
#> 3          4 <int [20]>    2.27
#> 4          3 <int [20]>    2.18
#> 5          3 <int [20]>    2.04
#> 6          5 <int [20]>    1.95

38 solutions faisables distinctes ont été obtenues.

La solution en elle-même se trouve dans la colonne partition. Voici la solution faisable qui offre le meilleur indice de Calinski-Harabasz parmi celles trouvées :

regionalisation <- resRAH$results$partition[[1L]]
regionalisation
#>  [1] 1 2 2 1 1 1 3 1 3 3 1 3 1 1 1 3 1 1 1 3

Celle-ci vérifie bien les contraintes de contiguïté et de taille :

is_feasible_solution(regionalisation, contiguite,
                     individus, m, M)
#> [1] TRUE

Amélioration de la solution faisable

Une solution faisable ayant été obtenue, on souhaite l’améliorer au regard d’un certain critère tout en préservant les contraintes. C’est ce que permet de faire la fonction enhance_feasible.

En conservant notre critère précédent (Calinski-Harabasz), on regarde si celui-ci peut être amélioré. On regarde également si le critère d’optimisation locale (“AHC”) permet d’améliorer l’ICH. Le critère “AHC” nécessite une distance de liaison. On propose d’utiliser les distances de saut minimal et maximal.

criteresAmelioration <- c("AHC", "CHI")
set.seed(123L)
resEnhance <- enhance_feasible(regionalisation,
                               contiguity = contiguite,
                               d = d, data = data,
                               sizes = individus,
                               m = m, M = M,
                               enhanceCriteria = criteresAmelioration,
                               linkages = c("single", "complete"),
                               parallel = FALSE,
                               verbose = TRUE)
#> → Evaluation of the 3 enhancements
#> → Calculation of 1 evaluation criteria on the initial partition
#> → Calcul of 1 evaluation criteria on the 3 enhanced partitions

resEnhance$results %>% select(-tempsCalcul_mins)
#> # A tibble: 3 × 6
#>   criterion linkage  statut       iterations regionalisationOpti dt_CHI
#>   <chr>     <chr>    <chr>             <int> <list>               <dbl>
#> 1 AHC       Single   amelioration          1 <int [20]>          -0.258
#> 2 AHC       Complete amelioration          6 <int [20]>          -1.61 
#> 3 CHI       NA       amelioration          1 <int [20]>           0.117

L’optimisation sous l’ICH permet d’augmenter cet indice de 0,1 , soit une amélioration d’environ 4%. La solution proposée est bien une solution faisable :

partitionCHI <- resEnhance$results$regionalisationOpti[[3L]]
is_feasible_solution(partitionCHI,
                     contiguite,
                     individus, m, M)
#> [1] TRUE

Régularisation

Supposons que l’algorithme AHR n’ait pas pu fournir une solution faisable (ce qui ne peut être le cas que s’il y a une contrainte de taille minimale). La fonction resolve_unfeasible va essayer de fournir une solution faisable à partir d’une solution qui vérifie la contrainte de contiguïté mais pas forcément les contraintes de taille.

Prenons par exemple la partition suivante :

regInfaisable <-
  c(1L, 2L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 4L,
    4L, 2L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L)

Celle-ci est composée de 4L clusters. On peut vérifier que tous ses clusters sont bien des régions, mais qu’elle ne vérifie pas les contraintes de tailles fixées précédemment. Le premier cluster est trop petit et le quatrième trop grand.

is_regionalisation(regInfaisable, contiguite)
#> [1] TRUE
clusters_sizes(regInfaisable, individus)
#>  1  2  3  4 
#>  3 31 23 43

resolve_unfeasible va à partir de de régionalisation proposer une solution faisable, en augmentant la taille du premier cluster et en baissant la taille du dernier.

set.seed(123L)
resolution <- resolve_unfeasible(contiguity = contiguite,
                                 sizes = individus,
                                 data = data,
                                 d = d, m = m, M = M,
                                 regionalisation = regInfaisable,
                                 verbose = TRUE)
#> → Transfert of elements one-by-one
#>  fully resolved partition

resolution
#> $status
#> [1] "fully_resolved"
#> 
#> $itTransfers
#> [1] 6
#> 
#> $itFusions
#> [1] 0
#> 
#> $initialSmallRegions
#> [1] 1
#> 
#> $finalSmallRegions
#> NULL
#> 
#> $initialBigRegions
#> [1] 4
#> 
#> $finalBigRegions
#> NULL
#> 
#> $resolvedRegions
#> [1] 1 4
#> 
#> $initialRegionsSizes
#>  1  2  3  4 
#>  3 31 23 43 
#> 
#> $finalRegionsSizes
#>  1  2  3  4 
#>  7 35 23 35 
#> 
#> $regionalisation
#>  [1] 1 1 2 3 3 3 3 3 3 4 4 3 4 3 3 4 4 3 3 3
#> 
#> $initialNbClusters
#> [1] 4
#> 
#> $finalNbClusters
#> [1] 4

La fonction a obtenu une solution faisable en réalisant six transferts d’éléments. Tous les contraintes sont désormais respectées. En particulier, la solution est bien une régionalisation :

is_regionalisation(resolution$regionalisation, contiguite)
#> [1] TRUE