Cours 4 : Les sondages

Introduction aux mégadonnées en sciences sociales

Laurence-Olivier M. Foisy

Université de Montréal

Retour sur le TP1

  • Qu’est-ce qui était le plus difficile?
  • Combien d’heures ça vous a pris?
  • Pourquoi selon vous le concept de chemin d’accès est autant difficile à maitriser?
  • C’est quoi un .RProj? Comment ça fonctionne?
  • C’est quoi un package? library(ggplot2)
  • Pourquoi le Quarto ne compilait pas?
  • Rouler le code ligne par ligne
  • Comprenez vous comment fonctionne git?
  • “This is not a git repository”
  • Est-ce que vous seriez capable de le refaire sans regarder les instructions?

Plan du cours

  1. Introduction
  2. Tidy data
  3. Les sondages
    1. Comment collecter des données de sondages?
    2. Comment nettoyer des données de sondages?

L’objectif

Notre travail

Tidy Data

Pourquoi “Tidy” les données?

“80% du temps d’analyse de données est consacré au nettoyage et à la préparation des données”

  • Facilite la manipulation
  • Simplifie la visualisation
  • Standardise l’analyse

Les 3 Règles des Données Tidy

Règle Description
1️⃣ Chaque colonne est une variable
2️⃣ Chaque ligne est une observation
3️⃣ Chaque dataframe est un élément observé

Les 5 Problèmes Communs

  1. Les en-têtes de colonnes sont des valeurs
  2. Plusieurs variables dans une colonne
  3. Variables stockées dans les lignes et colonnes
  4. Plusieurs types d’observations dans un tableau
  5. Une même observation dans plusieurs tableaux

1. Les en-têtes sont des valeurs

❌ Données de religion et revenu

religion <$10k $10-20k $20-30k
Agnostic 27 34 60
Atheist 12 27 37
Buddhist 27 21 30

1. Les en-têtes sont des valeurs

library(dplyr)
library(tidyr)

df %>%
  pivot_longer(
    cols = matches("\\$"), # Sélectionne les colonnes contenant "$"
    names_to = "revenu",
    values_to = "freq"
  ) %>%
  # Optionnel: ordonner les niveaux de revenu
  mutate(
    revenu = factor(
      revenu,
      levels = c("<$10k", "$10-20k", "$20-30k"),
      ordered = TRUE
    )
  ) %>%
  arrange(religion, revenu)

1. Les en-têtes sont des valeurs

✅ Données de religion et revenu

religion revenu freq
Agnostic <$10k 27
Agnostic $10-20k 34
Agnostic $20-30k 60
Atheist <$10k 12
Atheist $10-20k 27

2. Plusieurs variables dans une colonne

❌ Données sur la tuberculose

pays année m014 m1524 m2534 m3544
AD 2000 0 0 1 0
AE 2000 2 4 4 6
AF 2000 52 228 183 149

2. Plusieurs variables dans une colonne

df %>%
  pivot_longer(
    cols = starts_with("m"),
    names_to = "groupe",
    values_to = "cas"
  ) %>%
  separate(groupe, 
    into = c("sexe", "age"),
    sep = 1,
    ) %>%
  mutate(
    sexe = case_when(
      sexe == "m" ~ "M",
      sexe == "f" ~ "F"
    ),
    age = case_when(
      age == "014" ~ "0-14",
      age == "1524" ~ "15-24",
      age == "2534" ~ "25-34",
      age == "3544" ~ "35-44"
    )
  )

2. Plusieurs variables dans une colonne

✅ Données sur la tuberculose

pays année sexe groupe_age cas
AD 2000 M 0-14 0
AD 2000 M 15-24 0
AD 2000 M 25-34 1
AD 2000 M 35-44 0
AE 2000 M 0-14 2

3. Variables dans lignes et colonnes

❌ Données sur la température au Mexique

id élément d1 d2 d3 d4
MX17004 tmax NA 27.3 24.1 NA
MX17004 tmin NA 14.4 14.4 NA

3. Variables dans lignes et colonnes

# Solution avec pivot_longer
df %>%
  pivot_longer(
    cols = starts_with("d"),
    names_to = "date",
    values_to = "valeur",
    values_drop_na = TRUE  # Optionnel: supprime les NA
  ) %>%
  rename(mesure = élément)

3. Variables dans lignes et colonnes

✅ Données sur la température au Mexique

id date mesure valeur
MX17004 d1 tmax NA
MX17004 d1 tmin NA
MX17004 d2 tmax 27.3
MX17004 d2 tmin 14.4
MX17004 d3 tmax 24.1

4. Plusieurs types d’observations

❌ Données sur les chansons et le classement du Billboard

chanson artiste durée rang_sem1 rang_sem2
Baby Don’t 2Pac 4:22 87 82
Try Again Aaliyah 4:03 84 62

4. Plusieurs types d’observations

# Création des deux tables
# Table 1: Info Chansons
chansons <- df %>%
  select(chanson, artiste, durée) %>%
  mutate(id_chanson = row_number())

