Formation en R
Votre directeur entre :
“Nous devons identifier les 5 principales entreprises avec les plus grands écarts de TVA dans le secteur du commerce de détail. Divisez-les par taille d’entreprise—petite, moyenne et grande—sur la base du revenu imposable annuel des données d’impôt sur les sociétés. Pouvez-vous avoir cela prêt pour demain ?”
🤔 Vous pensez :
Ce module vous enseigne exactement comment résoudre ce problème.
Quelles données avons-nous ?
Que devons-nous faire ?
Question clé : Comment passer de jeux de données désordonnés et séparés à un tableau propre d’analyse ?
Réponse : En maîtrisant la transformation et les jointures !
Que sont les Données Tidy ?
Les données tidy organisent les données administratives fiscales dans un format cohérent et prêt pour l’analyse :

| ID Contribuable | Type Impôt | 2021 T1 | 2021 T2 | 2021 T3 | 2021 T4 |
|---|---|---|---|---|---|
| 101 | Impôt Revenu | 500 | 600 | 450 | 700 |
| 102 | TVA | 300 | 400 | 350 | 500 |
Quel est le problème avec cet ensemble de données ?
Problèmes :
Quelles sont les variables ?
ID Contribuable, Type Impôt, Trimestre, Montant Paiement.
Qu’est-ce qui constitue une seule observation dans cet ensemble de données ?
Une observation est un paiement d’impôt spécifique pour un contribuable pendant un trimestre particulier.
Comment transformeriez-vous l’ensemble de données pour respecter les trois caractéristiques tidy ?
| ID Contribuable | Type Impôt | Trimestre | Montant Paiement |
|---|---|---|---|
| 101 | Impôt Revenu | 2021 T1 | 500 |
| 101 | Impôt Revenu | 2021 T2 | 600 |
| 101 | Impôt Revenu | 2021 T3 | 450 |
| 101 | Impôt Revenu | 2021 T4 | 700 |
| 102 | TVA | 2021 T1 | 300 |
| 102 | TVA | 2021 T2 | 400 |
| 102 | TVA | 2021 T3 | 350 |
| 102 | TVA | 2021 T4 | 500 |
Les données tidy facilitent l’analyse :
L’Objectif
La majeure partie de notre travail implique de transformer des données désordonnées en données tidy, puis de les analyser.
Unité d’Observation
Ce que représente chaque ligne dans vos données brutes
Unité d’Analyse
Ce dont vous avez besoin pour votre analyse
Exemple : Vous avez des observations entreprise-trimestre mais besoin d’analyse entreprise-année
Chaque période de temps est une colonne
| firm_id | Q1_vat | Q2_vat | Q3_vat | Q4_vat |
|---|---|---|---|---|
| FIRM_01 | 1000 | 1200 | 1100 | 1300 |
| FIRM_02 | 800 | 900 | 950 | 1000 |
Bon pour :
Chaque observation est une ligne
| firm_id | quarter | vat_amount |
|---|---|---|
| FIRM_01 | Q1 | 1000 |
| FIRM_01 | Q2 | 1200 |
| FIRM_01 | Q3 | 1100 |
| FIRM_01 | Q4 | 1300 |
| FIRM_02 | Q1 | 800 |
Bon pour :
Transformation la plus courante dans l’analyse de données fiscales
Données de TVA trimestrielles larges :
| firm_id | vat_q1 | vat_q2 | vat_q3 | vat_q4 |
|---|---|---|---|---|
| FIRM_01 | 5000 | 5200 | 4800 | 5500 |
↓ Transformer en long ↓
| firm_id | quarter | vat_amount |
|---|---|---|
| FIRM_01 | Q1 | 5000 |
| FIRM_01 | Q2 | 5200 |
| FIRM_01 | Q3 | 4800 |
| FIRM_01 | Q4 | 5500 |
Pourquoi ? Requis pour :
pivot_longer()Décomposition :
cols : Colonnes à transformer (celles avec des mesures répétées)names_to : Nom de la nouvelle colonne qui contiendra les noms d’anciennes colonnesvalues_to : Nom de la nouvelle colonne qui contiendra les valeursPensez-y comme “déballer”
Vous prenez des colonnes (Q1, Q2, Q3, Q4) et les déballez en lignes, stockant le nom de colonne (Q1) et sa valeur (5000) séparément.
# Montrer d'abord le format large
head(vat_wide, 3)
# Transformer en format long
vat_long <- vat_wide %>%
pivot_longer(
cols = c(vat_q1, vat_q2, vat_q3, vat_q4),
names_to = "quarter",
values_to = "vat_amount"
)
# Montrer le résultat
head(vat_long, 6)
# Vérifier les dimensions
cat("Format large:", nrow(vat_wide), "lignes\n")
cat("Format long:", nrow(vat_long), "lignes (4x plus !)\n")Tâche : Transformer les données d’impôt sur les sociétés larges fournies en format long
exercise_04_01_template.Rdata/Intermediate/cit_wide.csvpivot_longer() pour transformer les colonnes d’année en format long10:00 Moins courant, mais important pour des tâches spécifiques
Données en format long :
| firm_id | year | tax_type | amount |
|---|---|---|---|
| FIRM_01 | 2023 | VAT | 20000 |
| FIRM_01 | 2023 | CIT | 15000 |
| FIRM_02 | 2023 | VAT | 18000 |
| FIRM_02 | 2023 | CIT | 12000 |
↓ Transformer en large ↓
| firm_id | year | VAT | CIT |
|---|---|---|---|
| FIRM_01 | 2023 | 20000 | 15000 |
| FIRM_02 | 2023 | 18000 | 12000 |
Pourquoi ? Utile pour :
pivot_wider()Décomposition :
id_cols : Colonnes qui identifient uniquement chaque ligne dans le résultatnames_from : Quelle colonne contient les valeurs qui deviendront de nouveaux noms de colonnevalues_from : Quelle colonne contient les valeurs pour remplir les nouvelles colonnesAttention aux doublons !
Si vous avez plusieurs lignes avec la même combinaison de id_cols et names_from, pivot_wider() créera une colonne de liste. Vérifiez toujours vos données d’abord !
# Créer des données d'exemple avec TVA et CIT
tax_long <- tibble(
firm_id = rep(c("FIRM_01", "FIRM_02"), each = 2),
year = rep(2023, 4),
tax_type = rep(c("VAT", "CIT"), 2),
amount = c(20000, 15000, 18000, 12000)
)
# Montrer le format long
print(tax_long)
# Transformer en format large
tax_wide <- tax_long %>%
pivot_wider(
id_cols = c(firm_id, year),
names_from = tax_type,
values_from = amount
)
# Montrer le résultat
print(tax_wide)
# Maintenant nous pouvons facilement calculer le ratio TVA/CIT
tax_wide <- tax_wide %>%
mutate(vat_cit_ratio = VAT / CIT)
print(tax_wide)Dans l’administration fiscale, les données vivent dans des systèmes séparés :
Système TVA
Système Impôt Sociétés
Registre Entreprises
L’Objectif
Combiner ces jeux de données pour analyser le comportement des entreprises à travers plusieurs types d’impôts et caractéristiques.
Trois tables séparées liées par firm_id :
Table 1 : TVA (panel_vat)
| firm_id | quarter | vat_amount |
|---|---|---|
| FIRM_01 | Q1 | 5000 |
| FIRM_01 | Q2 | 5200 |
Table 2 : Impôt Sociétés (panel_cit)
| firm_id | year | cit_amount |
|---|---|---|
| FIRM_01 | 2023 | 15000 |
| FIRM_02 | 2023 | 12000 |
Table 3 : Entreprises (dt_firms)
| firm_id | industry | size |
|---|---|---|
| FIRM_01 | Retail | Medium |
| FIRM_02 | Services | Small |
La clé : firm_id apparaît dans les trois tables, nous permettant de les lier.
Clé de jointure = La ou les colonnes utilisées pour associer les lignes entre tables
Table A (TVA)
| firm_id | vat |
|---|---|
| FIRM_01 | 5000 |
| FIRM_02 | 4500 |
| FIRM_03 | 6000 |
Table B (Entreprises)
| firm_id | industry |
|---|---|
| FIRM_01 | Retail |
| FIRM_02 | Services |
+
= ?
R associe les lignes où firm_id est identique
Qu’arrive-t-il à FIRM_03 ?
Cela dépend du type de jointure !
left_join() : Le Cheval de BatailleJointure la plus utilisée (80% des cas réels)
Garde toutes les lignes de la table gauche
Table A (gauche)
| firm_id | vat |
|---|---|
| FIRM_01 | 5000 |
| FIRM_02 | 4500 |
| FIRM_03 | 6000 |
Table B (droite)
| firm_id | industry |
|---|---|
| FIRM_01 | Retail |
| FIRM_02 | Services |
Résultat : left_join(A, B)
| firm_id | vat | industry |
|---|---|---|
| FIRM_01 | 5000 | Retail |
| FIRM_02 | 4500 | Services |
| FIRM_03 | 6000 | NA |
Note : FIRM_03 est conservée, mais industry est NA
Pourquoi c’est la valeur par défaut
Préserve votre jeu de données principal (la table gauche). Parfait pour enrichir des données existantes avec des attributs supplémentaires.
left_join() en Action# Créer des données d'exemple
vat_data <- tibble(
firm_id = c("FIRM_01", "FIRM_02", "FIRM_03"),
quarter = c("Q1", "Q1", "Q1"),
vat_amount = c(5000, 4500, 6000)
)
firm_data <- tibble(
firm_id = c("FIRM_01", "FIRM_02"),
industry = c("Retail", "Services"),
size = c("Medium", "Small")
)
# Montrer les tables originales
cat("Données de TVA :\n")
print(vat_data)
cat("\nDonnées d'entreprise :\n")
print(firm_data)
# Effectuer left join
vat_enriched <- left_join(vat_data, firm_data, by = "firm_id")
cat("\nAprès left_join :\n")
print(vat_enriched)
# Vérifier le comptage des lignes
cat("\nVérification du comptage des lignes :\n")
cat("Données de TVA :", nrow(vat_data), "lignes\n")
cat("Résultat :", nrow(vat_enriched), "lignes (identique !)\n")Tâche : Joindre les données CIT avec les caractéristiques d’entreprise
exercise_04_02_template.Rpanel_cit.csv et dt_firms.csvleft_join() pour ajouter les caractéristiques d’entreprise aux données CIT10:00 inner_join() : Seulement les CorrespondancesGarde seulement les lignes qui existent dans les DEUX tables
Table A
| firm_id | vat |
|---|---|
| FIRM_01 | 5000 |
| FIRM_02 | 4500 |
| FIRM_03 | 6000 |
Table B
| firm_id | industry |
|---|---|
| FIRM_01 | Retail |
| FIRM_02 | Services |
Résultat : inner_join(A, B)
| firm_id | vat | industry |
|---|---|---|
| FIRM_01 | 5000 | Retail |
| FIRM_02 | 4500 | Services |
Note : FIRM_03 supprimée (pas de correspondance dans Table B)
full_join() : ToutGarde TOUTES les lignes des DEUX tables
Table A
| firm_id | vat |
|---|---|
| FIRM_01 | 5000 |
| FIRM_02 | 4500 |
Table B
| firm_id | cit |
|---|---|
| FIRM_02 | 12000 |
| FIRM_03 | 15000 |
Résultat : full_join(A, B)
| firm_id | vat | cit |
|---|---|---|
| FIRM_01 | 5000 | NA |
| FIRM_02 | 4500 | 12000 |
| FIRM_03 | NA | 15000 |
Note : Toutes les entreprises conservées, avec NAs où il n’y a pas de correspondance
anti_join() : L’Outil de DétectiveRetourne les lignes de la table gauche qui N’ont PAS de correspondance dans la table droite
Table A (Déclarants TVA)
| firm_id | vat |
|---|---|
| FIRM_01 | 5000 |
| FIRM_02 | 4500 |
| FIRM_03 | 6000 |
Table B (Déclarants CIT)
| firm_id | cit |
|---|---|
| FIRM_01 | 15000 |
| FIRM_02 | 12000 |
Résultat : anti_join(A, B)
| firm_id | vat |
|---|---|
| FIRM_03 | 6000 |
Note : Seulement FIRM_03 retournée (a déposé TVA mais pas CIT)
Critique pour les diagnostics !
Utilisez anti_join() pour trouver :
Mêmes Données d’Exemple, Différents Types de Jointure :
Table A (TVA)
| firm_id | vat |
|---|---|
| FIRM_01 | 5000 |
| FIRM_02 | 4500 |
| FIRM_03 | 6000 |
Table B (CIT)
| firm_id | cit |
|---|---|
| FIRM_01 | 15000 |
| FIRM_02 | 12000 |
| FIRM_04 | 18000 |
left_join(A, B)
| firm_id | vat | cit |
|---|---|---|
| FIRM_01 | 5000 | 15000 |
| FIRM_02 | 4500 | 12000 |
| FIRM_03 | 6000 | NA |
Lignes : 3 (toutes de A)
inner_join(A, B)
| firm_id | vat | cit |
|---|---|---|
| FIRM_01 | 5000 | 15000 |
| FIRM_02 | 4500 | 12000 |
Lignes : 2 (seulement correspondances)
full_join(A, B)
| firm_id | vat | cit |
|---|---|---|
| FIRM_01 | 5000 | 15000 |
| FIRM_02 | 4500 | 12000 |
| FIRM_03 | 6000 | NA |
| FIRM_04 | NA | 18000 |
Lignes : 4 (toutes des deux)
anti_join(A, B)
| firm_id | vat |
|---|---|
| FIRM_03 | 6000 |
Lignes : 1 (dans A, pas dans B)
Parfois une clé ne suffit pas
Problème : La même entreprise apparaît dans plusieurs années
Données TVA (panel)
| firm_id | year | quarter | vat |
|---|---|---|---|
| FIRM_01 | 2022 | Q1 | 5000 |
| FIRM_01 | 2023 | Q1 | 5200 |
Caractéristiques entreprise (aussi panel)
| firm_id | year | industry |
|---|---|---|
| FIRM_01 | 2022 | Retail |
| FIRM_01 | 2023 | Retail |
Si vous joignez seulement par firm_id, vous obtiendrez des correspondances en double !
Les données réelles ont souvent une nomenclature incohérente
Table A utilise ‘id’
| id | vat |
|---|---|
| FIRM_01 | 5000 |
Table B utilise ‘firm_id’
| firm_id | industry |
|---|---|
| FIRM_01 | Retail |
⚠️ Histoire d’Horreur
Vous exécutez une jointure. Tout semble bien. Vous envoyez les résultats à votre directeur.
😱 Le lendemain :
“Ces chiffres semblent incorrects. Pourquoi avons-nous 50 000 observations entreprise-année quand nous n’avons que 5 000 entreprises ?”
Que s’est-il passé ? Clés dupliquées dans la table droite ont causé une explosion de lignes.
Leçon : Faites confiance, mais vérifiez. Validez TOUJOURS vos jointures.
by = "firm_id" mais une table utilise "firm_ID"AVANT de joindre :
# 1. Vérifier les clés dupliquées dans les deux tables
panel_vat %>%
count(firm_id, year) %>%
filter(n > 1)
dt_firms %>%
count(firm_id, year) %>%
filter(n > 1)
# 2. Vérifier que les colonnes clés existent et ont le même type
str(panel_vat$firm_id)
str(dt_firms$firm_id)
# 3. Vérifier les valeurs manquantes dans les colonnes clés
sum(is.na(panel_vat$firm_id))
sum(is.na(dt_firms$firm_id))APRÈS avoir joint :
# 4. Vérifier le comptage des lignes - a-t-il un sens ?
cat("Avant de joindre :", nrow(panel_vat), "lignes\n")
cat("Après avoir joint :", nrow(merged_data), "lignes\n")
# Pour left_join, devrait correspondre à la table gauche (sauf si droite a des doublons)
# Pour inner_join, devrait être inférieur ou égal à l'une ou l'autre table
# 5. Trouver les non-correspondances en utilisant anti_join
unmatched <- anti_join(panel_vat, dt_firms, by = c("firm_id", "year"))
cat("Lignes sans correspondance :", nrow(unmatched), "\n")
# 6. Vérifier les NAs inattendus dans les colonnes jointes
merged_data %>%
summarize(
na_industry = sum(is.na(industry)),
na_size = sum(is.na(size))
)
# 7. Vérification ponctuelle de quelques lignes
merged_data %>%
filter(firm_id == "FIRM_001") %>%
select(firm_id, year, vat_amount, industry, size)Tâche : La jointure fournie produit le mauvais nombre de lignes. Trouvez et réparez le problème.
exercise_04_03_template.R15:00 Scénario Réaliste : Calculer l’Écart de TVA par Taille d’Entreprise
Votre directeur veut identifier les 5 principales entreprises avec les plus grands écarts de TVA dans le commerce de détail, segmentées par taille.
Le Pipeline :
# Étape 1 : Transformer TVA de large à long (si nécessaire)
# Dans cet exemple, panel_vat est déjà en format long, donc nous agrégeons directement
# Étape 2 : Agréger TVA au niveau entreprise-année
vat_annual <- panel_vat %>%
mutate(year = lubridate::year(declaration_date)) %>%
group_by(firm_id, year) %>%
summarize(
actual_vat = sum(vat_outputs - vat_inputs, na.rm = TRUE),
vat_inputs = sum(vat_inputs, na.rm = TRUE),
vat_outputs = sum(vat_outputs, na.rm = TRUE),
quarters_filed = n(),
.groups = "drop"
)
# Étape 3 : Agréger CIT pour obtenir le revenu imposable
cit_annual <- panel_cit %>%
mutate(year = lubridate::year(declaration_date)) %>%
group_by(firm_id, year) %>%
summarize(
taxable_income = sum(taxable_income, na.rm = TRUE),
.groups = "drop"
)
# Étape 4 : Joindre TVA avec données CIT
vat_cit <- left_join(
vat_annual,
cit_annual,
by = c("firm_id", "year")
)
# Étape 5 : Joindre avec caractéristiques d'entreprise
vat_with_firms <- left_join(
vat_cit,
dt_firms,
by = c("firm_id", "year")
)
# Étape 6 : Créer des catégories de taille basées sur le revenu imposable
vat_with_firms <- vat_with_firms %>%
mutate(
firm_size = case_when(
taxable_income < 50000 ~ "Petite",
taxable_income >= 50000 & taxable_income < 125000 ~ "Moyenne",
taxable_income >= 125000 ~ "Grande",
is.na(taxable_income) ~ "Inconnu",
TRUE ~ "Inconnu"
)
)
# Étape 7 : Calculer l'écart de TVA (exemple simplifié)
# Écart TVA = TVA Attendue - TVA Réelle
vat_analysis <- vat_with_firms %>%
mutate(
expected_vat = vat_outputs * 0.15, # Supposant un taux de TVA de 15%
vat_gap = expected_vat - actual_vat
)
# Étape 8 : Filtrer sur commerce de détail et trouver le top 5 par taille
top_gaps <- vat_analysis %>%
filter(industry == "Retail", !is.na(vat_gap)) %>%
group_by(firm_size) %>%
slice_max(order_by = vat_gap, n = 5) %>%
ungroup()
# Étape 9 : Valider
cat("Résumé de l'analyse :\n")
cat("Total entreprises analysées :", nrow(vat_analysis), "\n")
cat("Entreprises commerce de détail :", sum(vat_analysis$industry == "Retail", na.rm = TRUE), "\n")
cat("Écarts principaux par taille :\n")
print(top_gaps %>% count(firm_size))
# Sauvegarder les résultats
fwrite(
vat_analysis,
here("data", "final", "vat_gap_analysis.csv")
)
cat("\n✓ Analyse d'écart de TVA complète !\n")Tâche : Identifier les 5 principales entreprises du commerce de détail avec les plus grands écarts de TVA, par catégorie de taille
exercise_04_final_template.Rdata/Final/vat_gap_analysis.csvCette analyse aidera à identifier les risques de conformité par taille d’entreprise !
20:00 pivot_longer() pour la plupart des tâches de transformation
left_join() comme votre jointure par défaut
anti_join() pour trouver les non-correspondances📖 Lectures Supplémentaires
R4DS Chapitre 6 : Données tidy
https://r4ds.hadley.nz/data-tidy
R4DS Chapitre 20 : Jointures
https://r4ds.hadley.nz/joins
Fiche aide-mémoire dplyr : Référence des fonctions de jointure
Fiche aide-mémoire tidyr : Référence des fonctions pivot
🥳 Vous pouvez maintenant :
Vous avez résolu le défi !
Vous savez maintenant comment : - Transformer les données trimestrielles en annuelles - Joindre les caractéristiques d’entreprise - Calculer des métriques comme l’écart de TVA - Segmenter par catégories (taille d’entreprise, industrie) - Classer et identifier les principales performances/risques
La pratique rend parfait
La meilleure façon de maîtriser les jointures est de pratiquer avec des données réelles. N’hésitez pas à expérimenter avec différents types de jointures et voir ce qui se passe !