Statistiques phonologiques avec r

Le logiciel de statistiques r permet d’explorer la phonologie d’une langue en calculant des statistiques à partir d’un corpus, ce qui ne peut être qu’un plus dans une description de langue.

Par exemple, certains de mes travaux récents portant sur la phonologie du japonais se sont intéressés à la question de la fréquence de certaines unités, et notamment de /r/, et j’ai pour cela utilisé r. Je ne m’étendrai pas sur mes résultats concernant /r/ obtenus avec r, même s’il y aura beaucoup à dire et à redire (r-dire?) sur les erreurs (r-heurts?) et errements (r-ments?) de certains sur ce sujet, mais j’aborderai ici les aspects pratiques du calcul des statistiques de fréquence.

Pourquoi et comment faire des statistiques phonologiques?

La description de la phonologie d’une langue ne peut que gagner à être enrichie d’une perspective quantitative et statistique, du moins pour les descriptivistes pour qui la phonologie ne se limite pas à créer une transcription pour une langue non-écrite et pour les diachroniciens qui n’acceptent pas cette idée étrange que «la fréquence statistique d’un son ne nous indique pas nécessairement quoi que ce soit concernant l’histoire de ce son» (sic!).

L’outil tout désigné pour calculer ce genre de statistiques est évidemment le logiciel r. Le logiciel Phonology assistant est également capable de calculer des fréquences de consonnes et voyelles et d’explorer la phonologie d’une langue, mais il manque la versatilité de r en termes des formats d’entrée possibles (r accepte les tableaux .csv ou même au format Excel), d’export des résultats, et de possibilités d’analyses statistiques inférentielles. Bref, on peut faire beaucoup plus de choses avec r.

Je présenterai ici du code r organisé selon la méthode tidyverse, qui permet à mon avis d’obtenir un code plus léger et plus lisible. Il suffit donc de charger les bibliothèques suivantes:

library(tidyverse)
library(magrittr)
library(stringr)
library(forcats)

Données

Le plus simple est de disposer de données sous forme de tableau, typiquement au format .csv, avec un mot par ligne. Une seule colonne sera utilisée, et il peut s’agir d’une transcription plutôt phonologique ou bien plutôt phonétique, tout dépend des objectifs.

Dans le cas d’une base de données MDF de Toolbox, il suffit de lire le fichier ligne par ligne, de filtrer uniquement les lignes correspondant au champ de forme phonologique, par exemple marqués par \lx, de supprimer le marqueur et les espaces superflus, puis de mettre le vecteur de formes dans une colonne nommée «wd» d’un tableau nommé «lx»:

lx <- read_lines("lx.txt") %>%
  str_subset("\\\\lx") %>%
  str_replace("\\\\lx", "") %>%
  str_trim() %>%
  tibble(wd = .)

On pourrait tout aussi bien calculer la fréquence sur un corpus de textes Toolbox. Le marqueur à rechercher serait alors par exemple \tx, et il suffirait de supprimer les marques d’affixes, clitiques et de ponctuation, et les espaces. Il est dans notre cas inutile de séparer chaque ligne en mots, mais comme ce n’est pas très compliqué, il est préférable de le faire pour la suite.

lx <- read_lines("tx.txt") %>%
  str_subset("\\\\tx") %>%
  str_replace("\\\\tx", "") %>%
  str_replace_all("[=\\-\\+~<>,;\\.\\?!]", "") %>%
  str_split("\\s+") %>%
  unlist() %>%
  str_subset("^\\S+$") %>%
  tibble(wd = .)

Pour l’exemple, nous allons utiliser le lexique PsychoGLÀFF du français, qui est accessible librement. Il contient quelques erreurs, mais rien de rédhibitoire pour notre exemple. Le fichier utilise comme séparateur un point-virgule et ne contient pas de noms de colonnes.

lx <- read_delim("psychoGLAFF-1.0.csv", delim = ";", col_names = F)

La forme phonétique est dans la quatrième colonne, que le logiciel a donc nommé «X4». Renommons la «wd»:

lx
## # A tibble: 335,530 x 28
##            X1    X2         X3          X4          X5    X6         X7
##         <chr> <chr>      <chr>       <chr>       <chr> <int>      <dbl>
##  1 übersexuel  Ncms übersexuel        <NA>        <NA>     0 0.00000000
##  2       aède  Ncms       aède        a.ɛd        a.Ed     9 0.31199906
##  3      aèdes  Ncmp       aède        a.ɛd        a.Ed     2 0.06933312
##  4      aïeul  Ncms      aïeul       a.jœl       a.j9l   153 5.30398397
##  5     aïeuls  Ncmp      aïeul       a.jœl       a.j9l     4 0.13866625
##  6      aïeux  Ncmp      aïeul        <NA>        <NA>    84 2.91199120
##  7   aïkidoka  Ncfs   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
##  8   aïkidoka  Ncms   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
##  9  aïkidokas  Ncfp   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
## 10  aïkidokas  Ncmp   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
## # ... with 335,520 more rows, and 21 more variables: X8 <int>, X9 <dbl>,
## #   X10 <int>, X11 <dbl>, X12 <int>, X13 <dbl>, X14 <int>, X15 <dbl>,
## #   X16 <int>, X17 <dbl>, X18 <int>, X19 <int>, X20 <chr>, X21 <int>,
## #   X22 <dbl>, X23 <dbl>, X24 <dbl>, X25 <dbl>, X26 <dbl>, X27 <dbl>,
## #   X28 <dbl>
lx %<>% rename(wd = X4)
lx
## # A tibble: 335,530 x 28
##            X1    X2         X3          wd          X5    X6         X7
##         <chr> <chr>      <chr>       <chr>       <chr> <int>      <dbl>
##  1 übersexuel  Ncms übersexuel        <NA>        <NA>     0 0.00000000
##  2       aède  Ncms       aède        a.ɛd        a.Ed     9 0.31199906
##  3      aèdes  Ncmp       aède        a.ɛd        a.Ed     2 0.06933312
##  4      aïeul  Ncms      aïeul       a.jœl       a.j9l   153 5.30398397
##  5     aïeuls  Ncmp      aïeul       a.jœl       a.j9l     4 0.13866625
##  6      aïeux  Ncmp      aïeul        <NA>        <NA>    84 2.91199120
##  7   aïkidoka  Ncfs   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
##  8   aïkidoka  Ncms   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
##  9  aïkidokas  Ncfp   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
## 10  aïkidokas  Ncmp   aïkidoka aj.ki.dɔ.ka aj.ki.dO.ka     0 0.00000000
## # ... with 335,520 more rows, and 21 more variables: X8 <int>, X9 <dbl>,
## #   X10 <int>, X11 <dbl>, X12 <int>, X13 <dbl>, X14 <int>, X15 <dbl>,
## #   X16 <int>, X17 <dbl>, X18 <int>, X19 <int>, X20 <chr>, X21 <int>,
## #   X22 <dbl>, X23 <dbl>, X24 <dbl>, X25 <dbl>, X26 <dbl>, X27 <dbl>,
## #   X28 <dbl>

Définissons au préalable les consonnes et les voyelles sous formes d’expressions régulières:

cons <- c("[pbtdkgmnɲŋfvszʃʒʁɥwjl]")
voy <- c("[iyueøoəɛœɔaɑ]̃?")
cv <- str_c(cons, "|", voy)

Filtrons les quelques entrées qui contiennent par erreur d’autres caractères que les frontières de syllabe ou les voyelles et consonnes définies ci-dessus. Il faut aussi filtrer d’autres entrées erronées: celles qui commencent ou finissent par «.», celles qui comprennent des syllabes sans voyelle, celles qui ne contiennent aucune voyelle, ainsi que celles dont le champ wd est vide (NA).

lx %<>% filter(str_detect(wd, str_c("^(", cv, "|\\.)+$"))) %>%
  filter(!str_detect(wd, "^\\.")) %>%
  filter(!str_detect(wd, "\\.$")) %>%
  filter(!str_detect(wd, str_c("(^|\\.)", cons, "+(\\.|$)"))) %>%
  filter(str_detect(wd, voy)) %>%
  filter(!is.na(wd))

La forme phonétique indique la longeur vocalique, qui n’est pas pertinent en français mais totalement prévisible. Nous pouvons donc l’éliminer, ainsi que les frontières de syllabe qui ne nous serviront pas ici. Enfin, les apostrophes, dont la fonction n’est pas claire, peuvent être effacées.

lx %<>% mutate(wd = str_replace_all(wd, "ː|\\.|'", ""))

Compter les consonnes et voyelles

