Archive for the ‘proc sort’ Category

h1

SAS 9.2 et PROC SORT : Quel est l’effet de l’option – sortseq=ascii (numeric_collation=on) ?

avril 10, 2011

Recherchez l’effet de l’option SAS 9.2 de la procédure PROC SORT : sortseq=ascii (numeric_collation=on),  une option qui manquait vraiment pour le tri de variables alphanumérique.

Pour cela, il vous suffit d’exécuter l’exemple suivant.

data one;
length x $10;
input x $;
datalines;
1
10
A
a
b
B
20
2
;
run;

proc sort data=one
out=nooption;
by x;
run;

proc sort data=one
out=withoption
sortseq=ascii (numeric_collation=on);
by x;
run;

title ‘No option’;
proc print data=nooption;
run;

title ‘With option’;
proc print data=withoption;
run;

Lecture Complémentaire

  • Trier les données par ordre croissant et décroissant
  • Tout sur l’instruction BY
  • SAS Online Doc 9.2: The PROC SORT Procedure
  • h1

    Tout sur l’instruction BY

    avril 14, 2009

    Me rendant compte que l’instruction BY pouvait poser des difficultés lorsqu’on débute avec SAS, j’ai décidé de faire le point sur cette instruction.

    1. Une instruction locale

    L’instruction BY peut servir dans toutes les étapes data et procédures à l’exception de PROC SQL.

    2. Un ordre défini par le nom des variables :

    L’instruction BY est suivie du nom des variables servant pour le tri.

    Dans un premier temps, les observations sont triées selon les valeurs de la première variable citée, ici SEX. Puis, pour chacune des valeurs prises par SEX (M et F), les données sont triées par NAME.

    proc sort data=sashelp.class out=class;
       by sex name;
    run;

    3. PROC SORT : le premier usage de l’instruction BY : L’instruction BY est logiquement obligatoire dans un PROC SORT. Sinon SAS ne saurait pas dans quel ordre trier les données. Le mot DESCENDING peut-être ajouté pour préciser un ordre décroissant. Ici les données sont d’abord triées par SEX puis par nom en ordre descendant.

    proc sort data=sashelp.class out=class;
       by sex descending name;
    run;

    L’autre manière de trier les données est d’utiliser la procédure PROC SQL.

    Idée : Vous aurez souvent un PROC SORT avant d’utiliser l’instruction BY dans une autre procédure ou étape data. Simplifiez-vous la vie en copiant l’insstruction au niveau du PROC SORT et en la collant au niveau de la procédure ou étape data suivante.

    4. SAS vous informe si les données ne sont pas triées comme indiqué

    L’instruction BY précise à SAS dans quel ordre les données doivent être lues. Si les données ne respectent pas cet ordre, SAS s’arrête et fourni un message d’erreur.

    5. L’instruction BY : obligatoire ou optionnelle ?

    L’instruction BY est optionelle dans une instruction SET. Utilisée pour empiler les données de deux data sets, elle permettra d’intercaler les observations des deux sources selon leur valeurs au lieu de mettre d’abord toutes les observations du premier data set cité et ensuite toutes les observations du second data set.

    L’instruction BY reste pratiquement indispensable avec MERGE puisqu’elle sert à relier les observations de deux data sets par les variables du même nom. Sans elle, les observations du second data set pour les variables du même nom réécrirait sur celle du premier.

    L’instruction BY est indispensable avec FIRST et LAST.

    6. D’autres usages de l’instruction BY

    L’instruction BY peut s’ajouter dans un PROC REPORT. Une option NOBYLINE permettra de changer le titre à chaque nouvelle combinaison de valeurs désignée par l’instruction. Néanmoins la mise à jour du titre par cette approche avec ODS RTF par exemple ne fonctionne pas.

    L’instruction BY peut servir dans toutes less procédures (sauf PROC SQL) et notamment dans les procédures statistiques. Voir dans les lectures complémentaire l’usage de l’instruction BY avec PROC FREQ.

    Lectures Complémentaires

    SAS Online Doc

    • BY-Group Processing in SAS Programs
    h1

    La 3ème méthode qui fait la différence pour sélectionner en se basant sur une second data set

    février 16, 2009

    Suite au précieux conseil d’Arnaud Gaborit (A.I.D.), je vous propose de compléter l’article du 26 janvier dernier intitulé « Deux méthodes pour sélectionner en se basant sur une second data set« . Les personnes travaillant sur de grosses bases de données apprécieront.

    Lectures complémentaires :

    1. Rappel : les données et le résultat attendu

    D’un côté, on a un data set contenant la liste des effets secondaires observés. Il y a une ligne par effet secondaire. Seuls les patients ayant eu un effet secondaire sont enregistrés.

    data ae_multi;
       input patref ae_id;
       datalines;
    1 1
    1 2
    2 1
    4 1
    4 2
    5 1
    ;
    run;

    De l’autre côté, on a la liste des patients correspondant à la population qui nous intéresse. Une ligne correspond à un patient.

    data pat_uniq;
       input patref;
       datalines;
    2
    3
    4
    ;
    run;

    Le but est de garder uniquement les effets secondaires des patients présents dans notre population.

    patref ae_id
    
       2     1
       4     1
       4     2

    2. Créer un format à partir d’un data set

    Dans une premier temps, un format numérique (TYPE=N est la valeur par défaut) appelé PATREF (FMTNAME=’PATREF’) est créé à partir du data set sur les patients. La variable PATREF sert de valeur START. Pour toutes les valeurs de START, on applique le même libellé (LABEL=’retenir’).

    Si le data set contient plusieurs variables (ce qui sera généralement le case), l’option KEEP servira à limiter la sélection à la variable servant à définir START.

    data pat (keep=start fmtname label);
       set pat_uniq (rename=(patref=start)); *(keep=patref);
       fmtname='PATREF';
       label='retenir';
    run;

    Si plusieurs valeurs identiques pour la variable START sont présentes dans votre fichier, vous devrez supprimer les doublons. L’étape suivante ne marchera pas sinon.

    *proc sort data=pat nodupkey;
    *by start;
    *run;

    Le data set est à présent convertit en format au moyen de l’option CNTLIN. Ce format est sauvegardé par défaut dans le catalogue FORMATS de la bibliothèque WORK.

    proc format cntlin=pat;
    run;

    3. Sélectionner les observations de son choix

    Ici, l’option WHERE est privilégiée à l’instruction IF pour des raisons de performance.

    Ensuite, grâce à la fonction PUT, les valeurs de PATREF sont converties en RETENIR si elles sont présentes dans le data set PAT_UNIQ. Seules ces valeurs RETENIR sont gardées.

    data ae_multi;
       set ae_multi (where=(put(patref,patref.) = 'retenir'));
    run;

    Voir le résultat :

    proc print data=ae_multi;
    run;
    h1

    Trier les données par ordre croissant et décroissant

    septembre 23, 2008

    Pour trier (to sort en anglais) les données d’un tableau SAS dans un ordre croissant ou décroissant, il existe sous SAS deux approches : la première consiste à faire appel à la procédure PROC SORT sur un data set existant, la seconde consiste à ordonner les données sélectionnées dans une procédure PROC SQL.

    1. L’exemple

    Dans l’exemple ci-dessous, les données du data set CLASS situé dans la bibliothèque SASHELP sont triées selon les valeurs des variables SEX, AGE et NAME.

    1. Variable SEX, ordre croissant : Les données de la variable SEX sont d’abord triées par ordre croissant (increasing order). Les femmes (F pour Female) apparaissent en premier, les hommes (M pour Male) apparaissent en second.
    2. Variable AGE, ordre décroissant : Dans chacun des groupes, les données sont ensuite triées par ordre décroissant d’âge (descreasing order). Les femmes les plus âgées apparaissent en premier et les hommes les plus jeunes en dernier.
    3. Variable NAME, ordre décroissant : Enfin quand plusieurs personnes du même sexe et du même âge appaissent, les données sont triées par ordre alphabétique inverse.

    2. Trier par ordre décroissant

    Deux mots-clés différents : Pour trier les données par ordre décroissant, chaque variable doit être accompagnée d’un mot-clé. Il s’agit de DESCENDING avec PROC SORT et DESC avec PROC SQL.

    Deux mot-clés situés à des endroits différents : Dans le cas de PROC SORT, ce mot DESCDENDING apparaît avant le nom de la variable. Dans le cas de la PROC SQL, DESC suit la variable

    3. La procédure PROC SORT

    Les data sets de la bibliothèque SASHELP sont des données figées par SAS. Elles ne peuvent donc pas être modifiées. C’est pourquoi dans l’exemple, un nouveau data set CLASS est créé dans la bibliothèque WORK grâce à OUT=.

    proc sort data=sashelp.class out=class;
    by sex descending age descending name;
    run;

    Un PROC PRINT suffira pour afficher les trois variables ou toutes les variables triées. NOOBS enlèvera le numéro des observations qui appraissent par défaut.

    proc print data=class noobs;
    *var sex age name;
    run;

    4. La procédure SQL

    proc sql;
    select sex, age, name
    from sashelp.class
    order by sex, age desc, name desc;
    quit;

    Rappel : Pour afficher toutes les variables remplacées la liste de SELECT par une étoile (*). Pour créer un data set, ajoutez CREATE TABLE class AS.

    5. Le résultat

    sex  age  name

    F   15   Mary
    F   15   Janet
    F   14   Judy
    F   14   Carol
    F   13   Barbara
    F   13   Alice
    F   12   Louise
    F   12   Jane
    F   11   Joyce
    M   16   Philip
    M   15   William
    M   15   Ronald
    M   14   Henry
    M   14   Alfred
    M   13   Jefrrey
    M   12   Robert
    M   12   John
    M   12   James
    M   11   Thomas

    h1

    Je garde ou je jette? les variables

    août 25, 2008

    Pour garder ou supprimer sous SAS des variables, il y a les mots-clés KEEP (garder) et DROP (enlever). Sélectionner les variables nécessaires par la suite et seulement celles-ci est très important. Cela fait partie des outils pour améliorer la performance d’un programme tant en terme de temps d’exécution que le volume demandé pour stocker les data sets. Voici plus en détails, et avec des exemples, l’utilisation de ce vocabulaire qui s’applique au data step, aux procédures et à la syntaxe de l’ODS OUTPUT.

    1. L’option dans un data step

    Les mots KEEP et DROP servent principalement en tant qu’option appliquée à un data set donné. Elles sont alors listées juste après le nom du data set entre parenthèses et sont suivies du signe égal :

    • Keep= Data Set Option : nom_du_dataset (keep=nom_var1 nom_var2 etc)
    • Drop= Data Set Option : nom_du_dataset (drop=nom_var1 nom_var2 etc)

    Exemple 1 : une instruction SET

    data class (drop=weight: height:);
       set sashelp.class (keep=name weight height);
       weight_kg = weight*0.45359237;
       height_m  = height*0.0254;
       bmi       = weight_kg/height_m**2;
    run;

    Dans le cas présent, les variables NAME (nom), WEIGHT (poids) et HEIGHT (taille) sont lues dans le fichier d’origine SASHELP.CLASS et gardées. De nouvelles variables sont calculées pour avoir un poids en kilogramme (WIEGHT_KG) et une taille en mètres (HEIGHT_M). A partir du poids et de la taille, l’indicateur de masse corporelle (BMI) est calculé. Les variables, dont le nom commence par WEIGHT et HEIGHT, ne sont plus nécessaires par la suite. Elles sont donc supprimée dans le data set final appelé CLASS.

    Soit le mot KEEP, soit le mot DROP est donné en option mais pas les deux afin d’éviter les confusions. Le choix entre KEEP et DROP dépend souvent du nombre de variables à lister par la suite. C’est donc un choix purement pratique.

    Note, Indice de masse corporelle : L’indice de masse corporel (Body Mass Index, BMI) est égal au poids divisé par la taille au carré (poids/taille2). Le site de l’Organisation Mondiale de la Santé (World Health Organisation, WHO) donne des précisions sur le sujet.

    Note, Conversion des unités de mesures (source Wikipedia) : je suppose que la taille donnée dans le fichier SASHELP.CLASS est exprimée en pouces (inches) et que le poids est exprimé en livres (pounds). Sachant qu’un pouce est égal à 2,54 cm et qu’une livre est égale à 0,45359237 kg, les tailles et poids du premier exemple ont pu être convertis en mètres et kilos.

    Exemple 2 : une instruction MERGE

    data age_ae;
       merge ae      (in=ref keep=name ae_id ae_sev)
             patient (keep=name age);
       by name;
       if ref;
    run;

    Dans ce second exemple, les patients ayant eu un effet secondaire (adverse event, AE) sont enregistrés dans le data set AE. Chaque effet secondaire est identifié de manière unique par les variables NAME et AE_ID. La sévérité de l’effet secondaire nous intéresse dans le data set AE.

    A cette information, est ajouté l’âge du patient disponible dans la variable AGE du data set PATIENT.

    La variable commune aux deux data sets est NAME. Il faut donc qu’elle reste dans les deux data sets. Seuls les patients ayant eu un effet secondaire sont sélectionnés grâce à l’option IN.

    Pour tester l’exemple, vous trouverez en fin d’article un code créant les fichiers PATIENT et AE.

    NOTE : Une variable utilisée par une autre option du data set comme RENAME ou WHERE ne pourra pas être supprimée au même moment.

    2. Quelques exemples de procédures

    Dans une procédure, elles suivent le nom du data set d’entrée et/ou du data set de sortie. Aucun autre mot ne doit être inséré entre le nom du data set et les options entre parenthèses.

    • proc sort data=… () out=…();
    • proc print data=… () width=min;
    • proc transpose data=…() out=…() prefix=visit;
    • proc freq data=…();
    • proc report data=…() split=’#’;
    • proc tabulate data=…()
    • proc gplot data=…()
    • proc boxplot data=…()
    • proc univariate data=…()
    • proc ttest data=…()
    • etc.

    La procédure SQL liste les variables à garder après le mot SELECT. Il n’y a pas à ce stade d’option pour supprimer les variables. Par contre, on peut affiner la sélection après que le data set final soit créé. Bien sûr, le temps de lecture est augmenté puisque toutes les variables sont lues pour créer le data set et non un sous-ensemble.

    proc sql;
       create table test (drop=ae_sdt ae_edt) as
          select a.*, age
          from ae a
          left join
               patient b
          on a.name=b.name;
    quit;

    NOTE : L’option WHERE est très pratique lorsqu’on en peut faire une sélection que sur le résultat de la fusion.

    proc sql;
       create table test (where=(ae_sev=1 or age=12)) as
          select a.*, age
          from (select name, ae_id, ae_sev
                from ae) a
          left join
               (select name, age
                from patient) b
          on a.name=b.name;
    quit;

    3. L’option dans l’ODS OUTPUT

    Les sorties générées par une procédure sont redirigeables vers un data set via l’instruction ODS OUTPUT. Le nom de la sortie est alors suivi du signe égal et du nom du data set de destination. Après ce nom les options sont ajoutables.

    ods exclude all;
    ods output onewayfreqs=exemple_ods (keep=age frequency percent);
    proc freq data=sashelp.class;
       table age;
    run;
    ods output clear;
    ods exclude none;

    Deux articles sur l’ODS OUTPUT sont déjà à votre disposition

    4. Les instructions KEEP et DROP dans un data step

    En plus des options KEEP et DROP, il existe les instructions KEEP (KEEP Statement) et DROP (Drop Statement) pouvant être exécutées dans un data step. L’important ici est de se souvenir que l’instruction s’applique à la fin du data step, une fois que le data set final est créé. Ainsi il n’est pas possible de supprimer une variable en milieu de programme pour ensuite créer une autre variable du même nom.

    data class (drop=i);
       do i=0 to 3;
          output;
       end;
       do i=10 to 12;
          output;
       end;
    run;

    data class;
       do i=0 to 3;
          output;
       end;
       drop i;
       do i=10 to 12;
          output;
       end;
    run;

    Les deux exemples ci-dessus font le même travail. La variable I n’apparaîtra pas dans le data set final car elle est supprimée en fin de programme.

    Lectures complémentaires : Outre les options KEEP et DROP, le programmeur utilisera souvent les options RENAME et WHERE et de temps en temps les options FIRSTOBS et OBS. Pour une liste complète des options, consultez la documentation en ligne : SAS Data Set Option.

    Après savoir comment supprimer les variables et donc les colonnes d’un data set SAS, vous serez peut-être intéressé de savoir comment supprimer ou garder certaines lignes d’un data set avec les mots-clés DELETE (DELETE Statement) et OUTPUT (OUTPUT Statement).

    Annexe : Créer les data sets PATIENT et AE pour tester l’exemple avec MERGE.

    data patient;
       set sashelp.class;
    run;

    data ae;
       set sashelp.class (keep=name);
       if name=‘Thomas’ then
          do;
             ae_id  = 1;
             ae_sdt = ’21MAR2007’d;
             ae_edt = ’28APR2007’d;
             ae_sev = 3;
             output;
             ae_id  = 2;
             ae_sdt = ’03JUN2007’d;
             ae_edt = ’19JUN2007’d;
             ae_sev = 1;
             output;
          end;
    run;

    h1

    Un tableau à une dimension avec PROC FREQ et ODS OUTPUT

    juillet 31, 2008

    Pour personnaliser une sortie statistique générée par une procédure SAS, il faut parfois convertir ses résultats en tableau (SAS data set). Dans un précédent article, la procédure PROC MEANS a été mise à l’honneur (Diriger la sortie d’un PROC MEANS dans un dataset SAS). Maintenant nous abordons la procédure PROC FREQ dans le cadre d’un tableau à une dimension en combinaison avec l’instruction ODS OUTPUT.

    1. Identifier le nom des sorties : un exemple avec une variable

    ods trace on/listing;

    *Exemple 1 : Proc Freq, une variable;
    proc freq data=sashelp.class;
       table age;
    run;

    ods trace off;

    On obtient deux types de tables selon qu’il s’agisse d’un tableau à une dimension ou à plusieurs dimensions.

    • OneWayFreqs: Dans le cas d’un PROC FREQ avec une seule variable, on parle de OneWayFreqs.
    • CrossTabFreqs: Dans le cas d’un tableau croisé, la sortie se nomme CrossTabFreqs.

    Output Added:
    ————-
    Name: OneWayFreqs
    Label: One-Way Frequencies
    Template: Base.Freq.OneWayFreqs
    Path: Freq.Table1.OneWayFreqs
    ————-

    1. Identifier le nom des sorties : un exemple avec 2 variables

    Dans ce second exemple, deux variables (AGE et SEX)  sont listées. Les statistiques de l’une seront indépendantes des statistiques de l’autre.

    ods trace on/listing;

    *Exemple 2 : Proc Freq, deux variables;
    proc freq data=sashelp.class;
       table age sex;
    run;

    ods trace off;

    Pour distinguer les statistiques de la table SEX de celles de la table AGE, deux outputs sont créés dans la fenêtre OUTPUT. Leur nom (NAME) est identique mais leur chemin d’accès (PATH) varie. On parle de TABLE1 et TABLE2.

    Output Added:
    ————-
    Name: OneWayFreqs
    Label: One-Way Frequencies
    Template: Base.Freq.OneWayFreqs
    Path: Freq.Table1.OneWayFreqs
    ————-

    Output Added:
    ————-
    Name: OneWayFreqs
    Label: One-Way Frequencies
    Template: Base.Freq.OneWayFreqs
    Path: Freq.Table2.OneWayFreqs
    ————-

    3. Identifier le nom des sorties : un exemple avec une instruction BY

    Avant de se servir d’une instruction BY, les données doivent être triées. La procédure PROC SORT le fait. 

    Le data set source est un data set non modifiable. C’est un data set de la bibliothèque SASHELP fournit avec le logiciel SAS.

    La version triée est sauvegardée de manière temporaire dans la bibliothèque WORK (bibliothèque par défaut quand son nom n’est pas cité). Ce nouveau dataset s’appelle CLASS.

    proc sort data=sashelp.class out=class;
       by sex;
    run;

    ods trace on/listing;

    *Exemple 3 : Proc Freq, une variable et une instruction BY;
    proc freq data=class;
       by sex;
       table age;
    run;

    ods trace off;

    Avec une instruction BY, il y a plusieurs outputs pour une seule procédure dans la fenêtre OUTPUT. Les deux sorties ont le même nom (NAME) mais le chemin d’accès (PATH) est différent. On parle de TABLE1 dans les deux cas et de BYGROUP1 et BYGROUP2 pour les distinguer.

    Output Added:
    ————-
    Name: OneWayFreqs
    Label: One-Way Frequencies
    Template: Base.Freq.OneWayFreqs
    Path: Freq.ByGroup1.Table1.OneWayFreqs
    ————-

    Output Added:
    ————-
    Name: OneWayFreqs
    Label: One-Way Frequencies
    Template: Base.Freq.OneWayFreqs
    Path: Freq.ByGroup2.Table1.OneWayFreqs
    ————-

    4. ODS OUTPUT et PROC FREQ

    L’instruction ODS OUTPUT convertit les sorties dans un data set SAS. Voici donc le résultat de trois PROC PRINT sur les data sets créés avec l’instruction ODS OUTPUT.

    4.1 Exemple 1, tableau avec une variable

    Dans ce premier exemple, on note, d’une part, la présence de la variable caractère TABLE ; d’autre part, deux variables listent les différentes valeurs prises par la variable AGE : l’une est caractère, l’autre est numérique.

    *Exemple 1 : Proc Freq, une variable;
    proc freq data=sashelp.class;
       table age;
       ods output onewayfreqs=exemple1;
    run;

                                             Cum      Cum
    Table     F_Age Age Frequency Percent Frequency Percent

    Table Age   11   11     2      10.53       2      10.53
    Table Age   12   12     5      26.32       7      36.84
    Table Age   13   13     3      15.79      10      52.63
    Table Age   14   14     4      21.05      14      73.68
    Table Age   15   15     4      21.05      18      94.74
    Table Age   16   16     1       5.26      19     100.00

    4.2 Exemple 2, tableau avec deux variables

    En ajoutant une deuxième variable SEX, des variables supplémentaires sont ajoutées dans la table de sortie. Elles listent les valeurs prises par la variable supplémentaire. Comme précédemment avec la variable AGE, il y a deux variables pour accéder à la fois à l’information sous forme caractère et sous forme numérique a priori. Mais comme ici la variable d’origine est caractère, les deux sont de type caractère.

    *Exemple 2 : Proc Freq, deux variables;
    proc freq data=sashelp.class;
       table age sex;
       ods output onewayfreqs=exemple2;
    run;

                                            Cum      Cum
    Table    F_Age Age Frequency Percent Frequency Percent F_Sex Sex

    Table Age  11   11     2      10.53        2     10.53
    Table Age  12   12     5      26.32        7     36.84
    Table Age  13   13     3      15.79       10     52.63
    Table Age  14   14     4      21.05       14     73.68
    Table Age  15   15     4      21.05       18     94.74
    Table Age  16   16     1       5.26       19    100.00
    Table Sex        .     9      47.37        9     47.37    F   F
    Table Sex        .    10      52.63       19    100.00    M   M

    4.3 Exemple 3, tableau avec une instruction BY

    Avec l’instruction BY, une seule variable est créée pour distinguer les âges des hommes de ceux des femmes.

    *Exemple 3 : Proc Freq, une variable et une instruction BY;
    proc freq data=class;
      
    by sex;
      
    table age;
    run;

                                                 Cum      Cum
    Sex   Table   F_Age Age Frequency Percent Frequency Percent

      Table Age   11   11     1      11.11      1      11.11
     F  Table Age   12   12     2      22.22      3      33.33
     F  Table Age   13   13     2      22.22      5      55.56
     F  Table Age   14   14     2      22.22      7      77.78
     F  Table Age   15   15     2      22.22      9     100.00
     M  Table Age   11   11     1      10.00      1      10.00
     M  Table Age   12   12     3      30.00      4      40.00
     M  Table Age   13   13     1      10.00      5      50.00
     M  Table Age   14   14     2      20.00      7      70.00
     M  Table Age   15   15     2      20.00      9      90.00
     M  Table Age   16   16     1      10.00     10     100.00

    NOTE : Dans le cas de l’utilisation de plusieurs PROC FREQ se servant du même OUTPUT (onewayfreqs par exemple), il est conseillé de nettoyer l’ODS OUTPUT avec l’option CLEAR.

    ods output clear;

    h1

    Diriger les sorties d’un PROC MEANS dans un dataset SAS

    juillet 24, 2008

    Par défaut, les résultats des procédures SAS sont affichés dans la fenêtre OUTPUT. Pour convertir ces résultats en tableau SAS (SAS dataset), la syntaxe de l’ODS (Output Delivery System) dispose d’outils appropriés comme les instructions 

    • ODS TRACE pour identifier une sortie,
    • ODS OUTPUT pour rediriger une sortie vers un data set,
    • ODS SELECT and ODS EXCLUDE pour choisir les sorties apparaissant dans la fenêtre OUTPUT.

    Pour illustrer ce sujet, la procédure PROC MEANS sera utilisée.

    Remise dans le contexte :

    • Changer l’éditeur pour une présentation plus fine : rediriger vers un document RTF avec ODS RTF s’applique une fois que la formulation nous convient.
    • Changer la formulation des informations : le TEMPLATE peut préalablement être modifié pour n’afficher qu’un sous ensemble du résultat par exemple.
    • Faire de gros travaux : pivoter un tableau, combiner des résultats entre eux ou encore calculer une valeur à partir des résultats existant fait partie des gros travaux qui nécessitent de passer par un dataset SAS.

    1. Identifier le nom des résultats

    Pour voir le nom des différentes outputs, entourez votre procédure des instructions ODS TRACE ON/LISTING et ODS TRACE OFF.

    • La première instruction demande l’affichage de chacune des noms des sorties.
    • L’option LISTING permet d’afficher cette information dans la fenêtre OUTPUT. La destination par défaut est la LOG.
    • Enfin, la seconde instruction désactive le traçage.

    Premier exemple

    ods trace on/listing;

    *Exemple 1 : Proc Means;
    proc means data=sashelp.class;
       var height weight;
    run;

    ods trace off;

    On obtient la sortie de PROC MEANS appelée SUMMARY.

    Output Added:
    ————-
    Name: Summary
    Label: Summary statistics
    Template: base.summary
    Path: Means.Summary
    ————-

    Second exemple avec une instruction BY : il est toujours possible d’ajouter une instruction BY dans PROC MEANS.

    proc sort data=sashelp.class out=class;
       by sex;
    run;

    ods trace on/listing;

    *Exemple 2 : Proc MEANS et l’instruction BY;
    proc means data=class;
       by sex;
       var height weight;
    run;

    ods trace off;

    Il y a plusieurs outputs pour une seule procédure. Les deux sorties ont le même nom (NAME) mais le chemin d’accès (PATH) est différent. On parle de BYGROUP1 et BYGROUP2 pour les distinguer.

    Output Added:
    ————-
    Name: Summary
    Label: Summary statistics
    Template: base.summary
    Path: Means.ByGroup1.Summary
    ————-

    Output Added:
    ————-
    Name: Summary
    Label: Summary statistics
    Template: base.summary
    Path: Means.ByGroup2.Summary
    ————-

    2. ODS OUTPUT et PROC MEANS

    Dans le premier exemple, un data set EXEMPLE1 est créé à partir de la sortie ONEWAYFREQ de la PROC MEANS appliquée au fichier SASHELP.CLASS.

    J’ai choisi d’écrire l’instruction ODS OUTPUT à l’intérieur de la procédure puisqu’elle s’applique uniquement à la procédure mais elle peut être aussi affichée avant la procédure.

    Une seule instruction pour plusieurs data sets à créer : Ecrire l’instruction ODS OUTPUT avant la procédure est intéressant si vous générez plusieurs sorties de procédures différentes et voulez lister tous les data sets SAS à créer en une seul instruction ODS OUTPUT.

    *Exemple 1 : Proc Means;
    proc means data=sashelp.class;
       var height weight;
       ods output summary=exemple1;
    run;

     
    Un fichier brut peu lisible : Après un PROC PRINT sur le data set EXEMPLE1, on découvre un fichier peu lisible dès qu’il y a plus d’une variable.

       VName_          
       Height      Height_N    Height_Mean  

       Height        19        62.336842105  

       Height_
       StdDev      Height_Min  Height_Max

    5.1270752466     51.3          72

       VName_
       Weight      Weight_N    Weight_Mean
       Weight        19        100.02631579

      Weight_
       StdDev      Height_Min  Height_Max
    22.773933494     50.5          150

    Solution, étape 1 : Une solution est de faire un PROC TRANSPOSE pour un résultat sous la forme ci-dessous.

       _NAME_     _LABEL_     COL1

    Height_N      N          19.000
    Height_Mean   Mean       62.337
    Height_StdDev Std Dev     5.127
    Height_Min    Minimum    51.300
    Height_Max    Maximum    72.000
    Weight_N      N          19.000
    Weight_Mean   Mean      100.026
    Weight_StdDev Std Dev    22.774
    Weight_Min    Minimum    50.500
    Weight_Max    Maximum   150.000

    Dans la sortie brute de l’ODS OUTPUT, les variables commencent soit par HEIGHT, soit par WEIGHT (soit par VNAME ne m’intéresse pas ici). Il s’agit de transposer toutes ces variables. Il faut donc toutes les lister. Par soucis de clarté et de simplicité, on peut faire appel à une version abrégée : la racine commune des variables suivie de deux points.

    proc transpose data=exemple1 out=exemple1;
       var height: weight:;
    run;

    Solution, étape 2 : Pour retrouver une présentation semblable à celle d’un PROC MEANS envoyé dans la fenêtre OUTPUT, il faut transposer de nouveau. Auparavant, il faut créer une variable distinguant la variable HEIGHT de la variable WEIGHT.

    var_name  N   Mean   Std_Dev Minimum Maximum

     Height  19  62.337  5.1271    51.3     72
     Weight  19 100.026 22.7739    50.5    150

    Créer une variable nommée VAR_NAME prenant soit la valeur HEIGHT soit la valeur WEIGHT.

    • La fonction SCAN récupérera, dans une variable nommée VAR_NAME, le premier mot de la variable _NAME_ après avoir précisé que chaque mot est séparé par un trait bas (underscore).
    • La longueur de la variable est définie explicitement au cas où certains noms de variables seraient supérieurs à 8 et donc tronqués.
    • Cette longueur est définie avant l’instruction SET pour que la variable VAR_NAME apparaisse en premier.

    data exemple1 (drop=_name_);
       length var_name $12;
       set exemple1;
       var_name=scan(_name_,1,‘_’);
    run;

    Il est alors possible de faire pivoter le data set. Une ligne est créée pour HEIGHT et une autre pour WEIGHT, c’est-à-dire pour chaque nouvelle valeur de VAR_NAME. Chaque colonne prend le nom contenu dans la variable _LABEL_.

    proc transpose data=exemple1 out=exemple1 (drop=_name_);
       by var_name;
       id _label_;
    run;

    L’instruction BY : Dans le cas du PROC MEANS, on ne rencontre pas de difficulté particulière liée à l’instruction BY. Au lieu d’avoir une ligne dans le fichier de sortie, on a autant de lignes que de valeurs dans la variable de l’instruction BY. Dans notre exemple, on a donc deux lignes avec une instruction BY SEX. Vous pouvez retrouver le code pour la transposition en fin d’article pour obtenir la sortie suivante :

    Sex=F

    var_name  N   Mean  Std_Dev Minimum Maximum

     Height   9 60.5889  5.0183   51.3    66.5
     Weight   9 90.1111 19.3839   50.5   112.5

    Sex=M

    var_name  N  Mean  Std_Dev Minimum Maximum

     Height  10  63.91  4.9379    57.3    72
     Weight  10 108.95 22.7272    83.0   150

    3. Sélectionner ou exclure certaines sorties ou toute les sorties (ODS EXCLUDE, SELECT, LISTING)

    L’instruction ODS LISTING CLOSE suspend l’envoie de toutes les sorties dans la fenêtre OUTPUT. L’ODS EXCLUDE interrompt un sous-ensemble ou toutes les sorties générées par la procédure. A l’inverse, l’ODS SELECT retient les sorties. A vous de voir si vous avez plus vite fait de lister les sorties à garder ou celles à exclure.

    Quelques sorties : Pour sélectionner ou exclure quelques sorties en particulier, il suffit d’ajouter leur nom, trouvés au préalable avec ODS TRACE ON, séparé par un espace dans l’instruction. Cela est pratique pour des procédures générant beaucoup de sorties comme PROC UNIVARIATE. 

    Toutes les sorties : Pour faire la même chose sur toutes les sorties, on utilise ALL et NONE avec ODS EXCLUDE et ODS SELECT.

    *Exemple 1 : Proc Means;

    ods exclude all; *ods select none; *ods listing close;
    proc means
    data=sashelp.class;
       var height weight;

       ods output summary=exemple1;
    run;
    ods exclude none; *ods select all; *ods listing;

    Note : Toutes les explications couvertes dans cet article s’appliquent aussi à la procédure PROC SUMMARY.

    Annexe : Détails du programme s’appliquant au second exemple

    *Exemple 2 : Proc MEANS et l’instruction BY;

    proc sort data=sashelp.class out=class;
       by sex;
    run;

    proc means data=class;
       by sex;
       var height weight;
       ods output summary=exemple2;
    run;

    proc transpose data=exemple2 out=exemple2;
       by sex;
       var height: weight:;
    run;

    data exemple2 (drop=_name_);
       length var_name $12;
       set exemple2;
       var_name=scan(_name_,1,‘_’);
    run;

    proc transpose data=exemple2 out=exemple2 (drop=_name_);
       by sex var_name;
       id _label_;
    run;

    proc print data=exemple2 noobs;
       by sex;
    run;

    h1

    2 suggestions pour grouper les valeurs d’une variable

    juillet 21, 2008

    Sous SAS, lorsqu’une variable contient plusieurs valeurs à regrouper pour n’en former qu’une seule, il existe plusieurs options. Voici deux suggestions : une basée sur la notion de RETAIN et FIRST/LAST, l’autre sur PROC TRANSPOSE et ARRAY.

    Pour illustrer le propos un data set liste plusieurs actions pour un patient à une visite donnée. Il s’agit de regrouper ces actions par patient et visite dans un seul record.

    Le data set avant

    pat_id  visit_dt rec_id action

       1   02APR2007    1   RAYON X
       1   02APR2007    2   ULTRASON
       1   02APR2007    3   SCANNER
       2   15NOV2007    2   RAYON X
       2   15NOV2007    1   ULTRASON

    Le data set après : une nouvelle variable caractère est créée ACTION_LST. On lui donnera une longueur de 200. Chaque action y est séparée par une barre. Les variables REC_ID (identifiant du record) et ACTION sont supprimées.

    pat_id  visit_dt action_lst

       1   02APR2007 RAYON X | ULTRASON | SCANNER
       2   15NOV2007 ULTRASON | RAYON X

    1. La force du RETAIN

    Pour débuter une variable ACTION_LST de longueur 200 est créée. Elle ne contient à la base aucune valeur.

    data final; *(drop = rec_id action);
       set orig;
       by pat_id visit_dt;
       length action_lst $200;
       retain action_lst ‘ ‘;
       if first.visit_dt then action_lst=action;
       else action_lst=catx(‘ | ‘,action_lst,action);
       *if last.visit_dt then output;
    run;

    Pour chaque nouvelle visite de chaque patient, ACTION_LST est initialisée. Elle prend la valeur de la variable ACTION.

    Du fait de la présence de l’instruction RETAIN, cette première valeur est maintenue pour tous les records d’une même visite. A chaque nouvelle lecture d’un record, une nouvelle action est ajoutée.

    La fonction CATX permet de concaténer les valeurs d’ACTION_LST et ACTION, et d’ajouter la barre comme délimiteur.

    Voici donc le résultat intermédiaire, avant l’activation du code mis en commentaires.

    pat_id  visit_dt action_lst

       1   02APR2007 RAYON X
       1   02APR2007 RAYON X | ULTRASON
       1   02APR2007 RAYON X | ULTRASON | SCANNER
       2   15NOV2007 ULTRASON
       2   15NOV2007 ULTRASON | RAYON X

    A présent, il s’agit de garder seulement le dernier record de chaque visite par patient avec LAST.VISIT_DT et à supprimer les variables REC_ID et ACTION.

    2. Rotation de données (PROC TRANSPOSE) et lecture en boucle (ARRAY)

    Avec cette seconde approche, le travail est découpé en deux étapes à commencer.

    Dans un premier temps, un PROC TRANSPOSE pour faire pivoter les données. Le data set n’a alors plus qu’une ligne par visite de patient. Chaque action apparaît dans une colonne donnée.

    proc transpose data=orig out=final2 (drop=_name_);
       by pat_id visit_dt;
       var action;
    run;

    pat_id  visit_dt  COL1      COL2      COL3

       1   02APR2007  RAYON X   ULTRASON  SCANNER
       2   15NOV2007  ULTRASON  RAYON X

    Dans un second temps un ARRAY nommé _ACTION est défini. Il contient toutes les variables commençant par COL. A chaque nouvelle lecture d’une variable COL, sa valeur est ajoutée à celle de la variable ACTION_LST.

    data final2 (drop=i col:);
       set final2;
       length action_lst $200;
       array _action {*} col:;
       do i=1 to dim(_action);
          action_lst = catx(‘ | ‘,action_lst,_action{i});
       end;
    run;

    Annexe : Création du data set utilisé pour l’exemple.

    data orig;
       input pat_id visit_dt date9. rec_id action $15.;
       format visit_dt date9.;
       datalines;
    1 02APR2007 1 RAYON X
    1 02APR2007 2 ULTRASON
    1 02APR2007 3 SCANNER
    2 15NOV2007 2 RAYON X
    2 15NOV2007 1 ULTRASON
    ;
    run;

    proc sort data=orig;
       by pat_id visit_dt;
    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;

    h1

    La base de la jointure de deux data sets avec MERGE

    mai 29, 2008

    Tout d’abord, une jointure c’est quoi ? Une jointure est le rapprochement de plusieurs data sets pour n’en former plus qu’un. Maîtriser les jointures est primordial pour un programmeur. On peut citer deux raisons : fusionner des données est un travail récurrent ; une mauvaise jointure a des conséquences sur toute la suite du travail. La syntaxe de SAS peut paraître triviale. L’important est de connaître ses données et de savoir clairement ce qu’on attend à la fin. Prenez le temps qu’il faut pour acquérir cette compétence, vous ne le regretterez pas.

    Les jointures peuvent être définies selon trois critères.

    • Ajouter des variables : on distingue d’un côté les jointures où le but principal est d’empiler des observations les une en dessous des autres (SET, UNION,…) des jointures où le premier but est d’ajouter des variables (MERGE, JOIN,…).
    • Avoir des variables communes aux deux data sets : selon les situations des variables communes doivent être précisées. Parfois, néanmoins, le but est différent et aucune variable commune n’est listée. On s’intéressera au cas le plus fréquent : des variables communes listées.
    • Jointure dans un data set : enfin la syntaxe du data step (SET, MERGE) se distingue de celle de la procédure SQL. Dans le cas présent, on se concentrera sur celle du data step.

    Objectif : En résumé, il s’agira de présenter la base de la jointure MERGE, propre au data step, où les variables communes sont listées dans une instruction BY. Voici donc pour commencer trois des quatre situations de base du MERGE avec des variables communes.

    Exemples : Dans les exemples qui suivent, il y a deux data sets à chaque fois. Le premier a pour variable A, X, Y, et le second a les variables X, Y et B. Les variables X et Y sont les variables communes. Le résultat de la jointure est un data set nommé FINAL. En fin d’article, vous trouverez le code pour créer rapidement ces différents data sets sources.

    1. Différents noms de jointure : si on a deux data sets qui ont une ou plusieurs variables communes, on aura le choix entre plusieurs jointures :

    • ONE-TO-ONE : si dans chaque data set, cette variable (ou la combinaison de ces variables) n’apparaissent qu’une fois, la jointure des deux data sets sera une jointure ONE-TO-ONE.
    • ONE-TO-MANY ou MANY-to-ONE : si un des deux data sets a pour cette variable plusieurs fois la même valeur, la jointure s’appellera ONE-TO-MANY ou MANY-to-ONE.
    • MANY-TO-MANY : maintenant si dans chacun des deux data sets il y a des doublons pour les variables communes, il s’agit d’une jointure MANY-TO-MANY.

    2. Des doublons dans aucun des data sets

    Dans l’exemple ci-dessous, chacune des combinaisons x=,y= n’apparaît qu’une fois. Une jointure des deux data sets sera ONE-TO-ONE.

    Avant deux data sets

       frst_one      second_one
     a    x    y     x    y    b
     1    A    B     B    D    2
     3    C    D     A    B    4
     5    B    D     E    F    6

    Après un seul data set

          final
     a    x    y    b
     1    A    B    4
     3    B    D    2
     5    C    D    .
     .    E    F    6

    Lister plus de deux data sets ONE-TO-ONE-TO-ONE-TO…: dans ce cas particulier, il est possible de lister plus de deux de data sets ayant tous des BY variables uniques.

    2. Des doublons parmi les variables communes dans un et un seul des data sets.

    S’il y a des doublons dans un des deux data sets, des lignes supplémentaires seront ajoutées. Et les observations de l’autre data sets seront dupliquées pour chaque doublons.

    MANY-TO-ONE : Ici, c’est le premier data set qui a des doublons. La valeur b=4 est répétée pour chacun des x=A, Y=B du premier data set.

    Avant deux data sets

      frst_many        second_one
     a    x    y      x    y    b
     1    A    B      B    D    2
     2    A    B      A    B    4
     9    A    B      E    F    6
     3    C    D
     5    B    D

    Après un seul data set

          final
     a    x    y    b
     1    A    B    4
     2    A    B    4
     9    A    B    4
     3    B    D    2
     5    C    D    .
     .    E    F    6

    ONE-TO-MANY : A l’inverse, ici, c’est le second data set qui a des doublons. C’est donc la valeur a=1 qui est répété pour chaque x=A, Y=B et a=5 pour chacune des combinaisons x=B, y=D du second data set.

    Avant deux data sets

      frst_one       second_many
     a    x    y     x    y    b
     1    A    B     B    D    2
     3    C    D     B    D    8
     5    B    D     A    B    3

                     A    B    4
                     E    F    6

    Après un seul data set

          final
     a    x    y    b
     1    A    B    3

     1    A    B    4
     5    B    D    2
     5    B    D    8
     3    C    D    .
     .    E    F    6

    Après la jointure, il est possible de garder seulement certaines observations. Cela fera l’objet d’un autre article. Jeudi prochain, nous verrons le cas particulier du MANY-TO-MANY.

    3. En langage SAS, cela donne quoi?

    Quelque soit le type de jointure parmi les trois listées (ONE-TO-ONE, MANY-TO-ONE et ONE-TO-MANY) la syntaxe du MERGE est la même. Je choisi dans cet exemple de nommer le premier data set FRST_… et l’autre SECOND_… Le data set créé s’appelle FINAL. Vous trouverez en bas de l’article le code pour créer ces data sets selon qu’il y a des doublons (FRST_MANY, SECOND_MANY) ou pas (FRST_ONE, SECOND_ONE).

    L’instruction BY implique que les données soient préalablement triées de la même manière. Si vous êtes sûr que l’ordre est bon, vous pouvez vous passer d’un tri. Mais c’est rare. Du coup, on a le choix entre un PROC SORT ou un ORDER BY (« GROUP BY ») dans une PROC SQL. 

    proc sort data=frst_…;
       by x y;
    run;

    proc sort data=second_…;
       by x y ;
    run;

    data final;
       merge frst_… second_…;
       by x y;
    run;

    Annexe : créer les data sets pour tester les jointures

    data frst_one;
       input a x $ y $;
       datalines;
    1 A B
    3 C D
    5 B D
    ;
    run;

    data second_one;
      input x $ y $ b;
       datalines;
    B D 2
    A B 4
    E F 6
    ;
    run;

    data frst_many;
       input a x $ y $;
       datalines;
    1 A B
    2 A B
    9 A B
    3 C D
    5 B D
    ;
    run;

    data second_many;
       input x $ y $ b;
       datalines;
    B D 2
    B D 8
    A B 3
    A B 4
    E F 6
    ;
    run;

    h1

    Retrouver la valeur suivante avec LAG ou un MERGE

    mars 5, 2008

    previsions_fr.jpg 

    Dans un précédent article, la fonction LAG a été présentée. Il s’agissait alors de créer une nouvelle variable contenant la valeur précédente d’une variable existante. Pour récupérer la valeur suivante cette fois, je vous propose deux méthodes : utiliser la fonction LAG avec un tri décroissant ou faire un MERGE.

    1. Trier par ordre décroissant et utiliser la fonction LAG : pour retrouver l’information suivante plutôt que l’information précédente, il vous suffit de trier les observations par ordre décroissant au préalable. Dans notre exemple précédent, (1, 2, 3, 4, 5) devient (2, 3, 4, 5, .). Afin de retrouver l’ordre d’origine, vous aurez besoin d’un second tri après. Cette fois-ci, c’est la dernière observation par patient qui sera manquante puisqu’il n’y a pas d’observation après pour un patient donné. En SAS, cela veut dire qu’il faudra utiliser LAST après le tri final ou FIRST avant le tri final.

    proc sort data=test;
       by patient descending dt;
    run;

    data next_dt;
      set test;
      by patient;
      next_dt=lag(dt);
      if first.patient then next_dt=.;
    run;

    proc sort data=next_dt;
       by patient dt;
    run;

    2. Extraire la valeur suivante avec un MERGE : en extrayant la variable date sans sa première observation dans un jeu de donnée, on obtient la liste des valeurs suivantes. Il suffit ensuite d’ajouter ces valeurs au jeu de données d’origine pour avoir la 2ème date comme valeur suivante pour la première observation, etc. Un MERGE sans instruction BY est suffisant. Comme avant, on actualise la dernière observation par BY variable via LAST.

    proc sort data=test;
       by patient dt;
    run;

    data next_dt;
       merge test
             test (firstobs=2 rename=(dt=next_dt));

    run;

    data next_dt;
       set next_dt;
       by patient;
       if last.patient then next_dt=.;
    run;

    h1

    Retrouver l’information précédente avec la fonction LAG

    mars 4, 2008

    Lorsqu’un test médical est conduit plusieurs fois sur un patient, les résultats pourront être sauvegardés dans une variable par ordre chronologique. Pour étudier le changement entre deux examens, on peut créer une nouvelle variable contenant l’information précédente. Ceci est un exemple d’application de la fonction LAG qui récupère la valeur de l’observation précédente.

    Ici je vous propose d’aborder cette fonction. Les exemples vont utiliser un jeu de donnée, nommé ‘test’, qui contient une variable identifiant chaque ‘patient’ et une variable date ‘dt’.  On peut donc avoir plusieurs dates par patient. L’objectif est de créer une nouvelle variable contenant la date précédente ou suivante.

    1. La fonction LAG retrouve l’observation précédente : si une variable x à cinq valeurs (1, 2, 3, 4, 5) et qu’une nouvelle variable y contenant le LAG de x est créée, cette nouvelle variable aura (., 1, 2, 3, 4) comme valeurs. Ceci veut aussi dire qu’il y aura autant d’observations y que d’observations x. Les données devront être triées au préalable.

    2. Obtenir la valeur précédente par sous groupe : la fonction LAG est indépendante d’une BY variable (ex : par patient). La première observation d’un patient devra pourtant être toujours manquante, puisqu’il n’y a pas de valeur avant pour ce patient. Pour que cette observation ne vienne pas du patient précédent, on la remplace par une valeur manquante grâce à FIRST. Mais la condition sur cette première observation sera faite après le LAG et non avant.

    proc sort;
       data=test;
       by patient dt;
    run;

    data next_dt;
       set test;
       by patient;
       next_dt=lag(dt);
       if first.patient then next_dt=.;
    run;

     

    3. Retrouver une information précédente mais pas la dernière3.1 Sauter plusieurs observations : la fonction LAG peut être agrémentée d’un nombre pour préciser l’écart avec l’observation actuelle. LAG(x) est équivalent de LAG1(x). Ainsi dans notre exemple précédant (1, 2, 3, 4, 5), LAG2(x) donne (., ., 1, 2, 3).

    3.2 Par sous-groupe : pour trouver l’information par sous-groupe, il faut ajouter un compteur. Celui-ci permet de définir une condition pour remplacer les dernières observations de chaque patient par des valeurs manquantes.

    data next_dt2 (drop=cnt);
       set test;
       by patient;
      next_dt2=lag1(dt);
       cnt+1;
       if first.patient then cnt=1;
       if cnt in (1,2) then next_dt2=.;
    run;

    En résumé, la fonction LAG ‘fait descendre’ de z steps les données. On peut récupérer l’observation précédente contigue ou une information plus lointaine.