MTA - Un des packages R du Riatelab



Le RIATE - Centre pour l’analyse spatiale et la géovisualisation - développe et maintient plusieurs applications, packages R et JavaScript dédiés au traitement et la visualisation de données spatialisées

Le code (open) source et leur documentation associée est accessible au sein de l’organisation GitHub riatelab

Concept et historique des travaux

Contexte général

Contexte général

Contexte général

Contexte général

Contexte territorial

Contexte territorial

Contexte territorial

Contexte spatial

Contexte spatial

Contexte spatial

Synthèse des écarts

Groupe HyperCarte : 20 ans de recherche interdisciplinaire

  • 1990’s : Travaux de C. Grasland, H. Mathian et J.C François sur la distribution de phénomènes sociaux discrets dans l’espace (appartenances et discontinuités territoriales)
  • 1996 : Création du groupe de recherche HyperCarte
  • 2006 : HyperAtlas 1.0

Groupe HyperCarte : 20 ans de recherche interdisciplinaire

  • 1990’s : Travaux de C. Grasland, H. Mathian et J.C François sur la distribution de phénomènes sociaux discrets dans l’espace (appartenances et discontinuités territoriales)
  • 1996 : Création du groupe de recherche HyperCarte
  • 2006 : HyperAtlas 1.0

“HyperAtlas oblige l’utilisateur à prendre conscience de la variété des mesures d’inégalités régionales en fournissant par défaut trois mesures variées de situations régionales fondées sur des contextes différents à la fois sur le plan conceptuel et sur le plan politique”

Ysebaert R., Lambert N., Grasland C. et al. (2012). HyperAtlas, un outil scientifique au service du debat politique - Application a la politique de cohesion de l’Union Europeenne, In: Fonder les sciences du territoire. Collection du CIST, pp. 243-267.

Groupe HyperCarte : 20 ans de recherche interdisciplinaire

  • 1990’s : Travaux de C. Grasland, H. Mathian et J.C François sur la distribution de phénomènes sociaux discrets dans l’espace (appartenances et discontinuités territoriales)
  • 1996 : Création du groupe de recherche HyperCarte
  • 2006 : HyperAtlas 1.0
  • 2011 : HyperAtlas 2.0 - nouvelles visualisations statistiques/cartographiques, design.
  • 2015 : Fin du groupe de recherche HyperCarte
  • 2021 : ESPON Regico/HyperAtlas 3.0 (ÖIR) : nouveau design, connexion à la base de données ESPON…

Des concepts mobilisés dans plusieurs études et rapports

ESPON, Parlement Européen, CGET-ANCT, Enseignement

Retour sur expérience d’HyperAtlas (2012)

Aspects positifs

  • Une application clic-bouton mûrement réfléchie (représentations graphiques, parcours utilisateur)
  • Un projet interdisciplinaire par essence
  • Concept de l’analyse multiscalaire facilement appréhendable par une diversité d’acteurs

Aspects limitants

  • Maintenance coûteuse
  • Processus de création d’un .hyp (données d’entrée d’HyperAtlas) peu évidente
  • Une application qui dépend du développement successif de plusieurs ingénieurs contractuels (difficile à faire évoluer)
  • Export vectoriel des cartes et graphiques impossible sous HyperAtlas

Créer un package R

  • Proposer une alternative technique pour mener ce type d’analyses
  • Le focaliser sur les fonctions coeur d’HyperAtlas :
    1. Calcul des déviations
    2. Calcul et représentation des synthèses
    3. Documenter des solutions (carto)graphiques pour réprésenter ces résultats
  • Ouvrir le code et les méthodes pour rendre possible d’éventuelles contributions ultérieures

Le package MTA

  • Multiscalar Territorial Analysis
  • Déposé sur le CRAN en mars 2017
  • 10 fonctions : gdev(), tdev(), sdev(), bidev(), mst(), mas(), map_bidev(), map_mst(), plot_bidev(), plot_mst()
  • Un jeu de données d’exemple qui porte sur les inégalités de revenu sur la Métropole du Grand Paris : com(), ept(), cardist()
  • 2 vignettes explicatives (concepts théoriques et application)
  • version 0.6.0 (dernière mise à jour : octobre 2023)

Dépendances du package

Préalables pour mener une analyse multiscalaire

Thématique

  • Définir un indicateur défini par la variable Z = V/P : un modèle d’allocation d’une ressource par rapport à une population de référence. La référence implicite de ces analyses étant l’équirépartition
  • Identifier des contextes territoriaux qui font sens d’un point de vue thématique ou politique

Technique

  • Jeu de données : disposer d’un numérateur, d’un dénominateur et d’une variable d’appartenance territoriale
  • Des géométries associées au jeu de données (calcul de la déviation spatiale : objet sf)

Exemple d’utilisation

Concentration de l’emploi dans la Métropole du Grand Paris

Packages et jeu de données

3 packages nécessaires

MTA : Calcul des déviations, représentations graphiques de synthèse des déviations

sf : Manipulation de données spatiales

mapsf : Représentations cartographiques

# Import des packages
library(MTA)
library(sf)
library(mapsf)

Le jeu de données

Couverture géographique / maille territoriale : Communes appartenant à une des 22 métropoles françaises (IGN, 2021)

Un numérateur : Emplois au lieu de travail en 2016 (INSEE, 2021)

Un dénominateur : Actifs de 15 à 64 en 2016, au lieu de résidence (INSEE, 2021)

Une variable d’appartenance : EPCI (communauté d’agglomération) d’appartenance de la commune (RIATE, 2021)

# Import des données
com <- st_read("data/data.gpkg", layer = "com", quiet = TRUE)
epci <- st_read("data/data.gpkg", layer = "epci", quiet = TRUE)

# Filtrer sur Paris
com <- com[com$LIB_EPCI == "Métropole du Grand Paris",]
epci <- epci[epci$LIB_EPCI == "Métropole du Grand Paris",]

Packages et jeu de données

3 packages nécessaires

MTA : Calcul des déviations, représentations graphiques de synthèse des déviations

sf : Manipulation de données spatiales

mapsf : Représentations cartographiques

# Import des packages
library(MTA)
library(sf)
library(mapsf)


Une analyse reproductible

Données et script sont accessibles dans le dépôt GitLab de la présentation: gitlab.huma-num.fr/rysebaert/mta-insee

Le jeu de données

Couverture géographique / maille territoriale : Communes appartenant à une des 22 métropoles françaises (IGN, 2021)

Un numérateur : Emplois au lieu de travail en 2016 (INSEE, 2021)

Un dénominateur : Actifs de 15 à 64 en 2016, au lieu de résidence (INSEE, 2021)

Une variable d’appartenance : EPCI (communauté d’agglomération) d’appartenance de la commune (RIATE, 2021)

# Import des données
com <- st_read("data/data.gpkg", layer = "com", quiet = TRUE)
epci <- st_read("data/data.gpkg", layer = "epci", quiet = TRUE)

# Filtrer sur Paris
com <- com[com$LIB_EPCI == "Métropole du Grand Paris",]
epci <- epci[epci$LIB_EPCI == "Métropole du Grand Paris",]

Le jeu de données

head(com)
Simple feature collection with 6 features and 8 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 649855.9 ymin: 6859834 xmax: 653707.7 ymax: 6863752
Projected CRS: RGF93_Lambert_93
    INSEE_COM                  NOM_COM      EPCI                 LIB_EPCI
771     75101 Paris 1er Arrondissement 200054781 Métropole du Grand Paris
772     75102  Paris 2e Arrondissement 200054781 Métropole du Grand Paris
773     75103  Paris 3e Arrondissement 200054781 Métropole du Grand Paris
774     75104  Paris 4e Arrondissement 200054781 Métropole du Grand Paris
775     75105  Paris 5e Arrondissement 200054781 Métropole du Grand Paris
776     75106  Paris 6e Arrondissement 200054781 Métropole du Grand Paris
    EPCI_SUB LIB_EPCI_SUB P16_EMPLT C16_ACTOCC1564
771       T1        Paris  61213.89       8061.414
772       T1        Paris  61152.99      11953.851
773       T1        Paris  31460.89      18880.705
774       T1        Paris  41601.54      13794.282
775       T1        Paris  53332.77      26531.098
776       T1        Paris  45135.11      16819.452
                              geom
771 MULTIPOLYGON (((652048.7 68...
772 MULTIPOLYGON (((652243.5 68...
773 MULTIPOLYGON (((653617.9 68...
774 MULTIPOLYGON (((653427.2 68...
775 MULTIPOLYGON (((653170.9 68...
776 MULTIPOLYGON (((651487.5 68...

Initialisation de l’analyse

# Initialisation de l'analyse
num <- which(colnames(com) == "P16_EMPLT")
denom <- which(colnames(com) == "C16_ACTOCC1564")
colnames(com)[c(num,denom)] <- c("num", "denom") 

# Retirer les valeurs égales à 0
com <- com[com$num != 0 & com$denom != 0,]

# Calculer le ratio de référence
com$ratio <- com$num / com$denom  

# Représentation cartographique
theme <- mf_theme("default", bg = NA, fg = NA, mar = c(0, 2, 2, 0),
                  pos = "left", line = 1.3, cex = 1, font = 2)

mf_map(x = com, var = "ratio", type = "choro", 
       breaks = "quantile", nbreaks = 4,
       border = "white",  leg_adj = c(0,2),
       leg_title = paste0("Emploi au lieu de travail /\n", 
                          "Actifs occupés au lieu ", 
                          "de résidence, 2016"))
# EPCI
mf_map(epci, col = NA, lwd = 1, add = TRUE)

# Sources 
credits <- paste0("Sources : INSEE, IGN, 2021 / ",
                  "Réalisation : R. Ysebaert, 2025")
mf_title(paste0("Ratio d'intérêt : concentration d'emploi", 
                " au lieu de travail dans la MGP"))
mf_credits(credits)

Déviation générale : écart à la moyenne de la MGP

# Déviation générale
com$gdevrel <- gdev(x = com,  var1 = "num", var2 = "denom",  
                    type = "rel")

# Palette de couleurs pour déviations(origine Color Brewer)
devpal <-  c("#4575B4", "#91BFDB", "#E0F3F8", "#FEE090", 
             "#FC8D59", "#D73027")

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "gdevrel", type = "choro", 
       breaks = c(min(com$gdevrel), 75, 90, 100, 111, 133, 
                  max(com$gdevrel)),
       border = "white", pal = devpal, 
       leg_title = paste0("Déviation au contexte général",
                          "\n(100 = moyenne de la MGP)"),
       leg_adj = c(0,2), leg_val_rnd = 0)
mf_map(epci, col = NA, lwd = 1, add = TRUE)

mf_title(paste0("Déviation générale (moyenne = ",
                round(sum(com$num) / sum(com$denom), 2),")"))
mf_credits(credits)

# Labels 
mf_label(x = com[which.min(com$gdevrel),], var = "NOM_COM",
         halo = TRUE, bg = "white")
mf_label(x = com[which.max(com$ratio),], var = "NOM_COM", 
         halo = TRUE, bg = "white")

Déviation générale : écart à un seuil spécifique

# Déviation générale / Assymétrie numérateur - dénominateur
com$gdevrel2 <- gdev(x = com,  var1 = "num", var2 = "denom", 
                     type = "rel", ref = 1)

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "gdevrel2", type = "choro", 
       breaks = c(min(com$gdevrel2), 75, 90, 100, 111, 133, 
                  max(com$gdevrel2)),
       border = "white", pal = devpal, 
       leg_title = paste0("Déviation au contexte général",
                          "\n(100 = moyenne de la MGP)"),
       leg_adj = c(0,2), leg_val_rnd = 0)
mf_map(epci, col = NA, lwd = 1, add = TRUE)
mf_title("Déviation générale / Assymétrie numérateur dénominateur")
mf_credits(credits)

Déviation territoriale : écart à l’EPCI d’appartenance

# Déviation territoriale
com$tdevrel <- tdev(x = com,  var1 = "num", var2 = "denom", 
                    key = "LIB_EPCI_SUB")

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "tdevrel", type = "choro", 
       breaks = c(min(com$tdevrel), 75, 90, 100, 111, 133, 
                  max(com$tdevrel)),
       border = "white", pal = devpal, 
       leg_title = paste0("Déviation au contexte territorial",
                          "\n(100 = moyenne de l'EPCI",
                          " d'appartenance)"),
       leg_adj = c(0,2), leg_val_rnd = 0)
 
mf_title("Déviation territoriale")
mf_credits(credits)

# Extraction du maximum et du minimum par EPCI, puis affichage
df.agg <- aggregate(tdevrel ~ LIB_EPCI_SUB, com, FUN = max)
df.max <- merge(df.agg, com)
df.max <- st_as_sf(df.max)

df.agg <- aggregate(tdevrel ~ LIB_EPCI_SUB, com, FUN = min)
df.min <- merge(df.agg, com)
df.min <- st_as_sf(df.min)

mf_label(x = df.max, var = "NOM_COM", halo = TRUE, 
         col = "#8B1713", bg = "white")
mf_label(x = df.min, var = "NOM_COM",  halo = TRUE,
         col = "#135D89", bg = "white")

Déviation spatiale : contiguïté territoriale

# Déviation spatiale
com$sdevrel <- sdev(x = com,  var1 = "num", var2 = "denom", 
                    order = 1)

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "sdevrel", type = "choro", 
       breaks = c(min(com$sdevrel), 75, 90, 100, 111, 133, 
                  max(com$sdevrel)),
       border = "white", pal = devpal, 
       leg_title = paste0("Déviation au contexte territorial",
                          "\n(100 = moyenne des communes",
                          " contigües)"),
       leg_adj = c(0,2), leg_val_rnd = 0)
mf_title("Déviation spatiale")
mf_credits(credits)

Déviation spatiale : contiguïté territoriale

# Extraction des limites communales 
borders <- st_intersection(st_buffer(com, 5), st_buffer(com, 5)) 
borders <- st_cast(borders,"MULTILINESTRING")
borders <- borders[borders$INSEE_COM != borders$INSEE_COM.1, ] 

# Calcul des discontinuités
borders$disc <- pmax(borders$ratio/borders$ratio.1,
                     borders$ratio.1/borders$ratio)

# Ne conserver que les 25 % les plus importantes
val <- as.numeric(quantile(borders$disc, probs = c(1 - 0.25)))
borders <- borders[borders$disc >=  val,]

mf_theme(theme)
mf_map(x = com, var = "sdevrel", type = "choro", 
       breaks = c(min(com$sdevrel), 75, 90, 100, 111, 133, 
                  max(com$sdevrel)),
       border = "white", pal = devpal, 
       leg_title = paste0("Déviation au contexte territorial",
                          "\n(100 = moyenne des communes",
                          " contigües)"),
       leg_adj = c(0,2), leg_val_rnd = 0)

mf_map(x = borders, var = "disc", type = "prop", lwd_max = 20,
       border = "black", col = "black", leg_pos = "topleft",
       leg_title = paste0("Discontinuités relatives\n",
                          "(max/min sur le ratio de référence)"), add = TRUE)
mf_title("Déviation spatiale et discontinuités")
mf_credits(credits)

# Extraire top 10 max/min 
df_max <- com[order(com$sdevrel, decreasing = TRUE),]
df_max <- df.max[1:10,]
df_min <- com[order(com$sdevrel, decreasing = FALSE),]
df_min <- df.min[1:10,]

mf_label(x = df_max, var = "NOM_COM",  halo = TRUE, bg = "white",
         col = "#8B1713")
mf_label(x = df_min, var = "NOM_COM",  halo = TRUE, bg = "white",
         col = "#135D89")

Redistributions ?

# Déviation générale
com$gdevabs <- gdev(x = com, var1 = "num", var2 = "denom", 
                    type = "abs")

# Sens des déviationS
com$gdevsign <- ifelse(com$gdevabs > 0, "Excédent d'emplois", 
                       "Déficit d'emplois")

# Cartographie
mf_theme(theme)
mf_map(epci, col = "peachpuff", border = "black", lwd = 1)
mf_map(x = com, var = c("gdevabs", "gdevsign"), 
       type = "prop_typo",
       pal = c("#F6533A","#515FAA"), inches = 0.2,
       val_order = c("Excédent d'emplois", "Déficit d'emplois"),
       val_max = max(abs(com$gdevabs)), border = "white", 
       leg_title = c(paste0("Emplois à redistribuer pour\n", 
                            "atteindre l'équilibre"),
                     "Sens de la redistribution"),
       leg_val_rnd = -2, leg_adj = c(0,2))

mf_title("Redistributions, déviation générale")
mf_credits(credits)

Redistributions ? Communes excédentaires d’emploi

# Déviation générale - top 10 des contributeurs...
df <- st_set_geometry(com, NULL)

# ... Au regard de leur masse de numérateur
df$gdevabsPerc <- df$gdevabs / df$num * 100
df <- df[order(df$gdevabsPerc, decreasing = TRUE), ]
df[1:10, c("INSEE_COM", "NOM_COM", "LIB_EPCI_SUB", "num", "gdevabs", "gdevabsPerc")]      


INSEE_COM

NOM_COM

LIB_EPCI_SUB

num

gdevabs

gdevabsPerc

75108

Paris 8e Arrondissement

Paris

174253.6

152539.9

87.5

94065

Rungis

Grand-Orly Seine Bièvre

27219.4

23732.8

87.2

75101

Paris 1er Arrondissement

Paris

61213.9

51094.7

83.5

75102

Paris 2e Arrondissement

Paris

61153.0

46147.7

75.5

75109

Paris 9e Arrondissement

Paris

117151.8

76427.5

65.2

92062

Puteaux

Paris Ouest - La Défense

77080.0

48745.2

63.2

75107

Paris 7e Arrondissement

Paris

70762.4

41392.7

58.5

75104

Paris 4e Arrondissement

Paris

41601.5

24286.0

58.4

75106

Paris 6e Arrondissement

Paris

45135.1

24022.2

53.2

94054

Orly

Grand-Orly Seine Bièvre

22609.1

11288.7

49.9

Redistributions ? Communes déficitaires d’emploi

# Déviation générale - top 10 des receveurs...
df <- st_set_geometry(com, NULL)

# ... Au regard de leur masse de numérateur
df$gdevabsPerc <- df$gdevabs / df$num * 100
df <- df[order(df$gdevabsPerc, decreasing = FALSE), ]
df[1:10, c("INSEE_COM", "NOM_COM", "LIB_EPCI_SUB", "num", "gdevabs", "gdevabsPerc")]      


INSEE_COM

NOM_COM

LIB_EPCI_SUB

num

gdevabs

gdevabsPerc

94001

Ablon-sur-Seine

Grand-Orly Seine Bièvre

650.9

-2465.9

-378.8

94056

Périgny

Grand Paris Sud Est Avenir

366.4

-1359.6

-371.1

94053

Noiseau

Grand Paris Sud Est Avenir

681.9

-2032.5

-298.0

93015

Coubron

Grand Paris Grand Est

692.5

-2039.1

-294.4

93033

Gournay-sur-Marne

Grand Paris Grand Est

1000.3

-2925.3

-292.4

93032

Gagny

Grand Paris Grand Est

5458.2

-15304.2

-280.4

94059

Le Plessis-Trévise

Grand Paris Sud Est Avenir

3030.1

-8288.1

-273.5

94058

Le Perreux-sur-Marne

Paris Est Marne et Bois

5718.4

-14076.5

-246.2

92022

Chaville

Grand Paris Seine Ouest

3619.7

-8661.0

-239.3

91589

Savigny-sur-Orge

Grand-Orly Seine Bièvre

6081.4

-14382.3

-236.5

Synthèse multiscalaire : Communes excédentaires

# Calcul typologie de synthèse
mst <- map_mst(x = com, 
               gdevrel = "gdevrel", 
               tdevrel = "tdevrel",
               sdevrel = "sdevrel",
               threshold = 150, superior = TRUE)

# Extraction de la liste 
com <- mst$geom
cols <- mst$cols
leg_val <- mst$leg_val

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "mst", type = "typo", border = "white", 
       lwd = 0.2, pal = cols, val_order = unique(com$mst),
       leg_pos = NA)
mf_map(epci, col = NA, lwd = 1, add = TRUE)

mf_legend(type = "typo", pos = "topleft", 
          val = leg_val, pal = cols, 
          title = paste0("Pour le contexte général (G)\n",
                         "et-ou territorial (T)\n",
                         "et-ou spatial (S)"))
mf_title(paste0("Synthèse multiscalaire (déviations 1.5 ", 
                "fois au-dessus de la moyenne)"))
mf_credits(credits)

# Ajouter des labels pour mst = 7
mf_label(x = com[com$mst == 7,], var = "NOM_COM", 
         halo = TRUE, overlap = FALSE, bg = "white")

Synthèse multiscalaire : Contradictions

# Calcul typologie de synthèse
mst <- map_mst(x = com, 
               gdevrel = "gdevrel", 
               tdevrel = "tdevrel",
               sdevrel = "sdevrel",
               threshold = 150, superior = TRUE)

# Extraction de la liste 
com <- mst$geom
cols <- mst$cols
leg_val <- mst$leg_val

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "mst", type = "typo", border = "white", 
       lwd = 0.2, pal = cols, val_order = unique(com$mst),
       leg_pos = NA)
mf_map(epci, col = NA, lwd = 1, add = TRUE)

mf_legend(type = "typo", pos = "topleft", 
          val = leg_val, pal = cols, 
          title = paste0("Pour le contexte général (G)\n",
                         "et-ou territorial (T)\n",
                         "et-ou spatial (S)"))
mf_title(paste0("Synthèse multiscalaire (déviations 1.5 ", 
                "fois au-dessus de la moyenne)"))
mf_credits(credits)

# Ajouter des labels pour mst = 6
mf_label(x = com[com$mst == 6,], var = "NOM_COM", 
         halo = TRUE, overlap = FALSE, bg = "white")

Synthèse multiscalaire : Communes déficitaires

# Calcul typologie de synthèse
mst <- map_mst(x = com, 
               gdevrel = "gdevrel", 
               tdevrel = "tdevrel",
               sdevrel = "sdevrel",
               threshold = 50, superior = FALSE)

# Extraction de la liste 
com <- mst$geom
cols <- mst$cols
leg_val <- mst$leg_val

# Cartographie
mf_theme(theme)
mf_map(x = com, var = "mst", type = "typo", border = "white", 
       lwd = 0.2, pal = cols, val_order = unique(com$mst),
       leg_pos = NA)
mf_map(epci, col = NA, lwd = 1, add = TRUE)

mf_legend(type = "typo", pos = "topleft", 
          val = leg_val, pal = cols, 
          title = paste0("Pour le contexte général (G)\n",
                         "et-ou territorial (T)\n",
                         "et-ou spatial (S)"))
mf_title(paste0("Synthèse multiscalaire (déviations 2 ", 
                "fois en-dessous de la moyenne)"))
mf_credits(credits)

# Ajouter des labels pour mst = 7
mf_label(x = com[com$mst == 7,], var = "NOM_COM", 
         halo = TRUE, overlap = FALSE, bg = "white")

Comparaison territoriale

plot_mst(x = com, gdevrel = "gdevrel", tdevrel = "tdevrel", sdevrel = "sdevrel", 
         lib.var = "NOM_COM", cex.names = .7,
         lib.val = c("Épinay-sur-Seine", "Pierrefitte-sur-Seine", "L'Île-Saint-Denis", "Le Pré-Saint-Gervais",
                     "Ablon-sur-Seine", "Ville-d'Avray", "Périgny"),
         legend.lab = "G : Métropole du Grand Paris, T : EPCI d'appartenance, S : Communes contigües (100 = moyenne du contexte)")

MTA : un révélateur d’inégalités ?

Un commentaire sur Pierrefitte-sur-Seine (93) posté par un internaute sur ville-ideale.fr

Et un autre sur Périgny (94)

Autres fonctions disponibles




  • bidev : Typologie de synthèse sur 2 déviations (vecteur).

  • map_bidev : Typologie de synthèse sur 2 déviations (vecteur + couleurs utiles à la cartographie des résultats)

  • plot_bidev : Graphique en diamant pour visualiser les résultats

  • mst : Typologie de synthèse sur 3 déviations (vecteur).

Retour utilisateur / questions

Postez des issues dans le dépôt GitHub du package !

Références indicatives

Pour l’histoire complète, consultez la fiche Rzine qui développe complètement l’analyse !