Nous pouvons maintenant passer au calcul des fréquences. Il suffit d’extraire la colonne wd sous forme de vecteur, puis de découper chaque forme en consonnes et voyelles, avant de transformer le résultat en vecteur et de supprimer les espaces superflus. Enfin, le résultat est transformé en tableau et sauvegardé dans une variable segs.

segs <- lx$wd %>%
  str_split(str_c("(?=", cv, ")")) %>%
  unlist() %>%
  str_subset("\\S+") %>%
  tibble(seg = .)

Rajoutons l’information de consonne (C) ou de voyelle (V) pour chaque segment:

segs %<>% mutate(cat = ifelse(str_detect(seg, voy), "V", "C"))

On obtient facilement les fréquences, auxquelles on peut rajouter les proportions en pourcentages:

freqs <- segs %>% 
  count(cat, seg, sort = T) %>%
  group_by(cat) %>%
  mutate(pct = n/sum(n)*100)
freqs
## # A tibble: 37 x 4
## # Groups:   cat [2]
##      cat   seg      n       pct
##    <chr> <chr>  <int>     <dbl>
##  1     C     ʁ 219015 18.483682
##  2     V     a 160933 17.895244
##  3     V     i 156186 17.367392
##  4     V     e 148806 16.546759
##  5     C     t 128589 10.852217
##  6     C     s 108893  9.189981
##  7     C     l  93965  7.930138
##  8     C     k  89890  7.586230
##  9     V     ɛ  87483  9.727835
## 10     V     ɔ  84886  9.439056
## # ... with 27 more rows

Avec ggplot, il est simplissime d’afficher un graphique de la fréquence par exemple des voyelles:

freqs %>% 
  filter(cat == "V") %>%
  ggplot() +
  geom_col(aes(x = seg, y = n))

On peut réordonner l’ordre des voyelles selon leur fréquence:

freqs %>% 
  filter(cat == "V") %>%
  ggplot() +
  geom_col(aes(x = fct_reorder(seg, desc(n)), y = n))

Il suffit de changer la variable y pour afficher les pourcentages, et de faire quelques réglages d’affichage:

freqs %>% 
  filter(cat == "V") %>%
  ggplot() +
  geom_col(aes(x = fct_reorder(seg, desc(n)), y = pct)) +
  labs(x = NULL, y = "fréquence relative (%)")

Enfin on peut afficher les voyelles et les consonnes sur deux graphiques alignés côte à côte:

freqs %>% 
  ggplot() +
  geom_col(aes(x = fct_reorder(seg, desc(n)), y = pct)) +
  labs(x = NULL, y = "fréquence relative (%)") +
  facet_wrap(~cat, scales = "free_x")

On peut régler quelques paramètres graphiques:

freqs %>% 
  ggplot() +
  geom_col(aes(x = fct_reorder(seg, desc(n)), y = pct), fill = "gray") +
  geom_hline(yintercept = seq(0, 20, 5), col = "white") +
  labs(x = NULL, y = "fréquence relative (%)") +
  facet_wrap(~cat, scales = "free_x") +
  scale_y_continuous(expand = c(0,0), limits = c(0,20)) +
  theme_classic() +
  theme(axis.ticks.x = element_blank(), strip.background = element_blank())

On peut changer un peu la présentation et rajouter les fréquences directement sur le graphique:

freqs %>% 
  ggplot(aes(x = fct_reorder(seg, desc(n)), y = pct)) +
  geom_col(fill = "gray") +
  geom_hline(yintercept = seq(0, 20, 5), col = "white") +
  geom_text(aes(label = round(pct,1)), vjust = -.5, size = 3.5) +
  labs(x = NULL, y = "fréquence relative (%)") +
  facet_wrap(~cat, scales = "free_x", nrow = 2) +
  scale_y_continuous(expand = c(0,0), limits = c(0,20)) +
  theme_classic() +
  theme(axis.ticks.x = element_blank(), strip.background = element_blank(), strip.text = element_blank())

À suivre ici.

Ce billet peut être cité de la façon suivante: Thomas Pellard, « Statistiques phonologiques avec r », Cipanglossia, 19 septembre 2017, http://cipanglo.hypotheses.org/548.

3 réflexions au sujet de « Statistiques phonologiques avec r »

  1. Guillaume Jacques

    Notons que ʁ est la consonne la plus fréquente, que c’est la seule uvulaire, et la seule que l’on trouve après sC- dans cette langue; comme le dirait une certaine personne, c’est bien la preuve que cette consonne n’est « pas comme les autres ». 🙂

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *