POL 6022 - Méthodes quantitatives avancées

Evelyne Brie

Hiver 2026

Analyse textuelle

Cette semaine, nous allons: (1) charger un jeu de données, (2) manipuler des chaînes de caractères, (3) traiter le contenu d’une variable textuelle en concaténant des vecteurs et en créant une Matrice Document-Terme, et (4) créer une nouvelle variable représentant le pourcentage de textes dans lesquels chaque unigramme apparaît.

Le jeu de données sur lequel nous travaillons aujourd’hui contient du texte provenant de sources de désinformation sur le Web. Il contient du texte de 244 sites Web et représente 12 999 publications au total (nous utiliserons un sous-ensemble de 5 000 publications dans ce laboratoire). Les pages Web de désinformation ont été identifiées à l’aide de l’extension Chrome BS Detector de Daniel Sieradski. Notez que par conséquent, les données que nous analyserons seront remplies d’absurdités…

Fonctions pertinentes: paste(), gsub(), Corpus(), VectorSource(), DocumentTermMatrix(), as.matrix(), apply(), order().


1. Chargement des données

Nous chargeons d’abord le fichier FakeNewsData.csv (disponible dans Studium: dossier Diapositives, semaine 12) dans notre environnement à l’aide de read.csv().

# Définir le répertoire de travail
setwd(dirname(rstudioapi::getSourceEditorContext()$path))

# Charger le jeu de données avec read.csv()
myData <- read.csv("FakeNewsData.csv")

# Observer les dimensions du jeu de données avec dim()
dim(myData)
## [1] 5000   21
# Observer les noms des variables avec colnames()
colnames(myData)
##  [1] "X"                  "uuid"               "ord_in_thread"     
##  [4] "author"             "published"          "title"             
##  [7] "text"               "language"           "crawled"           
## [10] "site_url"           "country"            "domain_rank"       
## [13] "thread_title"       "spam_score"         "main_img_url"      
## [16] "replies_count"      "participants_count" "likes"             
## [19] "comments"           "shares"             "type"
# Afficher la classe de chaque variable avec sapply() et class()
sapply(myData, FUN = class)
##                  X               uuid      ord_in_thread             author 
##          "integer"        "character"          "integer"        "character" 
##          published              title               text           language 
##        "character"        "character"        "character"        "character" 
##            crawled           site_url            country        domain_rank 
##        "character"        "character"        "character"          "integer" 
##       thread_title         spam_score       main_img_url      replies_count 
##        "character"          "numeric"        "character"          "integer" 
## participants_count              likes           comments             shares 
##          "integer"          "integer"          "integer"          "integer" 
##               type 
##        "character"
# Observer le titre et le texte de la cinquième observation
myData[5, c("title", "text")]
##                                                                                                 title
## 5 FANTASTIC! TRUMP'S 7 POINT PLAN To Reform Healthcare Begins With A Bombshell! » 100percentfedUp.com
##                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   text
## 5 Email HEALTHCARE REFORM TO MAKE AMERICA GREAT AGAIN \nSince March of 2010, the American people have had to suffer under the incredible economic burden of the Affordable Care Act—Obamacare. This legislation, passed by totally partisan votes in the House and Senate and signed into law by the most divisive and partisan President in American history, has tragically but predictably resulted in runaway costs, websites that don’t work, greater rationing of care, higher premiums, less competition and fewer choices. Obamacare has raised the economic uncertainty of every single person residing in this country. As it appears Obamacare is certain to collapse of its own weight, the damage done by the Democrats and President Obama, and abetted by the Supreme Court, will be difficult to repair unless the next President and a Republican congress lead the effort to bring much-needed free market reforms to the healthcare industry. \nCongress must act. Our elected representatives in the House and Senate must: \n1. Completely repeal Obamacare. Our elected representatives must eliminate the individual mandate. No person should be required to buy insurance unless he or she wants to. \n2. Modify existing law that inhibits the sale of health insurance across state lines. As long as the plan purchased complies with state requirements, any vendor ought to be able to offer insurance in any state. By allowing full competition in this market, insurance costs will go down and consumer satisfaction will go up. \n3. Allow individuals to fully deduct health insurance premium payments from their tax returns under the current tax system. Businesses are allowed to take these deductions so why wouldn’t Congress allow individuals the same exemptions? As we allow the free market to provide insurance coverage opportunities to companies and individuals, we must also make sure that no one slips through the cracks simply because they cannot afford insurance. We must review basic options for Medicaid and work with states to ensure that those who want healthcare coverage can have it. TRENDING ON 100% Fed Up