# Table 2: Classements
classements <- df %>%
  mutate(id_chanson = row_number()) %>%
  pivot_longer(
    cols = starts_with("rang_"),
    names_to = "semaine",
    values_to = "rang",
    names_prefix = "rang_sem"
  )

4. Plusieurs types d’observations

✅ Données sur les chansons du Billboard

id_chanson chanson artiste durée
1 Baby Don’t 2Pac 4:22
2 Try Again Aaliyah 4:03

✅ Données sur le classement du Billboard

id_chanson semaine rang
1 1 87
1 2 82
2 1 84
2 2 62

5. Une observation dans plusieurs tableaux

❌ Données médicales

Table 1 - Info Patient:

id_patient nom age
1 Alice 32
2 Bob 45

Table 2 - Mesures:

id_patient pression glucose
1 120/80 95
2 130/85 105

5. Une observation dans plusieurs tableaux

# Transformation en format long
df_mesures <- df_mesures %>%
  pivot_longer(
    cols = c(pression, glucose),
    names_to = "mesure",
    values_to = "valeur"
  )

# Joindre les tables
df_final <- df_patients %>%
  left_join(df_mesures, by = "id_patient")

5. Une observation dans plusieurs tableaux

✅ Données médicales

id_patient nom age mesure valeur
1 Alice 32 pression 120/80
1 Alice 32 glucose 95
2 Bob 45 pression 130/85
2 Bob 45 glucose 105

Avantages des Données Tidy

  • Facilite l’utilisation des outils d’analyse
  • Standardise la structure des données
  • Simplifie la visualisation
  • Rend le code plus lisible
# Exemple avec des données tidy
donnees_tidy %>%
  group_by(categorie) %>%
  summarise(moyenne = mean(valeur))

En Résumé

  • Structure consistante
  • Une observation = une ligne
  • Une variable = une colonne
  • Un type d’observation = un tableau

La structure tidy facilite l’analyse! 🎯

Les sondages

Observer vs. Demander

  • Observer: Regarder ce que les gens font.
  • Demander: Poser des questions aux gens.
    • Sondages
    • Entrevues

Histoire des sondages

Ère années Échantillonnage Entrevue Environnement des données
Première vague 1930 Échantillonnage probabiliste par zone Face à face Sondage seul
Deuxième vague 1950 Échantillonnage probabiliste par composition aléatoire (RDD) Téléphone Sondage seul
Troisième vague 2000 Échantillonnage non-probabiliste Ordinateur Sondages liées aux sources de mégadonnées

Représentativité des sondages

Un sondage historique

  • Magazine populaire Literary Digest
  • Sondages présidentiels réussis en 1920, 1924, 1928 et 1932
  • En 1936, sondage massif durant la grande dépression :
    • 10 millions de bulletins envoyés
    • 2.4 millions de réponses (240x plus que les sondages modernes!)
    • Sources : annuaires téléphoniques et registres automobiles

Le résultat?

  • Prédiction : Victoire d’Alf Landon
  • Réalité : Victoire écrasante de Roosevelt

Erreurs de représentativité

  • Les gens de la frame population du Literary Digest étaient systématiquement différents de la population générale.
  • L’échantillonnage doit être représentatif de la population cible.
  • Les non-réponses doivent être distribuées de manière aléatoire.

La pondération

Qu’est-ce que la pondération?

  • On donne plus ou moins de “poids” à certaines réponses pour corriger les biais d’échantillonnage
  • But : Faire en sorte que notre échantillon représente mieux la population réelle

Exemple simple : La cafétéria universitaire

Imaginons un sondage sur la satisfaction des repas :

  • 50 étudiants de 1er cycle répondent
  • 50 étudiants des cycles supérieurs répondent
  • MAIS dans la vraie université, c’est 70-30

Pourquoi pondérer?

Le problème de la représentativité

  • Certains groupes sont sur-représentés
  • D’autres sont sous-représentés
  • Les non-réponses ne sont pas aléatoires

L’exemple de la cafétéria (suite)

  • Dans notre sondage: 50% cycle 1 / 50% cycles sup
  • Réalité: 70% cycle 1 / 30% cycles sup
  • Solution: Donner plus de poids aux réponses des cycles sup

Comment pondérer?

La formule de base

Poids = % dans population / % dans échantillon

Pour notre exemple

  • Étudiants 1er cycle: 70/50 = 1.4
  • Étudiants cycles sup: 30/50 = 0.6

En pratique

  1. On compare notre échantillon au recensement
  2. On calcule les poids pour chaque groupe
  3. On applique les poids dans nos analyses

Exemple réel : Sondage électoral

La situation

  • Sondage de 1000 personnes, population totale de 1000000
  • Sur-représentation des 65+ ans
  • Sous-représentation des 18-34 ans

Les données

Âge Sondage (%) Sondage (n) Recensement (%) Population (n) Poids
18-34 20% 200 30% 300,000 1.5
35-64 45% 450 45% 450,000 1.0
65+ 35% 350 25% 250,000 0.71
Total 100% 1,000 100% 1,000,000 -

Visualisation de l’ajustement

Comment utiliser les poids en R?

Exemple dans une régression linéaire

# Exemple avec une régression linéaire
# Opinion sur échelle 0-10 selon l'âge et le revenu
model <- lm(opinion ~ revenu,
            data = df,
            weights = poids)  # Ajouter les poids ici
# Voir les résultats
summary(model)

Exemple dans un graphique


# Nuage de points pondéré
ggplot(sondage_individual, aes(x = revenu, y = opinion)) +
  geom_smooth(method = "lm", aes(weight = poids)) +
  labs(title = "Relation revenu-opinion (pondérée)",
       x = "Revenu",
       y = "Opinion (0-10)")

Mesure

Une question de formulation…

  • Le premier demande:
    “Est-ce que c’est permis de fumer pendant qu’on prie?”
    Non! C’est un péché

  • Le deuxième demande:
    “Est-ce que c’est permis de prier pendant qu’on fume?”
    Oui! Bien sûr

Point méthodologique

La façon dont on formule une question peut drastiquement influencer la réponse obtenue!

L’effet de l’ordre des questions

Version A:
“How much do you agree: Individuals are more to blame than social conditions for crime and lawlessness in this country.”
Résultat: ~60% blâment les individus

Version B:
“How much do you agree: Social conditions are more to blame than individuals for crime and lawlessness in this country.”
Résultat: ~60% blâment les conditions sociales

Avertissement

Même question avec ordre inversé = conclusions opposées!

Meilleure façon de poser des questions

  • Copier les questions des sondages précédents

Comment collecter des données de sondages? 📈

Quelle plateforme de sondage choisir?

  • La plateforme de sondage la plus populaire dans le monde académique et corporatif.
  • S’intègre avec les panels de répondants les plus populaires.
  • Très dispendieux. Environ 15000$ par année.
  • Permet d’ajouter des quotas et des logiques conditionnelles.

Autres options?

  • Données électorales comme les CES
  • Banques de données comme Odesi

Nettoyer des données de sondages 🧹

Étude électorale canadienne de 1993

Raw

CPSIGEN CPSA3 CPSG1 CPSO11 CPSA2
5 1 7 1 1
5 2 5 1 1
5 99 7 1 1
1 2 7 1 1
5 1 3 1 1
1 2 5 1 1

Codebooks

r$> table(df_raw$CPSG1)

   1    3    5    7    8    9 
 169  743  957 1843   57    6 

Étude électorale canadienne de 1993

Clean

ses_gender ses_born_canada vote_intention vote_probability_to_vote issue_gst_opposed
female 1 cpc 1 1.00
female 1 lpc 1 0.67
female 1 NA 1 1.00
male 1 lpc 1 1.00
female 1 cpc 1 0.33
male 1 lpc 1 0.67

Décortiquer le processus de cleaning

library(dplyr)

df_raw <- read.csv("data/ces/1993/raw/CES-E-1993_F1_subset.csv")

df_clean <- data.frame(id = 1:nrow(df_raw))

#------------------------------------------------------------------------------#
# VARIABLE : CPSG1 - GST opposition
# Question : In 1991 the Federal government adopted a new tax on goods and 
#           services, the GST. All things considered, are you VERY MUCH IN FAVOUR, 
#           SOMEWHAT IN FAVOUR, SOMEWHAT OPPOSED, or VERY MUCH OPPOSED to the GST?
# Codage   : 1 = Opposed, 0 = Favour
#------------------------------------------------------------------------------#
table(df_raw$CPSG1, useNA = "ifany")
df_clean$issue_gst_opposed <- NA
df_clean$issue_gst_opposed[df_raw$CPSG1 == 1] <- 0
df_clean$issue_gst_opposed[df_raw$CPSG1 == 3] <- 0.33
df_clean$issue_gst_opposed[df_raw$CPSG1 == 5] <- 0.67
df_clean$issue_gst_opposed[df_raw$CPSG1 == 7] <- 1
table(df_clean$issue_gst_opposed, useNA = "ifany")

write.csv(df_clean, "data/ces/1993/ces93_clean.csv", row.names = FALSE)

Anatomie d’une ligne de code

df_clean$issue_gst_opposed[df_raw$CPSG1 == 1] <- 0

1️⃣ Nouvelle variable : df_clean$issue_gst_opposed

  • Dataframe de destination : df_clean
  • Nom de la variable créée : issue_gst_opposed

2️⃣ Condition de sélection : [df_raw$CPSG1 == 1]

  • Dataframe source : df_raw
  • Variable source : CPSG1
  • Condition : égal à 1

3️⃣ Attribution : <- 0

  • Opérateur d’assignation : <-
  • Nouvelle valeur : 0

En langage naturel

“Pour toutes les observations où CPSG1 égale 1 dans df_raw,
assigner la valeur 0 à la variable issue_gst_opposed dans df_clean

Avantage de nettoyer tout de la même façon

Avantage de nettoyer tout de la même façon

Questions?

Prochain cours