Un nuage des sons régionaux

La toponymie désigne l’ensemble des noms des lieux d’un territoire. Repères familiers de l’espace quotidien, les noms des communes sont parfois chargés d’un sens, d’une dimension symbolique dont il est difficile de se défaire. Cet article propose, modestement, de prendre un peu de hauteur sur la toponymie française et de produire une carte qui donnent une idée du « motif dans le tapis ». À l’aide d’une portion de code d’un algorithme dédié à l’anonymisation des noms de villes qui fera prochainement l’objet d’un article, il est possible d’extraire les suffixes les plus fréquents, par région, des noms de villes françaises. Ce qui suit présente la méthode, le résultat final, et quelques enseignements que l’on peut en tirer. L’idée de produire une telle carte a été largement inspirée par cet article de Mathieu Rajerison qui présente par ailleurs un panorama très intéressant de la manière de représenter le texte sur des cartes.

Les données utilisées sont publiques et librement utilisables : il s’agit d’une base croisant la base Geofla de l’ensemble des communes de France en 2015 (il y en a près de 35 000) et la base des codes postaux de l’INSEE. J’y ai ajouté une colonne région prenant en compte la classification préalable à la réforme territoriale de 2015. L’objectif était de disposer d’ensemble géographiques suffisamment grands pour déceler des régularités toponymiques (se placer au niveau du département équivalait souvent à travailler sur un trop petit nombre de villes) et suffisamment petits pour ne pas écraser totalement certaines spécificités de localisation. Par ailleurs, la carte finale pourra ainsi donner des pistes de réponses à une question qui avait suscité de nombreux débats en 2015 : le nouveau découpage administratif en 13 régions est-il pertinent au regard de la toponymie française ?

La réforme territoriale de 2015

Remarque 1 : cet article se focalise sur la France métropolitaine, car le nombre de ville dans les outre-mer a semblé trop petit pour pouvoir extraire des caractéristiques toponymiques.

Remarque 2 : les composés ont fait l’objet d’un traitement particulier pour l’algorithme d’anonymisation, mais ne sont ici pas utilisés pour produire la carte. Cela se justifie par le fait qu’il n’y ait pas vraiment de sens à intégrer les syllabes finales des noms des Saint (Saint-Denis, Saint-Michel, Saint-Maurice…). Quant aux composés formés de deux toponymes, on peut considérer que leurs deux éléments suivent les mêmes schémas que les toponymes au nom simple.

Remarque 3 : la partie « méthode » décrit pas à pas comment la carte a été produite via Python. Les lecteurs/rices désireux-ses d’en savoir plus peuvent consulter le code en entier.

Sommaire

La carte

Brisons le suspense en présentant la carte des résultats.

Cliquez pour zoomer et afficher en meilleure qualité

Quelques observations

Tout d’abord, ce qui saute aux yeux est l’influence des dialectes régionaux sur la toponymie. Les suffixes en « -ac », « -gue », « -an » sont très présents dans l’ancien pays de langue d’oc tandis que les suffixes en « -euil » ou « -lly » sont plus présents dans l’ancien pays de langue d’oil. La toponymie alsacienne – et dans une moindre mesure celle de Lorraine – est clairement germanique. La Bretagne et la Corse présentent des sons aussi très identifiés au patois local.

Ensuite lorsqu’on superpose la carte des nouvelles régions sur celles des suffixes, on peut remarquer que celles-ci recoupent en partie la toponymie existante. C’est le cas notamment de la Nouvelle-Aquitaine, ou encore de la Bretagne (seule région à mettre à l’honneur le suffixe « -ven »). En revanche, l’ancienne région Midi-Pyrénées (qui fait désormais partie de l’Occitanie) a une toponymie plus proche de celle de la Nouvelle-Aquitaine que de celle de l’ancienne région Languedoc. L’Alsace, regroupée avec la région Grand-est, a quant à elle une toponymie très différente des anciennes régions Lorraine et Champagne.

Cette carte invite par ailleurs à porter une réflexion méthodologique sur l’outil de la lexicométrie. Certaines régions se caractérisent par une apparente richesse toponymique – c’est le cas de l’Alsace, la région Midi-Pyrénées – au contraire de certaines autres – comme la Normandie ou l’Île-de-France. Ici, le choix de critères arbitraires et généraux pour sélectionner les syllabes que l’on conserve comme expressions régulières pertinentes empêche sans doute de révéler davantage de suffixes pour ces régions. Le suffixe « ville » est très présent un peu partout, et aurait pu être évincé par une analyse factorielle qui aurait déterminé les syllabes les plus caractéristiques et non les plus fréquentes (comme cela a été fait l’article précédemment cité). Mais occulter la fréquence des suffixes « -ville » aurait signifié aller à l’encontre d’une expérience ordinaire des noms de lieux. Par exemple, la carte met au jour que ce suffixe est beaucoup moins présent dans le sud que dans le centre-nord de la France.

Enfin, le nuage de mot, s’il est esthétique, n’en est pas moins une méthode critiquable : parce qu’il associe le discours et l’image, il semble illustrer à point nommé l’adage selon lequel « une image vaut 1000 mots ». Or, il s’agit bien souvent d’un raccourci un peu trop facile à emprunter. Cette carte ne dit a priori rien sur les relations de pouvoir qui peuvent exister entre les régions via la toponymie, ni sur ce que celle-ci connote.

En bref, le nuage de mot ne saurait se substituer à une analyse fine des interactions sociales et de la charge symbolique des noms de lieux, afin de révéler ce que nommer veut dire.

Méthode

Le code présenté ici donne donc des exemples en Python pour :

  • Faire quelques manipulations simples sur une base de données et des listes
  • Extraire des expressions régulières sous forme de syllabes
  • Afficher un nuage de mot

Commençons par importer les packages qui nous seront utiles, l’espace de travail et les données.

# Import des packages utiles
import os
import pandas as pd
import numpy as np
import random 
import matplotlib.pyplot as plt
import math

## Définition de l'espace de travail sur ma machine
DIR_CSV = os.path.join("/chemin_du_dossier/DIR_CSV") 

## Import des données qui s'appellent ici data_final et qui sont au format csv
data_final= pd.read_csv(os.path.join(DIR_CSV, 'data_final.csv'), sep = ";")

Nous voudrions tout d’abord disposer d’une extraction de la base initiale par région, afin de pouvoir travailler directement sur les listes de communes d’une région donnée et pas sur la liste entière. Pour cela, il est nécessaire d’exclure Paris, Lyon et Marseille de la base initiale, car ces villes y sont renseignées avec une ligne par arrondissement et non pas une ligne pour la ville entière. Déroulons notre exemple sur la région Aquitaine.

##### Fonction qui fait l'extraction de la base de région à partir de la base initiale
>>> def extract(x):
>>>    data0 = data_final[data_final["Région"] == x]
>>>    data1 = data0[data0["NOM_DEPT"] != "PARIS"]
>>>    data2 = data1[data1["NOM_COM"].apply(lambda x: not x.startswith("MARSEILLE-"))]
>>>    data3 = data2[data2["NOM_COM"].apply(lambda x: not x.startswith("LYON-"))]
>>>    return(data3)
##### Application de la fonction à la région Aquitaine
data_aqui = extract("Aquitaine")

Définissons désormais une fonction qui permette d’extraire une colonne d’une base de donnée sous la forme d’une liste

##### Fonction qui extrait une colonne du dataframe sous forme de liste
def convliste(dataframe, colonne):   
    # Je convertis la colonne des noms de communes en matrice
    matrice = np.array(dataframe[colonne])
    # puis je convertis la matrice en liste
    liste_r = list(set(matrice))
    return(liste_r)

Testons cette fonction pour la colonne des noms des communes de la région Aquitaine :

>>> listeville = convliste(data_aqui, "NOM_COM") ## On applique la fonction à la base de donnée data_aqui extraite par la fonction extract()
>>> villeaqui[:10] ### On n'affiche que les 10 premiers éléments de la liste
['LE POUT',
 'FONTET',
 'BOURGOUGNAGUE',
 'CAPLONG',
 'MENDITTE',
 'BALEIX',
 'BLASIMON',
 'GOUALADE',
 "PONTONX-SUR-L'ADOUR",
 'RIBARROUY']

Définissons maintenant la fonction qui vont nous permettre d’extraire les suffixes les plus caractéristiques d’une liste. Cette fonction récupère sauvagement et peu subtilement toute suite de lettre supérieure 3 et inférieure à 8 à la fin de chaque nom de commune

##### Fonction d'extraction des suffixes
>>> def suffixe(liste):
>>>    suflist=[]  # On crée une liste vide dans laquelle on met les suffixes générés
>>>    NREPL = 8 # On définit un nombre de caractère max pour le préfixe
>>>    for l in liste:
>>>        for i in range(3,NREPL):
>>>            p = l[-i:]
>>>            suflist.append(p)
>>>    return(suflist)
>>> suffixe(villeaqui)[:30] ## On affiche ici les 30 premiers résultats
['OUT',
 'POUT',
 ' POUT',
 'E POUT',
 'LE POUT',
 'TET',
 'NTET',
 'ONTET',
 'FONTET',
 'FONTET',
 'GUE',
 'AGUE',
 'NAGUE',
 'GNAGUE',
 'UGNAGUE']

Évidemment, il va falloir nettoyer tout ça. Définissons d’abord quelques fonctions de calcul qui nous serons utiles par la suite.

##### Fonctions de calcul
## Je compte les occurrences de chaque élément d'une liste
def compt(liste):
    couples = Counter(liste).most_common()
    return(couples)

## Je calcule l'effectif moyen de l'occurrence des éléments d'une liste
def eff_moy(x):
    res = 0
    somme = 0
    for c in x:
        somme = somme + c[1]
    res = somme / len(x)
    return(res)

Définissons ensuite 2 fonctions de purification. La première retire de la liste les suffixes qui sont un sous-suffixe d’un suffixe, sans être eux-mêmes. En effet, si le suffixe « gnague » apparait plusieurs fois, je veux conserver cette régularité, mais je ne veux pas forcément garder les sous-suffixes « nague », « ague », « gue » et « ue ». En fait, je ne veux garder un sous-suffixe que si sa fréquence d’occurence n’est pas négligeable par rapport au sur-suffixe, car cela voudrait dire que le plus petit apparaît aussi dans un certain nombre d’autres noms. La seconde fonction cherche à privilégier les préfixes qui sont longs mais qui apparaissent un peu moins souvent par rapport à des préfixes qui sont courts mais très fréquents.

##### Fonctions de purification
### 1 ###
## Je retire de la liste de suffixes les suffixes qui
    # - sont un sous-suffixes d'un suffixe
    # - ne sont pas eux-mêmes
    # - ont une fréquence d'occurrence négligeable par rapport à leur sur-suffixe
def purif(liste):
    list_r = []
    remove = 0
    for x in liste:
        for x2 in liste:
            if x in x2 and len(x) != len(x2) and liste.count(x) < 1.5*liste.count(x2):
                remove = 1
        if remove == 0:
            list_r.append(x)
        remove = 0
    return(list_r)

### 2 ###
## Je cherche à privilégier les préfixes qui sont gros mais qui apparaissent un peu moins souvent par rapport à des préfixes qui sont petits mais très fréquents
## (taille*effectif) => privilégie les préfixes longs qui apparaissent peu 
## On compare ça à l'effectif moyen pour s'avoir si on le garde ou pas. Si <, on le vire

def sdc(x):
    res = []
    # remove = 0
    for c in x:
        if len(c[0])*c[1] > 12*eff_moy(x):
            res.append(c)
    return(res)

Mettons tout cela dans une fonction qui prend en entrée le nom de la région, et qui ne traite que les cas des villes au nom entier.

def freqsuf(x):
    def supra_fonction(x):
        ## Je crée la base de région
        data_region = extract(x)
        # Je convertis la colonne des noms de communes en matrice
        liste_r = convliste(data_region, "NOM_COM")
        return(liste_r)
    
    liste_r = supra_fonction(x)
    ## Je mets à part les villes avec les tirets
    data_tiret = tri_tiret(liste_r)[0]
    data_entier = tri_tiret(liste_r)[1]

    ############ Création des suffixes ############
    suflist = suffixe(data_entier)
    suflist_r = purif(suflist)
    suf_couples = compt(suflist_r) ## Résultat = liste de couples (suffixe, fréquence)
    suflist_r2 = map(lambda x : x, sdc(suf_couples))
    suflist_r2 = list(suflist_r2) 
    dicres = dict(suflist_r2) ## Convertit en un dictionnaire
    return(dicres)

Testons le résultat pour la région Aquitaine.

>>> freqsuf("Aquitaine")
{'GNAC': 35,
 'RES': 30,
 'SAC': 30,
 'SSE': 30,
 'LES': 27,
 'RAC': 27,
 'IGNAC': 23,
 'ILLAC': 22,
 'ZAC': 19,
 'IAC': 18,
 'ACQ': 17,
 'NES': 17,
 'LLES': 16,
 'ADE': 15,
 'NNE': 14,
 'SSAC': 14,
 'ERES': 13,
 'ENS': 13,
 'ONS': 13,
 'AUX': 12,
 'MONT': 12,
 'EUIL': 11,
 'OSSE': 11,
 'ILLE': 11,
 'RON': 10,
 'GNE': 10,
 'ZAN': 10,
 'IER': 10,
 'LON': 10,
 'TTE': 9,
 'EIX': 9,
 'GUES': 9,
 'OUS': 9,
 'YRAC': 9,
 'ANS': 9,
 'DOS': 9,
 'NSAC': 9,
 'ENX': 9,
 'AGNAC': 8,
 'LADE': 7,
 'ANCE': 7,
 'OURS': 7,
 'IRAC': 7,
 'ERAC': 7,
 'ANOS': 7,
 'ILLES': 6,
 'ISSAC': 6,
 'ROLLES': 6,
 'ARSAC': 6,
 'UILLAC': 6,
 'ILLON': 6,
 'AILLAN': 5,
 'SIGNAC': 5,
 'AURIAC': 5}

Remarquons que le résultat s’affiche sous forme d’un dictionnaire. C’est un type d’objet très pratique en Python qui permet d’associer une valeur à une clé (ici, le suffixe à sa fréquence), un peu comme un dictionnaire associe une définition à un mot. Remarquons également que nos critères de purification conservent plusieurs suffixes terminant pareil (« -ignac », -« illac », « -auriac », « -issac »…) sans les agréger pour autant (autour d’un « -ac »). Avec d’autres critères, les résultats auraient été différents : il s’agit donc d’un choix du chercheur ou de la chercheuse.

Passons désormais à la projection en nuage de mot. Il s’agit de la partie la plus simple car il existe des fonctions spécifiques. Chargeons les packages nécessaires.

pip install wordcloud ## On installe le package.

import re
from collections import Counter
from PIL import Image
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt

## Import de l'espace de travail avec les images de ma machine
DIR_IMG = os.path.join("/chemin_du_dossier/DIR_IMG") 

Pour projeter le nuage de mot, il est nécessaire de disposer d’une image avec un fond transparent. Prenons ici la carte de la région Aquitaine.

On assigne cette image à un masque qui donnera forme à notre nuage de mot. On assigne aussi sa couleur à la couleur de notre nuage. Le dictionnaire des suffixes créé en amont sera le texte du nuage. La fonction WordCloud crée ensuite le nuage.

## Masque de l'image
my_mask = np.array(Image.open('Aquitaine.png')) 

## Les couleurs sont celles de l'image
image_colors = ImageColorGenerator(my_mask) 

## On assigne le dictionnaire créé en amont à une variable "text"
text = freqsuf("Aquitaine") 

## Définition du nuage de mot
wc = WordCloud(width=1000, height=1000, background_color="white", mask=my_mask, max_font_size=250) 
wc.generate_from_frequencies(text) 
## La taille des mots sera proportionnelle à la fréquence

Il suffit ensuite d’afficher le graphique.

## Afficher le graphique
plt.figure(figsize= (20,14)) ## On définit la taille
plt.imshow(wc.recolor(color_func=image_colors), interpolation='bilinear') ## On affiche le nuage de mot
plt.axis('off') ## On retire l'axe du graphique
plt.show() ## On montre le graphique

Voici la fonction permettant de générer les nuages pour chaque région, et la boucle permettant de générer d’un coup toutes les images.

def plot_word_cloud_suf(region) :
    # Creation du dictionnaire
    sufliste = freqsuf(f'{region}')
    text = dict(sufliste)
    # Définir un masque
    custom_mask = np.array(Image.open(f'{region}.png'))
    # Couleurs
    image_colors = ImageColorGenerator(custom_mask) 
    # Définir le calque du nuage des mots
    wc = WordCloud(width=1000, height=1000, background_color="white", mask=custom_mask, max_font_size=200)
    plt.figure(figsize= (20,14))
    wc.generate_from_frequencies(text)
    plt.imshow(wc.recolor(color_func=image_colors), interpolation='bilinear')
    plt.axis('off')
    plt.savefig(f'carte_{region}.png')
    plt.show()
    return 

## Boucle pour générer les images
listeregion = convliste(data_final, "Région")
for region in listeregion2:
    plot_word_cloud_suf(region)

Moyennant quelques traitements sur un logiciel graphique, on peut assembler la carte !

Cliquez pour zoomer et afficher en meilleure qualité

Pour citer ce billet : Julia Descamps, « Un nuage des sons régionaux », publié sur Outsiders le 06/02/2021. Lien : https://shsoutsiders.wordpress.com/2021/02/05/un-nuage-des-sons-regionaux/

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s