2. Traitement du contenu textuel

Avant d’analyser les données, il peut être utile d’apporter des modifications aux textes contenus dans le cadre de données. Voici quelques fonctions utiles pour modifier des chaînes de caractères dans R.

Fonction Description
gsub() Supprime et substitue une chaîne de caractères
strsplit() Divise une chaîne de caractères donnée
paste() Assemble des éléments séparés en une seule chaîne
substr() Sélectionne un sous-ensemble d’une chaîne
toupper() Convertit tous les éléments en majuscules

Nous n’utiliserons que les fonctions paste() et gsub() aujourd’hui (nous allons concaténer deux colonnes de textes pour former un seul vecteur à analyser, puis remplacer certains mots dans ce vecteur). Si vous êtes intéressé·e, vous trouverez des exemples de manipulation de texte avec d’autres fonctions ici.

2.1 Fusionner deux variables textuelles pour créer une variable totalText

# Créer une nouvelle variable combinant le texte et le titre de l'article
myData$totalText <- paste(myData$title, myData$text, sep = " ")

# Convertir cette variable en caractère
myData$totalText <- as.character(myData$totalText)

# Afficher le contenu de la deuxième publication
myData$totalText[2]
## [1] "Re: Why Did Attorney General Loretta Lynch Plead The Fifth? Why Did Attorney General Loretta Lynch Plead The Fifth? Barracuda Brigade 2016-10-28 Print The administration is blocking congressional probe into cash payments to Iran. Of course she needs to plead the 5th. She either can’t recall, refuses to answer, or just plain deflects the question. Straight up corruption at its finest! \n100percentfedUp.com ; Talk about covering your ass! Loretta Lynch did just that when she plead the Fifth to avoid incriminating herself over payments to Iran…Corrupt to the core! Attorney General Loretta Lynch is declining to comply with an investigation by leading members of Congress about the Obama administration’s secret efforts to send Iran $1.7 billion in cash earlier this year, prompting accusations that Lynch has “pleaded the Fifth” Amendment to avoid incriminating herself over these payments, according to lawmakers and communications exclusively obtained by the Washington Free Beacon. \nSen. Marco Rubio (R., Fla.) and Rep. Mike Pompeo (R., Kan.) initially presented Lynch in October with a series of questions about how the cash payment to Iran was approved and delivered. \nIn an Oct. 24 response, Assistant Attorney General Peter Kadzik responded on Lynch’s behalf, refusing to answer the questions and informing the lawmakers that they are barred from publicly disclosing any details about the cash payment, which was bound up in a ransom deal aimed at freeing several American hostages from Iran. \nThe response from the attorney general’s office is “unacceptable” and provides evidence that Lynch has chosen to “essentially plead the fifth and refuse to respond to inquiries regarding [her]role in providing cash to the world’s foremost state sponsor of terrorism,” Rubio and Pompeo wrote on Friday in a follow-up letter to Lynch. More Related"
# Supprimer les termes non pertinents avec gsub()
myData$totalText <- gsub("Print", "", myData$totalText)
myData$totalText <- gsub("More Related", "", myData$totalText)
myData$totalText <- gsub("\n", "", myData$totalText) # Indique un saut de ligne dans les données brutes

