Posts Tagged ‘compteur’

h1

Combien de contrats ai-je au total ? (2/5) RETAIN

novembre 3, 2008

Après un premier article faisant appel à la procédure SQL pour identifier le nombre total de contrats d’un type donné à partir d’un data set SAS, voici comment un compteur dans une étape data peut résoudre le problème.

1. Renommer des variables

  • Avoir les mêmes noms en entrée et en sortie : Dans le cas présent, il faut définir de nouvelles variables ayant un nom différent de ceux existant. Pour avoir quand même au final le même nom qu’au départ, j’ai choisi de renommer mes variables d’origines rendant leur ancien nom de nouveau disponible.
  • Quel nom pour les variables ? Ces variables portent un nom commençant par un tiret bas (underscore). Cela permet d’avoir un nom toujours parlant et de les supprimer rapidement avec _: (supprimer toutes les variables commençant par un tiret bas).
data solution2 (drop=_: );
   set contrats (rename=(tel=_tel
                         habitat=_habitat);
   *...;
run;

D’autres variables sont supprimées :

  • La variable CLIENT disparaît dès de départ car il ne sert pas dans notre cas.
  • La variable NB_CNTR disparaît seulement à la fin car elle va servir pour faire nos calculs.
data solution2 (drop=_: nb_contr);
   set contrats (drop=client
                 rename=(tel=_tel
                         habitat=_habitat);
   *...;
run;

2. Créer un compteur

Une fois les variables d’origine renommées, les variables TEL et HABITAT sont initialisées à 0 avec un RETAIN. Cela veut dire que pour toutes les observations, ces variables ont dans un premier temps la valeur 0. SAS lit les observations les une après les autres. A chaque fois qu’une valeur de TEL est changée toutes celles qui suivent le sont aussi. C’est le principe du RETAIN.

data solution2 ;*(...);
   set contrats ;*(...);
   retain tel 0
          habitat 0;
   tel     = tel     + nb_cntr * _tel;
   habitat = habitat+  nb_cntr * _habitat;
run;

SAS initialise les variable TEL et HABITAT : Tout d’abord TEL=0 et HABITAT=0.

nb_cntrt _tel _habitat tel habitat
    5      1      0     0     0
    1      1      0     0     0
    2      0      1     0     0
    1      1      0     0     0
    3      0      1     0     0
    2      1      0     0     0

SAS lit la première ligne du data set : A la lecture de la première ligne du data set CONTRATS, la variable TEL est mise à jour. Sachant que pour la première observation, il a 5 contrats (NB_CNTRT=5 et _TEL=1), la première valeur de TEL est égale à 5 et toutes celles qui suivent aussi.

nb_cntrt _tel _habitat tel habitat
    5      1      0     5     0
    1      1      0     5     0
    2      0      1     5     0
    1      1      0     5     0
    3      0      1     5     0
    2      1      0     5     0

SAS lit la seconde ligne du data set : A la lecture de la seconde observation, on ajoute 1*1 à 5. Le résultat (la valeur 6) se répercute sur toutes les observations de la variable TEL.

nb_cntrt _tel _habitat tel habitat
    5      1      0     5     0
    1      1      0     6     0
    2      0      1     6     0
    1      1      0     6     0
    3      0      1     6     0
    2      1      0     6     0

SAS lit la troisième ligne du data set : A la lecture de la troisième observation, la valeur de TEL ne change pas car on ajoute zéro (0+2*0). Par contre, celle d’HABITAT change.

nb_cntrt _tel _habitat tel habitat
    5      1      0     5     0
    1      1      0     6     0
    2      0      1     6     2
    1      1      0     6     2
    3      0      1     6     2
    2      1      0     6     2

A la fin : Au final, le data set a cette forme. Ce qui nous intéresse ici ce sont les valeurs 9 et 5 de la dernière ligne d’observations.

nb_cntrt _tel _habitat tel habitat
    5      1      0     5     0
    1      1      0     6     0
    2      0      1     6     2
    1      1      0     7     2
    3      0      1     7     5
    2      1      0     9     5

3. Garder la dernière observation

A présent, il s’agit de ne garder que la dernière observation. On commence par créer une variable EOF par l’intermédiaire de l’option END= dans l’instruction SET. Le nom de la variable ici fait référence à l’expression End of File. Mais vous êtes libre de choisir le nom qui vous convient.

  • Une option sans parenthèses : On notera que END= est une option de l’instruction SET et non une option du data set CONTRATS. C’est la raison pour laquelle elle n’apparaît pas entre parenthèses.
  • Une option en fin d’instruction : Les options du data set listées entre parenthèses doivent suivre immédiatement le nom du data set. L’option END= ne peut être ajoutée qu’après.

Cette variable (EOT) a deux particularités :

  • Une variable temporaire : D’une part, elle ne sera pas conservée dans le data set final SOLUTION2 ;
  • Une variable binaire : D’autre part, la variable est binaire ; elle prend la valeur 1 pour la dernière observation, 0 autrement.
data solution2 (...);
   set contrats (...) end=eof;
   *retain ...;
   *tel=...;
   *habitat=...;
   if eof then output;
   *if eof=1 then output;
run;

La condition : Dans la condition qui suit, seule la dernière observation d’EOT a la valeur 1. C’est donc la seule qui est retenue dans la condition. Le « =1 » est implicite dans la condition. C’est pour cela que les deux instructions, celle active et celle en commentaire, sont identiques.

L’action basée sur la condition : Le mot OUTPUT redirige la ligne sélection avec la condition dans le data set final SOLUTION2.

A dans huit jours, pour retrouver une solution avec PROC MEANS.

Annexe : Rappel sur les données et l’étape data dans son ensemble :

data contrats;
   input client $ nb_cntr tel habitat;
   datalines;
a 5 1 0
b 1 1 0
c 2 0 1
d 1 1 0
e 3 0 1
f 2 1 0
;
run;

data solution2 (drop=_: nb_cntr);
   set contrats (drop=client
                 rename=(tel=_tel
                         habitat=_habitat)
                 end=eof;
   retain tel 0
          habitat 0;
   tel     = tel     + nb_cntr * _tel;
   habitat = habitat + nb_cntr * _habitat;
   if eof then output;
run;
h1

Identifier et supprimer les doublons

juillet 14, 2008

Identifier et supprimer les doublons dans un data set fait partie des compétences de base du programmeur SAS. Trois approches sont envisageables : PROC SQL, PROC SORT et un compteur dans un data step.

Qu’appelle-t-on doublon ? des lignes complètement identiques ou seulement des lignes ayant quelques variables communes ? Il est important de savoir si deux records ayant des observations communes sont considérés comme doublons ou s’il faut que toutes les observations soient identiques. Si seules quelques variables sont considérées, il faut savoir quelle ligne est conservée et lesquelles sont supprimées.

Le data set utilisé pour l’exemple est composé de trois variables CNTRY (country), PAT_ID (patient ID) et VAL (value). Le patient 2 de Chine a trois lignes d’observations dont deux strictement identiques.

cntry    pat_id    val

 CN        1        A
 CN        2        B
 CN        2        C
 CN        2        C
 HK        3        E 

1. PROC SQL

Avec la procédure SQL, il est possible de faire des calculs et de baser sa sélection d’observations sur ce calcul. En d’autres termes, il est possible de compter le nombre de fois qu’une valeur apparaît. Pour identifier les records avec doublons, on prend ceux comptés plus d’une fois.

Première présentation : Voici, tout d’abord, une présentation de PROC SQL décomposable en deux étapes.

proc sql;
   create table two (where=(cnt_pat > 1)) as
      select cntry, pat_id, val, count(*) as cnt_pat
      from one
      group by cntry, pat_id;
quit;

Dans un premier temps, une variable donne le nombre de records par patient dans un pays donné (CNT_PAT) grâce à la syntaxe GROUP BY et la fonction COUNT. A ce stade, on obtient le data set suivant :

cntry pat_id val cnt_pat

  CN     1    A     1
  CN     2    C     3
  CN     2    B     3
  CN     2    C     3
  HK     3    E     1

Dans un second temps, seules les observations ayant des doublons sont gardées, une fois le nouveau data set créé, grâce à l’option WHERE=.

Deuxième présentation : Au lieu de créer la variable CNT_PAT et de faire une sélection ultérieure, la fonction COUNT peut être  ajoutée dans une condition introduite par le mot HAVING et faisant toujours appel à la fonction COUNT.

Pour ne voir qu’une des lignes multiples, il suffit d’ajouter DISTINCT.

proc sql;
   create table two_bis as
   select /*distinct*/ cntry, pat_id, val
   from one
   group by cntry, pat_id
   having count(*) > 1;
quit;

A l’inverse, pour ne sélectionner que les observations n’apparaissant qu’une fois, « >1 » sera remplacé par « =1 ». Seules les observations ayant exactement une occurrence sont sélectionnées.

Le DISTINCT s’applique à l’intégralité des observations. Cela permet de ne garder que des lignes uniques.

Il n’est pas possible de garder la première ligne parmi les doublons définis par des variables précises (CNTRY et PAT_ID dans l’exemple) avec cette méthode.

2. Les options de la procédure PROC SORT (NODUP/NODUPRECS, NODUPKEY)

Il existe deux options dans la procédure PROC SORT pour supprimer les doublons selon qu’ils s’appliquent

  • à toute une ligne d’observation (NODUPRECS dont l’alias est NODUP) ou
  • à une liste de variables précises données dans l’instruction BY (NODUPKEY).

Au choix, le nouveau data set sans doublons remplace l’ancien ou est sauvegardé dans un dataset différent introduit par OUT=.

Les observations ayant été exclues peuvent être sauvegardées dans un nouveau data set dont le nom est défini par DUPOUT=.

proc sort data=one out=three nodupkey dupout=three_bis;
   by cntry pat_id;
run;

proc sort data=one out=four noduprecs /*nodup*/ dupout=four_bis;
   by cntry pat_id;
run;

3. Appliquer une variable compteur

Enfin, après PROC SQL et PROC SORT, une troisième possibilité pour identifier et supprimer les doublons, est d’ajouter une variable compteur.

Dans l’exemple ci-dessous, la variable compteur est nommée CNT. Pour la première observation d’un patient d’un pays donné, le compteur est initialisé à 1. Pour chaque nouveau record du patient, le compteur est incrémenté de 1. Quand la variable CNT est égale à 1, le record est ajouté dans le data set FIVE. Sinon il est ajouté dans le data set FIVE_BIS. A la fin, la variable compteur est supprimée.

Ainsi le data set FIVE contient les records sans doublons. Dans le cas du patient doublonné (patient 2), c’est la première observation qui est conservée.

Dans le data set FIVE_BIS, les records exclus de la première sélection sont conservés.

data five (drop=cnt) five_bis (drop=cnt);
   set one;
   by cntry pat_id val;
   if first.pat_id then cnt=1;
   else cnt+1;
   if cnt = 1 then output five;
   else output five_bis;
run;

Annexe :

data one;
   input cntry $ pat_id $ val $;
   datalines;
CN 1 A
CN 2 B
CN 2 C
CN 2 C
HK 3 E
;
run;

h1

Jongler avec les records grâce aux compteurs

juin 12, 2008

Sous SAS, une variable compteur permet de numéroter les lignes d’observations. Le plus souvent, cette variable sert ensuite à sélectionner certaines lignes d’un jeu de données et d’agir en fonction. Par exemple, pour chaque patient, un fois le compteur créé, il est simple de sélectionner les 5 premiers records.

Pour développer une variable compteur, il faut maîtriser la notion de FIRST. Vous pouvez vous reporter à l’article « Repérer les 1ers/derniers records (FIRST/LAST) ».

En outre, il faut comprendre la notion de RETAIN. Celle-ci sera abordée dans cet article.

Quatre exemples serviront à illustrer le sujet. Ils sont basés sur un data set nommé LAB ayant 9 lignes observations. Il est donné en fin d’article dans le paragraphe « Annexe ». On y trouve trois patients (PAT_ID), deux types de tests médicaux (TEST) et une date pour chaque test (variable TEST_DT). La variable compteur s’appelle à chaque fois CNT.

 1. La notion de RETAIN

Un RETAIN permet d’assigner une valeur à toutes les observations d’une variable. On préfèrera donc un RETAIN à « variable=valeur; » pour assigner une valeur unique pour toutes les records d’une variable données. 

Si entre temps, la valeur d’une observation est modifiée, tous les valeurs suivantes prennent cette nouvelle valeur. Cette seconde fonctionnalité sert pour créer une variable compteur.

Un RETAIN se définit le plus souvent dans une instruction RETAIN. Dans le cas des compteurs, on peut créer un RETAIN implicite prenant une valeur d’origine de zéro.

2. Pour chaque nouvelle observation incrémenter le compteur : dans ce premier exemple, à chaque nouvelle observation identifiée de manière unique par PAT_ID, TEST_DT et TEST, le compteur est incrémenté par 1. Le compteur va alors de 1 à 9 (le total d’observations). Les données sont préalablement triées par ces trois variables.

  test_dt    pat_id    test    cnt

28MAR2006      101     DBP      1
28MAR2006      101     SBP      2
29APR2006      101     DBP      3
27MAY2006      101     SBP      4
23JUN2006      101     SBP      5
13JAN2006      301     DBP      6
14FEB2006      301     SBP      7
15MAR2006      301     DBP      8
05MAR2006      401     DBP      9

Il faut donc dans un premier temps trier les données et rappeler cet ordre dans l’instruction BY du data step.

proc sort data=lab;
   by pat_id test_dt test;
run;

Puis la variable CNT prend une valeur de 0 pour chaque record.

Enfin les records sont lus les uns après les autres. A chaque nouvelle TEST pour un PAT_ID et TEST_DT donné, le compteur est incrémenté de 1.

data lab;
   retain cnt 0;
   set lab;
   by pat_id test_dt test;
   if first.test then cnt=cnt+1;
run;

Un RETAIN implicite : l’instruction RETAIN peut-être omise si un RETAIN implicite est utilisé. C’est le cas ici quand « cnt=cnt+1 » est remplacé par « cnt+1 ».

data lab;
   set lab;
   by pat_id test_dt test;
   if first.test then cnt+1;
run;

3. Pour chaque nouvelle date, incrémenter le compteur : dans le second exemple, pour chaque nouvelle date indépendamment du patient ou de type de test, le compteur est incrémenté par 1. Le compteur va de 1 à 8 (les 8 différentes dates). Les données sont triées par date au préalable.

  test_dt    pat_id    test    cnt

13JAN2006      301     DBP      1
14FEB2006      301     SBP      2
05MAR2006      401     DBP      3
15MAR2006      301     DBP      4
28MAR2006      101     SBP      5
28MAR2006      101     DBP      5
29APR2006      101     DBP      6
27MAY2006      101     SBP      7
23JUN2006      101     SBP      8

proc sort data=lab;
   by test_dt;
run;

data lab;
   set lab;
   by test_dt;
   if first.test_dt then cnt+1;
run;

4. Pour chaque nouveau patient, incrémenter le compteur : dans ce troisième exemple, chaque patient reçoit un numéro unique allant de 1 à 3 ; trois étant le nombre total de patients. Les données sont donc triées par PAT_ID.

   test_dt    pat_id    test    cnt

28MAR2006      101     SBP      1
28MAR2006      101     DBP      1
27MAY2006      101     SBP      1
23JUN2006      101     SBP      1
29APR2006      101     DBP      1
14FEB2006      301     SBP      2
13JAN2006      301     DBP      2
15MAR2006      301     DBP      2
05MAR2006      401     DBP      3

proc sort data=lab;
   by pat_id;
run;

data lab;
   set lab;
   by pat_id;
   if first.pat_id then cnt+1;
run;

5. Pour chaque nouveau date d’un patient donné, incrémenter le compteur : dans ce dernier exemple, à chaque nouvelle date, le compteur est incrémenté par un. Le premier patient ayant 4 dates différentes et 5 observations, le compteur va de 1 à 4 pour lui. Une des valeurs est doublée pour la date identique. Le second patient ayant trois dates uniques, le compteur va de 1 à 3 pour lui. Le compteur est donc réinitialisé à 1 pour chaque nouveau patient.

  test_dt    pat_id    test    cnt

28MAR2006      101     SBP      1
28MAR2006      101     DBP      1
29APR2006      101     DBP      2
27MAY2006      101     SBP      3
23JUN2006      101     SBP      4
13JAN2006      301     DBP      1
14FEB2006      301     SBP      2
15MAR2006      301     DBP      3
05MAR2006      401     DBP      1

proc sort data=lab;
   by pat_id test_dt;
run;

data one;
   set orig;
   by pat_id test_dt;
   if first.pat_id then cnt=1;
   else if first.test_dt then cnt+1;
run;

Annexe :

data orig;
   format test_dt date9.;
   input pat_id test_dt date9. test_type $;
   datalines;
101 28MAR2006 SBP
101 28MAR2006 DBP
101 27MAY2006 SBP
101 23JUN2006 SBP
301 14FEB2006 SBP
101 29APR2006 DBP
301 13JAN2006 DBP
301 15MAR2006 DBP
401 05MAR2006 DBP
;
run;