Préparation des données
Un article de IMSP - Formation continue.
Préparer les données de terrain pour l'analyse est l'une des tâches qui demande le plus de resources et de temps au quotidien. Il existe une grande variété d'outils aptes à cette tâche, et même dans certaines conditions les éditeurs et traitements de texte peuvent s'avérer utiles. Nous décrivons ici quelques procédures simples avec R et STATA. En effet, il est important de réaliser les tâches selon des méthodes documentées et aisément vérifiables: la reproductivité des opérations de gestion de données (transferts, recodage, création de nouvelles variables) contribue à la transparence scientifique et est une exigence de plus en plus présente. Le scripting ou programmation des opérations est souhaitable, un sujet abordé dans le prochain chapitre. Les exemples sont pris d'un cas réel.
Tableau synoptique:
| opération | STATA | R |
|---|---|---|
| remplacer une valeur dans une variable | source('gsr.r') quart <- test001$X.QUARTIER quart <- gsr(quart,"OONAH","FONAH") etc. quart <- as.factor(quart) test001$X.QUARTIER <- quart | |
| renommer les variables | nouveaux.noms <- c("quartier","ddn","sexe","p1ph1", "p1ph2","p1ptemp","p1prate","p1pge", "p1pdenpf","p1pgam") names(test001) <- nouveaux.noms | |
| traiter les données manquantes | thegam <- test001$p1pgam thegam <- as.character(thegam) thegam <- gsr(thegam,"",NA) thegam <- as.factor(thegam) test001$p1pgam <- thegam etc. | |
| convertir des dates | myddn <- test001$ddn myddn <- as.character(myddn) myddn <-strptime(myddn,"%d.%m.%Y") myddn.frame <- as.data.frame(myddn) test001$ddn <- myddn.frame$myddn save(test001,file = "test005.Rdata") | |
| calculer un âge |
Sommaire |
Remplacer une valeur dans une variable
Reprenons le fichier test001: http://www.santepublique.org/fc/data/test001.csv déjà présenté dans analyse préliminaire avec R et STATA.
R
Chargeons le fichier
test001 <- read.csv('test001.csv')
Examen des données
names(test001)
résultat:
[1] "X.QUARTIER" "X.DDN" "X.SEXE" "X.P1PH1" "X.P1PH2" [6] "X.P1PTEMP" "X.P1PRATE" "X.P1PGE" "X.P1PDENPF" "X.P1PGAM"
de quel type sont les variables?
str(test001)
résultat:
data.frame': 754 obs. of 10 variables: $ X.QUARTIER: Factor w/ 12 levels "FONAH","GOUNYAHOUN",..: 9 9 9 9 9 9 9 9 9 9 ... $ X.DDN : Factor w/ 288 levels "01.01.1989","01.01.1990",..: 1 4 1 6 4 7 83 1 18 24 ... $ X.SEXE : Factor w/ 2 levels "F","M": 2 2 1 2 1 1 1 1 1 2 ... $ X.P1PH1 : int 34 30 35 45 36 37 35 35 35 25 ... $ X.P1PH2 : int 37 28 35 48 36 36 42 38 35 29 ... $ X.P1PTEMP : num 37 36.4 37.1 37.2 37 37.2 36.2 37.2 36.6 37.5 ... $ X.P1PRATE : int 0 0 0 0 0 0 0 0 0 0 ... $ X.P1PGE : Factor w/ 3 levels "","N","Y": 2 3 3 2 3 3 3 3 2 3 ... $ X.P1PDENPF: int 0 33400 150 0 250 100 400 100 0 9600 ... $ X.P1PGAM : Factor w/ 3 levels "","N","Y": 2 3 3 2 2 2 2 2 2 2 ...
La variable X.QUARTIER est considérée par R comme un facteur. Examinons-la:
levels(X.QUARTIER) [1] "FONAH" "GOUNYAHOUN" "LIANH" "OONAH" [5] "OONDE" "OOUNYAHOUN" "SEREKENI" "ZIANH" [9] "ZONDE" "ZONDE kodeniko" "ZONDE Kodeniko" "ZONDE koko"
Plusieurs noms sont suspects de doublons:
- GOUNYAHOUN (position 2) et OOUNYAHOUN (position 6)
- OONDE (position 5) et ZONDE (position 9)
- ZONDE kodeniko (position 9) et ZONDE Kodeniko (position 10) (élementaire...)
- FONAH (position 1) et OONAH (position 4)
- LIANH (position 3) et ZIANH (position 8)
examinons les effectifs:
length(X.QUARTIER) [1] 754
examinons le tableau des fréquences:
table(X.QUARTIER)
X.QUARTIER
FONAH GOUNYAHOUN LIANH OONAH OONDE
172 64 2 1 1
OOUNYAHOUN SEREKENI ZIANH ZONDE ZONDE kodeniko
2 89 64 215 35
ZONDE Kodeniko ZONDE koko
30 79
Renseignements et contacts pris, on nous transmet le plan suivant. Le village de Serekeni est situé quant à lui à 7 Km. environ de Zonde.:
Il est décidé d'unifier GOUNYAHOUN et OUNYAHOUN, LIANH et ZIANH, OONAH et FONAH, et finalement ZONDE kodeniko et ZONDE Kodeniko. ZONDE koko constitue un noyau de population réel et bien distinct de Zonde kodeniko.
Modification des données
Nous allons utiliser une fonction très utile, et par la même occasion nous familiariser un peu plus avec le chargement de fonctions dans R. Global search and replace (Schwarz 2006) met en oeuvre la fonction interne replace(). Vous pouvez copier la fonction suivante et la coller dans un éditeur de texte. enregistrez-la dans un fichier que vous nommerez gsr.r
gsr <- function(Source, Search, Replace) {
if (length(Search) != length(Replace)) stop("Search and Replace Must Have Equal Number of Items\n")
Changed <- as.character(Source)
for (i in 1:length(Search))
{
cat("Replacing: ", Search[i], " With: ", Replace[i], "\n")
Changed <- replace(Changed, Changed == Search[i], Replace[i]) }
cat("\n")
Changed
}
ou bien vous pouvez la télécharger et la placer dans votre répertoire de travail: http://www.santepublique.org/fc/data/gsr.r
Charger cette fonction en mémoire est ensuite très simple. Nous utilisons la commande source() qui permet d'exécuter des fichiers de code source:
source('gsr.r')
l'opération est silencieuse, vous pouvez vérifier que la fonction est bien présente:
gsr
vous devriez voir s'afficher le code source de la fonction (vous pouvez consulter ainsi le code source de n'importe quelle fonction dans R).
Nous souhaitons modifier les données de la variable qui contient les noms de quartier. Nous en faisons d'abord une copie de travail:
quart <- test001$X.QUARTIER
vérifions:
> levels(quart) [1] "FONAH" "GOUNYAHOUN" "LIANH" "OONAH" [5] "OONDE" "OOUNYAHOUN" "SEREKENI" "ZIANH" [9] "ZONDE" "ZONDE kodeniko" "ZONDE Kodeniko" "ZONDE koko"
nous alllons remplacer "OONAH" par "FOONAH":
>quart <- gsr(quart,"OONAH","FONAH") Replacing: OONAH With: FONAH
"OONDE" par ZONDE:
> quart <- gsr(quart,"OONDE","ZONDE") Replacing: OONDE With: ZONDE
exercice: à l'aide de la fonction gsr() remplacez "LIANH" par "ZIANH", "OOUNYAHOUN" par "GOUNYAHOUN" et "ZONDE Kodeniko" par "ZONDE kodeniko"
Il est temps de vérifier notre travail:
> levels(quart) NULL
Qu'est-il arrivé?
> str(quart) chr [1:754] "ZONDE" "ZONDE" "ZONDE" "ZONDE" "ZONDE" "ZONDE" ...
Nos facteurs initiaux ont été transformés en chaînes de caractères (à la ligne 4 de la fonction). Nous le changeons à nouveau en facteurs:
quart <- as.factor(quart)
tout rentre dans l'ordre:
> str(quart) Factor w/ 7 levels "FONAH","GOUNYAHOUN",..: 5 5 5 5 5 5 5 5 5 5 ... >levels(quart) [1] "FONAH" "GOUNYAHOUN" "SEREKENI" "ZIANH" [5] "ZONDE" "ZONDE kodeniko" "ZONDE koko"
Les localisations correspondent désormais à la carte.
nous pouvons produire une table des fréquences:
> table(quart)
quart
FONAH GOUNYAHOUN SEREKENI ZIANH ZONDE
173 66 89 66 216
ZONDE kodeniko ZONDE koko
65 79
nous avons maintenant sept quartiers avec entre 65 et 216 observations. Cela paraît satisfaisant, nous consignons les valeurs modifiées dans la table d'origine:
test001$X.QUARTIER <- quart
et nous ne manquons pas d'enregistrer notre fichier modifié avec un nouveau nom:
save(test001,file = "test002.Rdata")
STATA
en construction
Renommer les variables
R
Lors de l'importation des données depuis le fichier test001.csv (voir Analyse_préliminaire_avec_R_et_STATA#R_2 nous remarquons que le logiciel a modifié les noms des variables: passage en majuscules et ajout d'un X. devant l'ancien nom.
>names(test001) [1] "X.QUARTIER" "X.DDN" "X.SEXE" "X.P1PH1" "X.P1PH2" [6] "X.P1PTEMP" "X.P1PRATE" "X.P1PGE" "X.P1PDENPF" "X.P1PGAM"
Imaginons que nous souhaitons revenir aux noms précédents. Nous allons créer un vecteur nouveaux.noms à l'aide de la fonction c() (comme "chaînage"):
nouveaux.noms <- c("quartier","ddn","sexe","p1ph1","p1ph2","p1ptemp","p1prate","p1pge","p1pdenpf","p1pgam")
puis nous assignons ces valeurs aux noms des variables:
names(test001) <- nouveaux.noms
vérifions:
>names(test001) [1] "quartier" "ddn" "sexe" "p1ph1" "p1ph2" "p1ptemp" [7] "p1prate" "p1pge" "p1pdenpf" "p1pgam"
N'oublions pas d'enregistrer notre fichier de données modifié:
save(test001,file = "test003.Rdata")
STATA
En construction
Traiter les données manquantes
Les données manquantes, qu'elles soient explicites ou masquées, sont un problème sérieux dans toute analyse statistique, notamment lorsque des tentatives sont faites pour leur attribuer une signification (imputation). "La source ultime de la plupart des données manquantes masquées est probablement l'absence de représentation standard des données manquantes" [1]. Nous devons être particulièrement attentifs lors de l'échange de donnéesentre différents logiciels, systèmes et usagers.
R
Dans ce logiciel, les données manquantes sont représentées par la valeur réservée NA. Il peut arriver, lors d'une importation de données, que les données manquantes ne soient pas correctement reconnues. Par exemple, notre objet test001 a été importé d'un fichier test001.csv
Nous rerpartons du fichier que nous avons enregistré dans l'exercice précédent:
load("test003.Rdata")
source('gsr.r')
Nous allons examiner un exemple: la variable dicothomique "X.P1PGAM" (présence ou absence de gamètocytes) dans l'objet test001.
str(test001$p1pgam) Factor w/ 3 levels "","N","Y": 2 3 3 2 2 2 2 2 2 2 ...
Nous constatons que les données manquantes (champ vide) ont été interprétées comme un niveau, la variable apparait comme un facteur à trois niveaux. Ce n'est pas souhaitable. Nous copions notre variable dans un objet temporaire.
thegam <- test001$p1pgam
convertissons nos facteurs en chaînes de caractères:
thegam <- as.character(thegam)
remplaçons toutes les valeurs "" par des NA.
thegam <- gsr(thegam,"",NA) Replacing: With: NA
vérifions:
> thegam [1] "N" "Y" "Y" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "Y" "N" [19] "N" "N" "N" "Y" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "Y" "N" "N" [37] "N" "N" "N" "N" "N" "N" "Y" "Y" "N" "N" "N" "N" "N" "N" "N" "N" "N" "Y" [55] "N" NA NA "N" "N" "N" "Y" "N" "Y" "N" "N" "N" "N" NA "N" "N" "N" "N" etc.
nous convertissons à nouveau en facteur la variable et nous vérifions:
thegam <- as.factor(thegam) str(thegam) Factor w/ 2 levels "N","Y": 1 2 2 1 1 1 1 1 1 1 ...
C'est bien ce qu'on voulait. Nous attribuons ces valeurs à la variable thegam de la table:
test001$p1pgam <- thegam
exercice: examiner d'autres variables de la table test001. Quelle autre variable présente exactement le même cas de figure? appliquez le traitement à cette variable.
Enregistrez le résultat de votre travail, vous l'utiliserez dans d'autres exercices:
save(test001,file = "test004.Rdata")
STATA
en construction
Traiter des dates
Il n'y a pour l'instant pas de consensus sur un format universel pour traduire les variables temporelles, il faut donc s'attendre à trouver les données temporelles dans des formats assez divers. La première mesure consiste à déterminer le format d'origine. Il faut ensuite le traduire dans la représentation interne des données temporelles du logiciel de statistiques utilisé. Dans un troisième temps on abordera des opérations telles que le calcul des âges.
R
Convertir des dates
Il faut s'attentre à voir arriver des dates dans les formats les plus divers. La première mesure consiste à déterminer lequel.
Le tableau test001 comporte une variable chronologique: ddn, qui représente la date de naissance des enfants examinés. Observons-la:
> str(test001$ddn) Factor w/ 288 levels "01.01.1989","01.01.1990",..: 1 4 1 6 4 7 83 1 18 24 ... > head(test001$ddn) [1] 01.01.1989 01.01.1992 01.01.1989 01.01.1995 01.01.1992 01.01.1996 288 Levels: 01.01.1989 01.01.1990 01.01.1991 01.01.1992 ... 30.03.1996
Les dates de naissance ont été interprétées comme des facteurs, ce qui est faux. Nous établissons que le format d'origine est jour(deux décimales) . mois (deux décimales) . année (complète)
nous créons un objet de travail avec les dates à convertir:
myddn <- test001$ddn
nous convertissons nos facteurs en chaînes de caractères:
myddn <- as.character(myddn)
R est doté d'une fonction très puissante pour lire des dates: strptime(). Consultons l'aide:
Help(strptime)
Nous déterminons que la représentation correspondant à notre format de date est %d.%m%.Y Nous pouvons donc tenter la conversion:
myddn <- strptime(myddn,"%d.%m.%Y")
vérifions:
>str(myddn)
'POSIXlt', format: chr [1:754] "1989-01-01" "1992-01-01" "1989-01-01" "1995-01-01" ...
> summary(myddn)
Min. 1st Qu.
"1988-01-15 00:00:00 CET" "1992-10-15 00:00:00 CET"
Median Mean
"1995-04-19 00:00:00 CEST" "1995-02-23 02:33:06 CET"
3rd Qu. Max.
"1997-11-15 00:00:00 CET" "2001-03-15 00:00:00 CET"
POSIXlt étant la représentation interne de temps de R. C'est ce que l'on voulait.
Avant de pouvoir assigner le contenu de notre nouvelle variable nous devons la transformer en table (pour contourner un affreux bogue):
myddn.frame <- as.data.frame(myddn)
vérifions:
> head(myddn.frame)
myddn
1 1989-01-01
2 1992-01-01
3 1989-01-01
4 1995-01-01
5 1992-01-01
6 1996-01-01
...
nous pouvons maintenant assigner nos dates à la table d'origine:
test001$ddn <- myddn.frame$myddn
et nous enregistrons notre table:
save(test001,file = "test005.Rdata")
Calculer un âge
L'âge nous sera plus utile que la date de naissance. Nous allons calculer le calculer à la date du premier passage, le 3 mai 2001. Commençons par attribuer cette date à un objet:
passage1 <- "03.05.2001"
nous le convertissons en date POSIX à l'aide de la fonction strftime():
passage1 <- strptime(passage1,"%d.%m.%Y")
et nous vérifions:
> str(passage1) 'POSIXlt', format: chr "2001-05-03"
maintenant nous pouvons calculer l'âge comme la différence entre la date du passage et la date de naissance. Nous pourrions soustraire directement test001$ddn de passage1, mais nous aimerions avoir le contrôle sur toutes les options: nous utilisons donc la fonction difftime()
p1age <- difftime(passage1,test001$ddn,tz="",units="days")
l'objet obtenu est du type "difftime", et l'unité le jour:
>str(p1age) Class 'difftime' atomic [1:754] 4505 3410 4505 2314 3410 ... ..- attr(*, "tzone")= chr "" ..- attr(*, "units")= chr "days"
A première vue, nous ne trouvons pas de fonctions qui permettent de traduire la durée des objets "difftime" en années+mois. Nous nous contenterons d'une approximation années+décimales. Attention toutefois avec des séries où l'âge est déterminé de façon plus précise que dans la présente! Il importe de s'interroger si une plus grande précision de calcul que annéés = jours / 365 a un sens ou non dans l'analyse.
nous convertissons l'objet temporel en objet numérique:
p1age <- as.numeric(p1age)
puis nous le convertissons en années+décimales:
p1age <- p1age / 365.25
vérifions:
> str(p1age) num [1:754] 12.34 9.34 12.34 6.34 9.34 ...
Histogrammes d'âges
nous pouvons désormais étudier le profil d'âges de l'échantillon d'enfants étudié:
>summary(p1age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.1341 3.4660 6.0440 6.1940 8.5530 13.3100
hist(p1age)
Nous avons d'autres possibilités pour l'analyse graphique. Installons l'extension Hmisc du professeur Frank E. Harrell (Vanderbilt University)
install.packages('Hmisc') # une seule fois par installation
library('Hmisc') # lors de la session
Nous allons utiliser la fonction histbackback(), illustrée dans la Gallerie de graphiques avec R, pour étudier la répartition par sexe et tranches d'âge de notre population. Il nous faut pour cela dichotomiser l'effectif par sexes à l'aide de la fonction split:
> agesex <- split(p1age,test001$sexe) > str(agesex) List of 2 $ F: num [1:384] 12.34 9.34 5.34 2.31 12.34 ... $ M: num [1:370] 12.34 9.34 6.34 1.50 2.31 ...
Nous pouvons générer maintenant un histogramme double et le colorier:
out <- histbackback(agesex, brks=c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)) barplot(out$right, col="skyblue", horiz=TRUE, space=0, add=TRUE, axes=FALSE) barplot(-out$left, col="hotpink" , horiz=TRUE, space=0, add=TRUE, axes=FALSE)
On découvre d'intéressantes possibles différences dans les effectifs des classes d'âge selon le sexe. Nous reviendrons sur ce point.
Polygones et courbes de densité par âge
La construction de polygones de fréquences est une autre technique fréquemment utilisée pour comparer les distributions de fréquences. On obtient le polygone de fréquences en joignant le milieu des sommets des barres de l'histogramme des âges. Nous adaptons ici une technique publiée dans R graph gallery. La table agesex a été générée dans l'exercice précédent.
hf <- hist(agesex$F,breaks=c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14),prob=TRUE,plot=FALSE) hm <- hist(agesex$M,breaks=c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14),prob=TRUE,plot=FALSE)
exercice: inspectez les objets hm et hf ainsi générés. Que contiennent-ils?
Nous préparons les informations pour le tracé des polygones:
dbreaksm <-hm$mids[2] - hm$mids[1] dbreaksf <-hf$mids[2] - hf$mids[1] xxm <- c(hm$mids[1]-dbreaksm,hm$mids,tail(hm$mids,1)+dbreaksm) xxf <- c(hf$mids[1]-dbreaksf,hf$mids,tail(hf$mids,1)+dbreaksf) yym <- c(0,hm$density,0) yyf <- c(0,hf$density,0)
Puis nous dessinons un histogramme (de la même couleur que le fond) et les deux polygones de fréquences:
hist(agesex$M,prob=TRUE,xlab="Age",main="Distribution par âges: F (rose) et M (bleu)",border="white",xlim=c(0,14)) lines(xxf,yyf,lwd=2,col="hotpink") lines(xxm,yym,lwd=2,col="skyblue")
Nous pouvons tout aussi bien utiliser la fonction de calcul de densité, déjà abordée dans analyse graphique avec R et STATA#R_2, avec un abord plus simple:
hist(agesex$M,prob=TRUE,xlab="Age",main="Distribution par âges: F (rose) et M (bleu)",border="white",xlim=c(0,14)) lines(density(agesex$M), col='skyblue', lwd=3) lines(density(agesex$F), col='hotpink', lwd=3)
Ajouter l'âge à la table comme une nouvelle colonne
Nous allons créer une nouvelle table test002 qui incorpore la colonne p1age calculée dans les précédents exercices. Nous utiliserons pour cela la fonction data.frame() en passant comme arguments les noms des colonnes à créer dans l'ordre souhaité.
test002 <- data.frame(test001$quartier,test001$ddn,test001$sexe,p1age,test001$p1ph1,
test001$p1ph2,test001$p1ptemp,test001$p1pge,test001$p1pdenpf,test001$p1pgam) ;
vérifions:
> head(test002) ; test001.quartier test001.ddn test001.sexe p1age test001.p1ph1 1 ZONDE 1989-01-01 M 12.342352 34 2 ZONDE 1992-01-01 M 9.342352 30 3 ZONDE 1989-01-01 F 12.342352 35 4 ZONDE 1995-01-01 M 6.339612 45 5 ZONDE 1992-01-01 F 9.342352 36 6 ZONDE 1996-01-01 F 5.339612 37 test001.p1ph2 test001.p1ptemp test001.p1pge test001.p1pdenpf test001.p1pgam 1 37 37.0 N 0 N 2 28 36.4 Y 33400 Y 3 35 37.1 Y 150 Y 4 48 37.2 N 0 N 5 36 37.0 Y 250 N 6 36 37.2 Y 100 N
On remarquera que la reconstitution d'une table par cette méthode incorpore la référence à la table d'origine dans les noms des variables. Nous souhaitons conserver nos anciens noms de variables:
names(test002) <- c("quartier","ddn","sexe","p1age","p1ph1","p1ph2","p1ptemp",
"p1pge","p1pdenpf","p1pgam") ;
Il est temps d'enregistrer le travail.
save(test002,file = "test002.Rdata") ;
STATA
en construction
Lectures recommandées
Pour continuer
|
|