# Vérification: afficher à nouveau le contenu de la deuxième publication
# Remarquez comment ces termes ont été supprimés avec succès?
myData$totalText[2]
## [1] "Re: Why Did Attorney General Loretta Lynch Plead The Fifth? Why Did Attorney General Loretta Lynch Plead The Fifth? Barracuda Brigade 2016-10-28  The administration is blocking congressional probe into cash payments to Iran. Of course she needs to plead the 5th. She either can’t recall, refuses to answer, or just plain deflects the question. Straight up corruption at its finest! 100percentfedUp.com ; Talk about covering your ass! Loretta Lynch did just that when she plead the Fifth to avoid incriminating herself over payments to Iran…Corrupt to the core! Attorney General Loretta Lynch is declining to comply with an investigation by leading members of Congress about the Obama administration’s secret efforts to send Iran $1.7 billion in cash earlier this year, prompting accusations that Lynch has “pleaded the Fifth” Amendment to avoid incriminating herself over these payments, according to lawmakers and communications exclusively obtained by the Washington Free Beacon. Sen. Marco Rubio (R., Fla.) and Rep. Mike Pompeo (R., Kan.) initially presented Lynch in October with a series of questions about how the cash payment to Iran was approved and delivered. In an Oct. 24 response, Assistant Attorney General Peter Kadzik responded on Lynch’s behalf, refusing to answer the questions and informing the lawmakers that they are barred from publicly disclosing any details about the cash payment, which was bound up in a ransom deal aimed at freeing several American hostages from Iran. The response from the attorney general’s office is “unacceptable” and provides evidence that Lynch has chosen to “essentially plead the fifth and refuse to respond to inquiries regarding [her]role in providing cash to the world’s foremost state sponsor of terrorism,” Rubio and Pompeo wrote on Friday in a follow-up letter to Lynch. "

 

2.2 Créer une Matrice Document-Terme

Une Matrice Document-Terme affiche la fréquence des termes dans un ensemble de documents. Ces documents peuvent être stockés sous forme de fichiers distincts, ou comme différentes lignes d’un vecteur (comme dans ce laboratoire).

 

Qu’est-ce qu’une Matrice Document-Terme?

Lors de la création d’une MDT, on supprime la ponctuation et les mots vides (c.-à-d. les mots courants comme « le », « un », etc.) et on effectue la racinisation (stemming), c’est-à-dire la réduction des mots à leur racine (par exemple, « élections » et « électeurs » deviennent « élect »).

Terme 1 Terme 2 Terme 3 Terme 4
Document 1 1 0 3 0
Document 2 0 0 0 0
Document 3 0 1 0 0
Document 4 3 0 3 0
Document 5 2 0 1 0

 

Nous utiliserons le package tm (text mining) pour effectuer l’analyse textuelle. Installez ce package avec install.packages() et chargez-le avec library().

# Charger le package tm
library(tm)

# Créer un corpus à partir du texte dans le vecteur "myData$totalText"
myCorpus <- Corpus(VectorSource(myData$totalText),
                   readerControl = list(language = "eng"))

# Créer une Matrice Document-Terme
dtm <- DocumentTermMatrix(myCorpus,
                          control = list(stemming = TRUE,
                                         stopwords = TRUE,
                                         minWordLength = 3,
                                         removeNumbers = TRUE,
                                         removePunctuation = TRUE))

# Observer les dimensions de la Matrice Document-Terme
dim(dtm)
## [1]  5000 89818

3. Calcul des unigrammes

Nous créons ici une matrice avec tous les termes contenus dans la variable et une variable représentant le nombre total de publications dans lesquelles un terme donné (ou unigramme) apparaît. Autrement dit, nous voulons savoir quels mots apparaissent dans la plupart des publications de désinformation. C’est l’une des façons de calculer la prévalence des termes — et c’est aussi la méthode à utiliser pour répondre aux questions du travail pratique portant sur l’analyse textuelle.

# Créer une matrice avec les termes de la variable
myWords <- as.matrix(dtm)

# Calculer le nombre de publications dans lesquelles un terme donné apparaît
v1 <- apply((myWords > 0) * 1, 2, sum)

# Afficher les 10 premiers termes du vecteur
v1[1:10]
##         ––––     –‘ballot        –‘big      –“cool”      –“could       –“grab 
##            1            1            1            1            1            1 
##        –“old       –“our” –“profession     –“spies” 
##            1            1            1            1
# Générer un pourcentage
v2 <- v1 / dim(dtm)[1]

# Quel est le pourcentage maximum de publications dans lesquelles un terme apparaît?
max(v2)
## [1] 0.5148
# Quel terme apparaît dans le plus grand pourcentage de publications?
which(v2 == max(v2))
##  will 
## 81727
# Voir les 30 termes les plus utilisés
v2[order(v2, decreasing = T)][1:30]
##    will     one   peopl    time    like   state    also     can    just     new 
##  0.5148  0.4874  0.4464  0.4296  0.4270  0.4168  0.4004  0.4002  0.3966  0.3898 
##     now    said    year     say    even     get     use    make   trump    mani 
##  0.3890  0.3878  0.3862  0.3678  0.3670  0.3542  0.3510  0.3466  0.3258  0.3228 
## hillari  report clinton    take     day   elect    work    call   right    come 
##  0.3216  0.3212  0.3172  0.3156  0.3142  0.3138  0.3124  0.3106  0.3036  0.3026
# Conserver uniquement les termes présents dans plus de 5% des publications
dtm2 <- dtm[, v2 > .05]

# Combien de mots/colonnes restent-ils?
dim(dtm2)
## [1] 5000  915
# Comparaison avec la dtm originale
dim(dtm)
## [1]  5000 89818
# Combien de termes ont été supprimés?
dim(dtm2)[2] - dim(dtm)[2]
## [1] -88903
 

Bilan

Nous avons parcouru les étapes fondamentales de l’analyse textuelle automatisée:

  • Chargement et exploration des données textuelles brutes.
  • Prétraitement : concaténation de variables textuelles avec paste(), suppression de termes parasites avec gsub().
  • Construction de la MDT : racinisation, suppression des mots vides et de la ponctuation via le package tm.
  • Calcul des unigrammes : identification des termes les plus fréquents dans l’ensemble du corpus.
  • Filtrage : conservation uniquement des termes présents dans plus de 5% des publications, réduisant considérablement la dimensionnalité de la matrice.

Cette approche constitue la base de nombreuses méthodes d’analyse textuelle plus avancées (analyse de sentiment, modèles thématiques, etc.).

 


 

Exercice

Effectuez une analyse similaire sur la variable myData$thread_title en suivant les instructions ci-dessous.

Étape 1

Chargez le jeu de données.

Fonction pertinente: read.csv().

Étape 2

Créez un corpus.

Fonctions pertinentes: Corpus(), VectorSource().

Étape 3

Créez une Matrice Document-Terme.

Fonction pertinente: DocumentTermMatrix().

Étape 4

Créez une matrice à partir de la Matrice Document-Terme et calculez le nombre (et non le pourcentage) de titres de fils de discussion dans lesquels un terme donné apparaît.

Fonctions pertinentes: as.matrix(), apply().

Étape 5

Affichez les 5 termes les plus utilisés.

Fonction pertinente: order().

# Votre code ici
# Étape 1 : charger le jeu de données (déjà fait plus haut)
# myData <- read.csv("FakeNewsData.csv")

# Étape 2 : créer un corpus à partir de myData$thread_title
corpusTitres <- Corpus(VectorSource(as.character(myData$thread_title)),
                       readerControl = list(language = "eng"))

# Étape 3 : créer une Matrice Document-Terme
dtm_titres <- DocumentTermMatrix(corpusTitres,
                                 control = list(stemming = TRUE,
                                                stopwords = TRUE,
                                                minWordLength = 3,
                                                removeNumbers = TRUE,
                                                removePunctuation = TRUE))

# Étape 4 : créer une matrice et calculer le nombre de titres
# dans lesquels chaque terme apparaît
myWordsTitres <- as.matrix(dtm_titres)
v_titres <- apply((myWordsTitres > 0) * 1, 2, sum)

# Étape 5 : afficher les 5 termes les plus utilisés
v_titres[order(v_titres, decreasing = TRUE)][1:5]
##   trump hillari clinton   elect   video 
##     662     536     471     263     249