Archive for the ‘proc sql’ Category

h1

SAS 9.2. : WARNING avec PROC SQL et l’option UNDO_POLICY=NONE

janvier 6, 2013

Depuis SAS 9.2, un nouveau WARNING apparaît dans la log quand le nom de la table SAS d’entrée est le même que le nom de la table SAS de sortie. Pour éviter ce message, l’option undo_policy=none est à ajouter dans l’instruction proc sql;. SAS support prévient néanmoins que l’usage du même nom en entrée et en sortie présente un risque et que l’option enlève l’avertissement mais pas le risque.

1. Le message dans la log

WARNING: CREATE TABLE statement recursively references the target table

 

2. Exemple

Ce code générerait le warning avec SAS 9.2 si l’option undo_policy=none n’était pas utilisée.

proc copy in=sashelp out=work;
   select class;
run;

proc sql undo_policy=none;
   create table class as 
   select name, age
   from class;
quit;

 

En savoir plus

h1

Des options pour se débarrasser de WARNING de SAS 9.2

août 18, 2010

Avec SAS 9.1.3 et SAS9.2 de nouveaux warning apparaissent. En voici deux que vous pouvez faire disparaître dans le cas où ils ne reflètent pas un problème dans votre programme.

  1. Avec PROC SQL, je crée une nouvelle table du même nom que la table source.
  2. Dans un data set je réduis la longueur d’une variable

1. Utiliser le même nom de data set en entré et en sortie avec PROC SQL

Pour illustrer le sujet, je vais d’abord créer un data set appelé ONE avec deux variables X et Y.

data one;
x=‘A’;
y=‘B’;
run;

Ensuite je vais créer avec PROC SQL un nouveau data set du même nom ONE qui ne contiendra que la variable X.

proc sql;
create table one as
select x
from one;
quit;

Le message dans la log ressemble à ceci:
WARNING: This CREATE TABLE statement recursively reference the target table. A consequence of this is a possible data integrity problem.

Pour ne plus avoir ce message dans la log, ajouter l’option UNDO_POLICY=NONE

proc sql undo_policy=none;
create table one as
select x
from one;
quit;

Ce cas existe depuis SAS 9.1.3.

2. Réduire la longueur d’une variable dans une étape data

Une solution pour changer la longueur d’une variable est de définir sa longueur dans une instruction LENGTH avant de lire les données avec un SET par exemple.

Depuis SAS 9.2, si la nouvelle longueur est plus petite que l’ancienne un WARNING apparaît dans la log. Ceci est une bonne chose car cela vous permet de repérer d’éventuelles coupures (truncations) de vos données.

Dans certains cas cependant, vous savez pertinemment que le nombre de caractères dans données est au plus X et que dès lors elles ne seront pas coupées en réduisant la longueur.

Vous pouvez vous épargner le warning en encadrant votre étape data des options globales VARLENCHK=nowarn et VALENCHK=warm.

Je vous conseille d’appliquer cette option localement afin de pouvoir continuer à repérer d’autres coupures potentielles non prévues.

data two;
length x $32;
x=‘A’;
run;

data two_a;
length x $1;
set two;
run;

WARNING : Multiple lengths were specified for the variable x by input data set(s). This may cause truncation of data.

options varlenchk=nowarn;
data two_b
length x $1;
set two;
run;
options varlenchk=warn;

Notez cependant, qu’avec SAS 9.1.3 ou SAS 9.2, un warning apparaît dès lors que la variable est donnée dans une instruction BY. L’option VARLENCHK de SAS 9.2 ne vous enlèvera pas le warning. Je vous conseille donc de changer votre longueur au préalable si vous ne voulez pas ce WARNING.

data two_b;
length x $1;
set two;
by x;
run;

WARNING: Multiple lengths were specified for the BY variable x by input data sets and LENGTH, FORMAT, INFORMAT statements. This may cause unexpected results.

Lectures complémentaires :

h1

Deux manières de créer un data set vide

juillet 2, 2009

Dans un précédent article Copier la structure d’un data set et se séparer des données, nous avons vu comment récupérer la structure d’une table (data set) de référence, c’est-à-dire copier les caractéristiques des variables sans les données. Ici, vous verrez comment créer un data set, appelé EMPTY, sans données et sans se baser sur un data set de référence.

L’intérêt est de souvent de pouvoir ensuite empiler des data sets ayant des longueurs de variables. En effet la longueur d’une variable rencontrée dans le premier data set sera la longueur de référence. Il ne faut pas qu’elle soit plus petite que celle du data set suivant. Autrement le texte des observations d’après est coupé (truncated).

1. Avec une étape data

Au choix, vous avez l’instruction ATTRIB ou les différentes instructions LABEL, LENGTH, FORMAT, INFORMAT pour créer les variables du data set.

data empty;
attrib var_text   label=‘Var. caractère, longueur 20’ length=$20
var_num_dt label=‘Var. numérique, longueur 8’ format=date9.;
stop;
run;

Dans la log, SAS précisera qu’aucune valeur n’a été donné aux variables VAR_TEXT et VAR_NUM_DT.

NOTE: Variable var_text is uninitialized.
NOTE: Variable var_num_dt is uninitialized.

2. Créer un data set vide avec la procédure SQL

La procédure SQL

proc sql;
create table empty
(
var_text char(20) label=‘Var. caractère, longueur 20’ ,
var_num_dt num label=‘Var. numérique, longueur 8’ format=date9.
);
quit;

3. Voir le résultat

J’ai choisi d’ajouter l’option VARNUM à la procédure PROC CONTENTS pour afficher les données dans l’ordre

proc contents data=empty varnum;
run;

La variable VAR_TEXT apparaît en premier. Il s’agit d’une variable alphanumérique de longueur 20 sans format et ayant pour libellé : Var. caractère, longueur 20.

La seconde variable VAR_NUM_DT est numérique, de longueur 8. Le format DATE9 est appliqué dessus de manière permanente. Le libellé de cette variable est : Var. numérique, longueur 8.

The CONTENTS Procedure

Variables in Creation Order

# Variable   Type   Len Format Label

1 var_text   Char   20         Var. caractère, longueur 20
2 var_num_dt Num     8  DATE9. Var. numérique, longueur 8

Lectures complémentaires

h1

Changer la longueur d’une variable avec PROC SQL (instruction ALTER)

avril 27, 2009

En SAS, l’instruction ALTER de PROC SQL a la capacité de changer le libellé d’un data set, modifier le format/informat ou le libellé d’une variable. Mais son principale intérêt réside dans sa capacité à modifier la longueur d’une variable caractère sans changer la position de la variable dans le data set.

1. Comparaison avec deux autres méthodes : PROC DATASETS et l’étape DATA

Alors que PROC DATASETS permet de modifier le format/informat, le libellé d’une variable ou de renommer une variable, il ne permet pas de changer la longueur d’une variable.

Avec une simple étape data, il est possible de modifier la longueur de la variable en ajoutant une instruction LENGTH avant de lire les donnees avec un SET/MERGE… Mais la séquence d’affichage des variables n’est plus respecté. Il faut soit avoir sauvegardé au préalable l’ordre d’affichage (ou l’entrer manuellement, bof !), soit utiliser l’instruction ALTER de PROC SQL…

2. Quelques données pour tester soi-même

* Create two formats SEX and GENDER;
proc format;
value $ sex ‘M’=‘Male’
‘F’=‘Female’;
value $ gender ‘M’=‘Homme’
‘F’=‘Femme’;
run;

*Create a data set named CLASS ;
*based on the SASHELP.CLASS data set ;
*adding the SEX format to the SEX variable;

data class;
set sashelp.class;
format sex sex.;
run;

3. L’instruction ALTER pour modifier la longueur d’une variable

Dans l’instruction ALTER de PROC SQL, le mot-clé MODIFY introduit deux variables SEX et NAME à modifier.

proc sql;
alter table class (label=‘Changes with PROC SQL and ALTER’)
modify sex format=$gender.,
name char(20) label=‘Student Name’;
quit;

Changement le plus intéressant :

  • La longueur de la variable caractère NAME est à l’origine 8. Elle est remplacée par 20.

Quelques changements secondaires :

  • Libellé de data set : Dans l’exemple ci-dessus le data set CLASS a pour libellé ‘Changes with PROC SQL and ALTER’.
  • format de variable : Le format de la variable SEX est à l’origine $SEX. Il est ici remplacé par le format $GENDER.
  • Libellé de variabel : Le libellé de la variable NAME est ajouté. Il s’agit de ‘Student Name’.

A retenir :

  1. Seules les variables caractères peuvent avoir leur longueur changées au moyen de l’instruction ALTER.
  2. Cette instruction permet certe de modifier le format d’un data set. Néanmoins, elle ne permet pas de supprimer tous les formats d’un data set.

Lectures complementaires :

SAS Online DOC

  • The SQL Procedure
h1

Deux méthodes pour sélectionner des données en se basant sur un second data set

janvier 26, 2009

Avec IF/SELECT/WHERE, OUTPUT/DELETE vous savez comment garder une partie des données disponible en fonction de la valeur de variables. Mais comment faire si les données servant de critère sont dans un autre data set ?

Voici un exemple :

  • D’un côté, on a un data set SAS avec tous les effets secondaires (adverse events)  de tous les patients. Un patient peut avoir plusieurs effets secondaires et donc plusieurs lignes. Il peut aussi ne pas être présent s’il aucun effect secondaire n’a été enregistré.
  • De l’autre côté on a un data set contenant seulement les patients répondant aux critères du protocol d’étude clinique (une ligne par patient).

Comment garder tous les effects secondaires de ces patients valides par protocole et seulement de ceux-là ?. SAS a deux possibilités :

  1. un MERGE dans une étape data utilisé avec son option (IN=)
  2. un PROC SQL avec la condition WHERE… IN ().

Vous trouverez un rappel sur le MERGE en lisant : « La base de la jointure de deux data sets avec MERGE« .

1. Les data sets AE_MULTI et PAT_UNIQ servent d’exemple

Une ou plusieurs lignes par patients dans le data set AE_MULTI : le data set AE_MULTI contient six effets secondaires (6 lignes d’observations) se référant à 4 patients (numéros : 1, 2, 4 et 5).

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

Une seule ligne par patient dans le data set PAT_UNIQ : le data set PAT_UNIQ contient trois patients (3 observations) : numéros 2, 3 et 4.

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

En d’autres termes, on souhaite garder les observations de AE_MULTI si et seulement si le patient est aussi enregistré dans le data set PAT_UNIQ. Trois observations sont à conserver : celles du patient 2 avec ses deux effets secondaires et celle du patient 4 avec son effet secondaire.

patref    ae_id

   2        1
   4        1
   4        2

2. Deux solutions

Dans ce cas du MERGE, on annote (flag) les données avec des variables temporaires (AE et PAT).

  • Si la valeur de PATREF est présente dans le fichier AE_MULTI, alors AE=1, sinon AE=0.
  • Si la valeur de PATREF est présente dans le fichier PAT_UNIQ, la variable PAT=1, sinon elle est égale à 0.

Si le patient est à la fois dans les deux data sets  (si AE=1 et PAT=1), alors les données sont envoyées (ouputted) dans le data set VERSION1.

Comme se sont les variables du data set AE_MULTI qui nous intéresse et non celles du data set PAT_UNIQ, on ne garde parmi les variables de PAT_UNIQ seulement la variable PATREF servant de lien et listée dans l’instruction BY.

data version1;
   merge ae_multi (in=ae)
         pat_uniq (in=pat keep=patref);
   by patref;
   if ae and pat;
run;

Avec la procédure SQL, on sélectionne toutes les données disponibles dans le data set AE_MULTI en précisant que les valeurs de la variable PATREF doit aussi être présentes dans une autre sélection, celle définie entre parenthèses.

proc sql;
   create table version2 as
      select *
      from ae_multi
      where patref in (select patref from pat_uniq);
quit;

Entre les deux méthodes, on notera deux différences :

  1. Avec le merge, il est possible de lier des data sets ayant plus d’une variable commune (by pays centre patient;).
  2. Avec la procédure SQL, on peut multiplier les données dans le WHERE. Par exemple, on pourrait à la fois choisir les patients qui sont dans le data set PAT_UNIQ et exclure les effets secondaires qui sont aussi présents dans un autre fichier que AE.
h1

5 points à retenir sur l’opérateur logique LIKE

décembre 14, 2008

Comment dire à SAS de sélectionner toutes les observations pour lesquelles une variable donnée contient le mot CASTS ? L’opérateur logique LIKE, utilisable uniquement une condition exprimée par WHERE est fait pour ce travail. Voici donc 5 points à retenir sur LIKE que vous pourrez vous même vérifier à l’aide du data set founi en bas d’article dans la rubrique « Annexe » et des exemples de la section 2.

1. Les 5 points à retenir sur LIKE

1. Trouver un texte ayant une structure donnée : Le but de LIKE est de trouver, pour une variable donnée, toutes les observations ayant une structure particulière.

2. Traiter des chaînes de caractères : LIKE s’applique à une chaîne de caractères et non à des valeurs numériques.

3. Autoriser un caractère libre : Le tiret bas (underscore : _) désigne qu’il faut absolutement un caractère à l’endroit où il est utilisé : ni plus ni moins.

4. Autoriser une chaîne de caractère libre : Le pourcentage (percent : %) accepte toute chaîne de caractère, quelque soit sa longueur, à l’endroit où le symbole apparaît. L’absence de caractère est aussi valide.

5. Appliquer LIKE dans un WHERE : LIKE s’utilise uniquement avec WHERE dans une étape data ou dans la procédure SQL. Il ne fonctionnera pas avec IF.

2. Quelques exemples

Un exemple avec les pourcentages : Dans ce premier exemple, on sélectionne toutes les lignes d’observations où la variable TEST_LAB contient le mot CASTS en majuscule. Il est possible de prendre une observation contenant du texte ou des blancs avant le mot CASTS et/ou après le mot CASTS.

data casts;
   set lab (where=(test_lab like '%CASTS%'));
run;

Pour prendre toutes les observations indépendamment de la case ajouter la fonction UPCASE autour du nom de la variable UPCASE(TEST_LAB) like…

Un exemple avec le tiret bas : imaginons que vous vouliez repérer toutes les tests en rapport avec « Leukocyte esterase » mais vous envisagerez une mauvaise saisie au niveau du k, c et y. Vous demandez alors toutes les observations ressemblant au mot « Leukocyte esterase » en acceptant n’importe quelle autre valeur pour k, c et y.

data check_leuk;
   set lab (where=(test_lab like 'LEU_O__TE ESTERAS'));
run;

Un exemple combinant pourcentage et tiret bas : dans ce troisième et dernier exemple, on cherche à trouver toutes les observations contenant le mot CAST tout en assumant que la lettre A peut être erronnée dans les données du data set LAB. N’importe quel caractère à l’emplacement du A est autorisé.

data check_casts;
   set lab (where=(test_lab like '%C_ST%'));
run;

La même chose avec PROC SQL

proc sql;
   create table check_casts as
       select *
       from lab
       where test_lab like '%C_ST%';
quit;

Annexe : CASTS réfère à des tests de laboratoires sur les urines (urinal test).

data lab;
   length test_label $100;
   input test_label $;
   datalines;
FATTY CASTS
GRANULAR CASTS
HYALINE CASTS
RED BLOOD CELL COSTS
LEUKOCYTE ESTERASE
RENAL TUBULAR EPITHELIAL CASTS
WAXY CASTS
WHISTE BLOOD CELL CASTS
KETONE
;
run;
h1

Combien de contrats ai-je au total ? (1/5) PROC SQL

octobre 27, 2008

Sur le forum http://www.commentcamarche.net, j’ai rencontré une question pour laquelle une série d’articles seront rédigés et ainsi pour présenter différentes solutions. Je commence aujourd’hui avec une procédure SQL précédée d’une introduction sur les données. Les quatre prochains articles utiliseront les compteurs, les procédures PROC MEANS, PROC FREQ et PROC TABULATE.

1. Les données

On part d’un fichier contenant une liste de clients et deux types de contrats : téléphone (tel) et habitation (habitat).

  • Une variable TEL indique si oui ou non le client a un contrat de téléphone peu importe qu’il s’agisse d’une ligne fixe, d’un téléphone portable personnel. Une variable NB_CNTR précise le nombre de contrats de ce type le client a signé.
  • Une variable HABITAT indique si oui ou non le client a un contrat en relation avec son habitat qu’il s’agisse de sa maison principale, sa maison secondaire ou d’un contrat de location pour ses enfants partis étudiés, etc. La variable NB_CNTR précise comme précédemment le nombre de contrats de ce type souscrit par le client.
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;

Objectif : Le but du jeu est de retrouver le nombre total de contrats téléphoniques et de contrats d’habitation, soit 9 dans le premier cas (5+1+1+2) et 5 dans le second cas (2+3).

2. La procédure SQL

Dans cet exemple, un data set, appelé SOLUTION1, est créé à partir du fichier CONTRATS.

Créer une nouvelle variable TEL : SAS créé une nouvelle variable TEL.

  • Nombre de contrats par client : Pour chaque ligne d’observation, SAS multiplie le nombre de contrat par la variable binaire TEL pour retrouver le nombre de contrats téléphonique par client.
a 5 1 0 => 5*1 = 5
b 1 1 0 => 1*1 = 1
c 2 0 1 => 2*0 = 0
d 1 1 0 => 1*1 = 1
e 3 0 1 => 3*0 = 0
f 2 1 0 => 2*1 = 2
  • Puis, il fait la somme de tous les nombres (5+1+0+1+0+2) pour connaître le nombre total de contrats téléphoniques. Ce nombre est répété pour chaque observation.
a 5 1 0 => 9
b 1 1 0 => 9
c 2 0 1 => 9
d 1 1 0 => 9
e 3 1 0 => 9
f 2 0 1 => 9

Créer une nouvelle variable HABITAT : De la même manière, une nouvelle variable HABITAT est créée.

  • Nombre de contrat par client : Pour chaque ligne d’observation, SAS multiplie le nombre de contrats par la variable binaire HABITAT pour retrouver le nombre de contrats liés à l’habitation par client.
a 5 1 0 => 5*0 = 0
b 1 1 0 => 1*0 = 0
c 2 0 1 => 2*1 = 2
d 1 1 0 => 1*0 = 0
e 3 0 1 => 3*1 = 3
f 2 1 0 => 2*0 = 0
  • Nombre total de contrats : Puis, il fait la somme de tous les nombres (0+0+2+0+3+0) pour connaître le nombre total de contrats d’habitation. Ce nombre est répété pour chaque observation.
a 5 1 0 => 5
b 1 1 0 => 5
c 2 0 1 => 5
d 1 1 0 => 5
e 3 0 1 => 5
f 2 1 0 => 5

Ne souhaitant pas afficher le nom des clients (variable CLIENT), il est possible d’extraire une seule ligne pour les nouvelles variables TEL et HABITAT en ajoutant le mot-clé DISTINCT. Comme toutes les lignes ont les valeurs 9 et 5, une seule sera conservée.

proc sql;
   create table solution1 as
      select distinct sum(nb_cntr*tel) as tel,
                      sum(nb_cntr*habitat) as habitat
      from contrats;
quit;

Je vous donne rendez-vous tous les lundi pour présenter une nouvelle solution à commencer par lundi prochain dans une étape data, basée sur la notion de variable compteur générée à partir d’un RETAIN.

h1

Copier la structure d’un data set et se séparer des données

octobre 16, 2008

Sous SAS, comment récupérer les caractéristiques d’un data set dans un autre data set sans les données et ainsi s’épargner un travail occasionnel mais qui peut vite devenir fastidieux ? Le nom des variables, leur position dans le data set et leurs autres attributs (type, longueur, format, informat) forme la structure d’un data set. Pour les récupérer, deux notations sont offertes : l’option OBS= dans une étape data et le mot LIKE dans une procédure SQL.

1. Créer le data set servant d’exemple

Pour illustrer les deux notations, nous partirons d’un data set nommé CLASS contenant une ligne d’observations et trois variables :

  • le nom de l’élève (NAME),
  • la date de début du cursus (SDT pour starting date) et
  • la date de fin du cursus (EDT pour ending date).

data class;
attrib name length=$15 label=‘Nom’
sdt informat=date9. format=date9. label=‘Starting Date’
edt informat=date9. format=date9. label=‘Ending Date’;
input name $ sdt edt;
datalines;
Charline 06OCT2006 15JUN2007
;
run;

Un PROC CONTENTS résumera les attributs des variables de la manière suivante :

proc contents data=class;
run;

# Variable Type Len Pos Format Informat Label

3 edt Num 8 8 DATE9. DATE9. Ending Date
1 name Char 15 16 Nom
2 sdt Num 8 0 DATE9. DATE9. Starting Date

1. Créer un data set vide

Dans cette première partie, un data set SQL_SOLUTION et ETAPE_DATA sont créés. Les deux ont la même structure

La procédure SQL : Au lieu de désigner toutes les variables à garder après un AS SELECT, on passe directement au data set de référence en l’introduisant avec le mot LIKE.

proc sql;
create table sql_solution like class;
quit;

L’étape data : L’option data set OBS= sur le fichier d’entrée précise qu’aucune observation ne sera lue. Seul le ‘header’ du data set contenant les caractéristiques sont lues par SAS et sauvegardées dans le data set de sortie ETAPE_DATA.

data etape_data;
set class (obs=0);
run;

SAS ira un peu plus vite avec une instruction STOP.

data etape_data;
set class;
stop;
run;

3. Ajouter des observations au data set vide

Une fois le data set copié sans les observations deux lignes sont ajoutées. La première désigne Jean-Pierre qui a début en janvier 2006. Christophe est nommé en second. Il a début le 7 octobre 2005 et terminé le 18 juin 2007.

La procédure SQL : Dans la PROC SQL, je vous propose d’ajouter les observations manuellement grâce à l’instruction INSERT INTO.

proc sql;
create table sql_solution like class;
insert into sql_solution
set name=‘Jean-Pierre’, sdt=’10JAN2006′d
set name=‘Christophe’, sdt=’07OCT2005′d, edt=’18JUN2007′d;
quit;

L’étape data : Dans un data step, les nouvelles observations sont sauvegardées dans un autre data set et sont ajoutées au moyen de l’instruction SET.

data add;
name=‘Jean-Pierre’;
sdt=’10JAN2006′d;
output;
name=‘Christophe’;
sdt=’07OCT2005′d;
edt=’18JUN2007′d;
output;
run;

data etape_data;
set class (obs=0)
add;
run;

Note : Dans ce cas, il faut que le data set contenant la structure apparaissent en premier. SAS sauvegarde toujours la première variable qu’il rencontre avec ses attributs. Proposer une autre variable du même nom avec des attributs différents ensuite n’alternera pas celles sauvegardées en premier.

h1

Renommer une variable (RENAME)

octobre 14, 2008

Pour renommer une variable SAS dispose d’une option RENAME applicable dans une étape data et dans une procédure. De plus, l’instruction RENAME de l’étape data et le mot-clé AS de la PROC SQL sont disponibles. Voici donc une présentation de ces différentes notations.

La base des exemples :

Pour illustrer le propos, le data set CLASS de la bibliothèque SASHELP aura deux variables à renommer : la variable NAME deviendra la variable NOM et la variable HEIGHT s’appellera TAILLE.

Une observation seulement sera gardée : celle concernant ‘Barbara’. Selon que les variables sont renommées dans le fichier d’entrée ou le fichier de sortie, la variable NAME ou NOM sera utilisée pour définir la condition.

Quel ordre ? : Qu’il s’agisse d’une option ou d’une instruction RENAME, il faut utiliser le signe égal (=) avec :

  • en premier, à gauche le nom actuel de la variable
  • en dernier, à droite le nouveau nom.

Pour s’en souvenir, vous pouvez imaginer une forme de flèche. A=>B symbole alors variable A donne/devient variable B).

1. Les options du data step en solitaire

Après avoir donné le nom d’un data set dans une étape data ou dans une procédure comme PROC SORT ou PROC TRANSPOSE, il est possible de lister des options entre parenthèses parmi lesquelles on trouve RENAME. L’option s’applique donc au data set nommé juste avant.

Dans une étape data, il est possible d’appliquer l’option RENAME sur les data sets à lire ou sur les data sets créés. Voici deux exemples avec des instructions SET. Cela marche aussi avec d’autres instructions nommant un data set comme MERGE.

Dans ce premier exemple, les variables NAME et HEIGHT du data set d’entrée (SASHELP.CLASS) sont renommées. Une fois cela fait, SAS effectue les étapes suivantes à savoir garder les observations se référant à Barbara. C’est pour cela que la condition s’applique sur la variable NOM.

data opt_in;
set sashelp.class (rename=(name=nom height=taille));
if nom=‘Barbara’ then output;
run;

Dans ce second exemple, l’option RENAME est appliquée sur le fichier de sortie. La condition est donc traitée par SAS avant que les variables soient renommées. Il faut désigner la variable avec son nom d’origine (NAME) dans la condition.

data opt_out (rename=(name=nom height=taille));
set sashelp.class;
if name=‘Barbara’ then output;
run;

Les deux data sets OPT_IN et OPT_OUT donnent le même fichier final.

nom Sex Age taille Weight

Barbara F 13 65.3 98

2. Les procédures

De la même manière, l’option RENAME peut s’appliquer sur le fichier d’entrée (sauf PROC SQL) et de sortie d’une procédure. Voici deux exemples avec les procédures PROC SORT et PROC SQL.

proc sort data=sashelp.class
out=class (rename=(name=nom height=taille));
by age;
run;

proc sql;
create table opt_out (rename=(name=nom height=taille)) as
select *
from sashelp.class;
quit;

La procédure SQL dispose du mot-clé AS pour renommer une variable en particulier parmi celles énumérées.

proc sql;
create table sql_as as
select name as nom, sex, age, height as taille, weight
from sashelp.class;
quit;

3. L’instruction RENAME du data step

Il existe une instruction RENAME s’appliquant uniquement à l’étape data. Il est important dans ce cas particulier de se rappeler que les variables sont renommées seulement une fois le data set final (INSTRUCT) créé. Le processus de SAS est donc le même qu’avec l’option sur le fichier OPT_OUT.

data instruct;
set sashelp.class;
rename name=nom height=taille;
if name=‘Barbara’ then output;
run;

4. L’option RENAME combinée avec d’autres options du data step

L’option RENAME n’est qu’une des options du data set SAS. Parmi les autres options les plus courantes, citons KEEP, DROP, WHERE, FIRSTOBS et OBS.

Dans ce dernier exemple, on pourra remarquer que l’option WHERE utilise la variable sur l’âge une fois celle-ci renommée. Tandis que l’option KEEP désigne la variable âge d’origine.

data class;
set sashelp.class (keep=age where=(_age=12) rename=(age=_age));
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

4 étapes de base pour créer un data set avec PROC SQL

septembre 18, 2008

La procédure SQL disponible sous SAS est une alternative à l’étape data (data step en anglais) dans de nombreuses situations. La syntaxe est dérivée du langage SQL abbréviation de Structured Query Language. Il s’agit donc de faire une requête (query) auprès de SAS pour extraire une information à partir d’un ou plusieurs jeux de données.

Voici donc ici l’occasion de voir la syntaxe de base pour créer un data set à partir d’un autre data set.

1. Début et fin : les instructions PROC SQL et QUIT.

Pour débuter une procédure SQL, il faut taper une instruction commençant par PROC SQL et finir avec une instruction QUIT (et non RUN). Entre ces deux instructions, une nouvelle instruction créera le nouveau data set.

Il est possible d’avoir autant d’instructions entre PROC SQL et QUIT que voulu. Dans la suite, seul un data set sera créé. Il n’y aura q’une seule instruction.

proc sql;
*instruction 1: créer un premier data set par exemple;
*instruction 2 : créer un second data set par exemple;

quit;

2. Lister les variables après SELECT

Un mot introductif, SELECT : l’instruction centrale débute avec le mot-clé SELECT. A la suite figurent toutes les variables à garder.

La virgule comme délimiteur : A la différence d’un data step, la procédure SQL utilise la virgule et uniquement la virgule comme délimiteur entre les noms de variables.

proc sql;
select age, height, weight
from sashelp.class;
quit;

Renommer une variable avec AS : Au stade de la sélection des variables, il est possible d’assigner un nom différent de celui d’origine en se servant du mot AS.

Dans l’exemple ci-dessous, la variable d’origine s’appelle HEIGHT. Après cette variable s’appelle TAILLE.

proc sql;
select height as taille
from sashelp.class;
quit;

Ajouter des attributs : Dans un data step, les instructions ATTRIB, LABEL, FORMAT, INFORMAT et LENGTH sont disponibles pour définir les attributs d’une variables. Avec PROC SQL, les attributs sont à donner après le nom de chaque variable.

Exemple : Dans cet exemple, toutes les variables reçoivent un label. De plus, la longueur des variables NAME (caractère) et AGE (numérique) sont redéfinies.

proc sql;
select name length=15 label=‘Nom’,
age length=4,
height label=‘Taille’,
weight label=‘Poids’
from sashelp.class;
quit;

Note : Dans un data step, un symbole dollar ($) est obligatoire pour définir la longueur des variables caractères, alors qu’avec PROC SQL, ce symbole n’est pas demandé.

Lister toutes les variables : pour lister toutes les variables du data set source rapidement, le symbole étoile (asterik) fera le travail. Rien n’empêche d’ajouter d’autres variables à la suite.

Exemple : Dans l’exemple ci-dessous, toutes les variables du data set CLASS situé dans la bibliothèque SASHELP sont gardées. De plus, pour chaque observation, une variable EXTRA prend la valeur ‘TEST’.

proc sql;
select *, ‘TEST’ as extra
from sashelp.class;
quit;

3. Définir le data set source avec le mot-clé FROM

Dans chacun des exemples ci-dessus, un data set source a été introduit pas le mot-clé FROM.

4. Assigner un nom de data set avec CREATE

Par défaut aucun data set n’est créé : Avec un data step, il faut dans un premier temps créer le data set pour pouvoir ensuite l’imprimer. Avec PROC SQL, les informations sont automatiquement envoyées dans la fenêtre de destination (OUTPUT par exemple) si aucun nom de data set n’est donné avant la sélection des variables.

La structure de début de l’instruction centrale : Pour donner un nom de data set, il faut commencer l’instruction centrale par :

CREATE TABLE mon_nom_de_table AS…

Pourquoi un mot-clé TABLE ? : La présence du mot-clé TABLE se justifie par le fait que SAS est capable de créer plusieurs types de fichiers : les SAS data sets appelés aussi TABLE, et les VIEW. Pour débuter seules les TABLES nous intéressent, l’usage des VIEW étant beaucoup plus occasionnel.

Par ailleurs, sachez que les options du data set vues dans un data step (DROP, KEEP, RENAME, WHERE…)  s’appliquent également dans la PROC SQL mais seulement une fois le nouveau data set créé. Pour les curieux, il y a l’article « Je garde ou je jette ? les variables« .

proc sql;
create table class (drop=age) as
select *, ‘TEST’ as extra
from sashelp.class;
quit;

Depuis la version SAS 9, SAS demande explicitement d’éviter de créer un data set portant le même nom que le data set source. Si vous le faites, un message apparaîtra dans la log.

WARNING: This CREATE TABLE statement recursively references the target table. A consequence of this is a possible data integrity problem.

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

Quand compilation et exécution font la différence, un exemple

août 21, 2008

SAS effectue plusieurs lectures d’un programme. A la première lecture, c’est la compilation. A la seconde, c’est l’exécution. Connaître ces notions vous aidera à comprendre les exemples ci-dessous : pourquoi le premier code proposé ne fonctionne pas alors que les autres passent. Les fonctions PUT, VVALUE, le dictionnaire DICTIONARY.COLUMNS et le DATA _NULL_ serviront dans les exemples.

1. Un premier exemple avec la fonction PUT

Rappel sur la fonction PUT : Une fonction PUT permet de convertir une variable numérique en variable texte ou une variable texte en une autre variable texte. Elle est composée de deux paramètres. D’un côté, il y a la variable d’origine. De l’autre côté, il y a le format à appliquer sur cette variable d’origine. C’est donc la valeur sous forme formatée qui devient une valeur texte. Reportez vous à l’article « Convertir une variable caractère en numérique et inversement » pour plus de détails.

La phase de compilation : A la compilation, SAS  vérifie que la variable d’origine et le format associé sont tous les deux du même type. Il faut qu’une variable numérique est un format qui s’applique à une variable numérique. De manière identique, il faut un format caractère pour une variable texte.

La phase d’exécution : Si on ne connaît pas à l’avance le type de la variable, on peut avoir envie de définir une condition : si la variable est numérique applique tel format, sinon applique tel autre format. Hors une condition IF/THEN n’est visible par SAS qu’à la phase d’exécution.

En d’autres termes, SAS tentera d’appliquer un format numérique à une variable numérique avant de regarder si la fonction PUT est définie dans une condition.

data import_excel;
   retain type ‘CHAR’;
   study = ‘999’;
   output;
   study = ‘888’;
   output;
run;

data study_new;
   set import_excel;
   if type=‘NUM’ then study_new=put(study,best.);
   else study_new=study;
run;

Le résultat : Dans l’exemple, un format numérique (BEST.) est appliqué à une variable texte (STUDY). La première partie de la condition ne s’applique pas car le type de la variable n’est pas égal à NUM. Mais SAS cherche un format $BEST. qui n’existe pas. Il est obligé de s’arrêter là.

28 data study_new;
29 set ref;
30 if upcase(type)=’NUM’ then study_new=put(study,best.);
                                                  —–
                                                  48

ERROR 48-59: The format $BEST was not found or could not be loaded.

31 else study_new=study;
32 run;

NOTE: The SAS System stopped processing this step because of errors.
WARNING: The data set WORK.STUDY_NEW may be incomplete.
When this step was stopped there were 0 observations and 3 variables
.

Un exemple où le type de la variable est inconnu : en important un fichier EXCEL (PROC IMPORT/MIXED=YES), le type de la variable sous SAS peut-être inconnu. La variable sera caractère si une cellule contient du texte. Sinon, elle sera numérique.

2. Une solution rapide

Une fonction Vxxx: la fonction VVALUE retourne une valeur texte. Elle utilise le format associé à la variable en interne pour construire la valeur sous forme formatée. La nouvelle variable aura une longueur de 200.

data study_new;
   set ref;
   if type=’NUM’ then study_new=vvalue(study);
   else study_new=study;
run;

3. Une solution plus lourde mais pouvant s’appliquer à plusieurs variables

Une autre solution est de générer le code à exécuter. Si la variable est numérique, c’est l’instruction avec PUT qui apparaîtra, sinon c’est l’autre instruction. Cette approche fait appel à la notion de dictionnaire et de DATA _NULL_.

3.1 Créer un data set contenant le nom des variables et leur type à partir du dictionnaire (dictionary) appelé COLUMNS

La première étape consiste à créer un data set, nommé DICT_REF, contenant la variable STUDY et son type.

Choix du dictionnaire : Le dictionnaire (dictionary) COLUMNS est un data set de référence, créé de manière automatique par SAS. Il répertorie toutes les variables contenues dans tous les data sets de toutes les bibliothèques actives. Il contient donc une ligne par variable.

Chaque ligne du dictionnaire contient plusieurs informations caractérisant cette variable dont :

  • la bibliothèque d’origine (variable LIBNAME)
  • le data set d’origine (variable MEMNAME)
  • le nom de la variable (variable NAME)
  • le type de la variable (variable TYPE).

Prendre un sous-ensemble du dictionnaire : Ici seule la variable STUDY est utile. Elle provient du data set EXCEL_IMPORT sauvegardé de manière temporaire dans la bibliothèque WORK.

Mettre en majuscule : Le type de la variable est soit « num » soit « char », toujours en minuscule. Le nom de la variable peut avoir un mélange de majuscules et minuscules selon la manière dont est saisi le nom lors de sa création. Pour éviter des surprises, le nom de la variable et le type sont mis en majuscule grâce à la fonction UPCASE. L’avantage des dictionnaires, c’est qu’ils peuvent lister plus d’une variable.

proc sql;
   create table dict_ref as
      select upcase(name) as name,
             upcase(type) as type
      from dictionary.columns
      where upcase(libname) = ‘WORK’ and
            upcase(memname) = ‘EXCEL_IMPORT’ and
            upcase(name)    = ‘STUDY’;
quit;

3.2 Ecrire le code et l’appeler

La deuxième étape consiste à créer un programme nommé TMP_STUD.SAS donc le nom et l’emplacement est défini dans l’instruction FILENAME. Pour écrire dans ce fichier, l’instruction FILE est ajoutée dans le DATA _NULL_. Pour plus de précisions sur l’instruction PUT, reportez-vous à l’article « Ecrire un texte avec l’instruction PUT« .

filename stud ‘C:/sasref/tmp_stud.sas’;

data _null_;
   set dict_ref;
   file stud;
   put ‘data study_new;’;
   put @3 ‘set excel_import;’;
   if type=‘NUM’ then put @3 ‘study_new=put(‘ name ‘,9.);’;
   else put @3 ‘study_new=’ name ‘;’;
   put ‘run;’;
run;

%include stud;
filename stud;

L’écriture du programme est fonction des informations contenues dans le DICT_REF. Si plus d’une variable est sélectionnée, il faudrait préciser deux choses :

  • Les instructions DATA et SET ne sont à écrire qu’une fois. Elles sont ajoutées lors de la première boucle fait en interne autour de l’étape DATA via « if _N_=1 ».
  • L’instruction RUN apparaît une fois en fin de programme. Il faut donc l’ajouter une fois le dernier record du fichier de référence atteint. L’option END= de l’instruction SET crée une variable interne prenant la valeur 0 pour tous les records sauf le dernier où elle prend la valeur 1. La condition est donc basée sur cette variable.

filename stud ‘C:/sasref/tmp_stud.sas’;

data _null_;
   set dict_ref end=eof;
   file stud;
   if _N_=1 then
      do;
         put ‘data study_new;’;
         put @3 ‘set excel_import;’;
      end;

   if type=‘NUM’ then put @3 ‘study_new=put(‘ name ‘,9.);’;
   else put @3 ‘study_new=’ name ‘;’;
   if eof then
 put ‘run;’;
run;

%include stud;
filename stud;

Le code sauvegardé dans le fichier TMP_STUD.SAS se présente donc ainsi, vu que la variable STUDY est de type caractère.

data study_new;
   set excel_import;
   study_new=STUDY ;
run;

L’instruction %INCLUDE permet l’exécution de ce code.

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

Penser conditionnel (3/3) : CASE WHEN

juillet 3, 2008

Après avoir présenté les deux alternatives sous SAS dans un data step pour définir des conditions (La base du IF et SELECT), partons voir la syntaxe de PROC SQL pour définir une nouvelle variable.

1. Différences et ressemblances avec les IF et SELECT

Différences : A la différence des deux syntaxes du data step,…

  • … une seule action est possible pour une condition donnée : à savoir définir une et une seule nouvelle variable ou macro variable. Les conditions ne peuvent dont pas servir pour créer de nouveaux data sets ou imbriquer des conditions dans d’autres conditions.
  • … le nom de la nouvelle variable n’est donné qu’une seule fois.

Ressemblances :

  • Comme dans SELECT WHEN et ELSE IF,  seuls les records non déjà valides pour les cas listés précédemment sont relus.
  • Comme IF THEN, une dernière condition ELSE liste tous les cas non sélectionnés auparavant.

Un des avantages de la procédure SQL est de pouvoir définir une nouvelle variable à partir d’une ancienne variable portant le même nom. Alors que dans un data step, cela se passe en plusieurs étapes : 

  • Tout d’abord, la variable est renommée pour pouvoir se servir de son nom d’origine pour la nouvelle variable.
  • Ensuite la variable d’origine portant le nom créé uniquement pour l’occasion est supprimée.

Un autre avantage est l’usage des fonctions et du mot-clé DISTINCT.

2. La syntaxe CASE WHEN en exemple

2.1 Définir une variable à partir de plusieurs variables

Dans cet exemple, la variable POP prend au maximum 4 valeurs :

  • 1 si la variable AGE est supérieure à 13 et la variable SEX est égale à F ;
  • 2 si l’AGE est entre 0 et 13 et qu’il s’agit d’une femme ;
  • 3 si il s’agit d’un homme et que l’âge est une valeur positive ;
  • valeur manquante enfin pour les autres cas.

proc sql;
   /*create table class as*/
   select *, case
               when age > 13 and sex=‘F’ then 1
               when age >= 0 and sex=‘F’ then 2
               when age < 0 or sex not in (‘F’,‘M’) then .
               else 3
           end as pop
   from sashelp.class;
quit;

2.2 Utiliser les fonctions

Dans l’exemple ci-dessous, le critère est appliqué par groupe de valeurs définies dans la variable SEX. En regardant les données, deux groupes de valeurs apparaissent : ‘F’ pour les femmes et ‘M’ pour les hommes. La variable MAX_AGE1 est égale à ’13’ si, pour un groupe donné, la valeur maximale observée parmi les valeurs non manquantes de la variable AGE est 13. Sinon, MAX_AGE1 prend la valeur ‘N/A’.

proc sql;
   select sex, case
                 when max(age) = 13 then ’13’
                 else ‘N/A’
               end as max_age1
   from sashelp.class
   group by sex;
quit;

A présent, vu qu’il n’y a qu’un critère définissant la condition et qu’une seule valeur n’est attendue, ce critère peut être donné en début.

proc sql;
   select sex, case max(age)
                 when (13) then ’13’
                 else ‘N/A’
               end as max_age2
   from sashelp.class
   group by sex;
quit;

Mais, il est bon de se demander si un format ne serait pas plus adapté dans ce cas.

proc format;
  value max_age
   13    = ’13’
   other = ‘N/A’;
run;

proc sql;
   select sex, put(max(age),max_age.) as max_age2
   from sashelp.class
   group by sex;
quit

2.3 Créer une macro variable

Voici pour terminer un petit exemple où une macro variable POP est crée. Sa valeur dépend de l’âge maximum observé dans le data set.

proc sql; *noprint;
   select case
            when
max(age) > 13 then ‘>13’
            when max(age) >= 0 then ‘[0-13]’
            else ‘N/A’
          end into :pop
   from sashelp.class;
quit;

h1

Mes valeurs sont t’elles proches de la moyenne ? (écart-type)

juin 17, 2008

Pour résumer une série de mesures, il est courant de proposer la moyenne. Mais avoir une moyenne de 11 n’a pas le même sens selon que les valeurs soient dispersées entre 10 et 13, ou entre 0 et 20. Dans le premier cas, toutes les valeurs sont plus proches de la moyenne que dans le second cas. L’écart-type (ou standard deviation en anglais) reflète cette subtilité. Le statisticien aura pour rôle d’interpréter cette valeur. En comprenant le sens d’un écart-type,  le programmeur trouvera un outil supplémentaire pour vérifier la cohérence de ses résultats.

1. Des variables continues

La moyenne et l’écart-type sont des outils s’appliquant uniquement aux données numériques, suivant un ordre donné où l’écart entre chacune des valeurs potentielles est toujours le même. On peut donc parler de valeurs continues au sens large du terme.

Ainsi, l’âge d’une personne est une donnée continue qu’elle soit arrondie en années, en mois, en jours, en heure. La moyenne et l’écart-type auront une précision similaire.

2. Définition l’écart-type d’une population

L’écart-type ayant pour but de chiffrer l’écart entre les valeurs  et la moyenne. Il est donc logique de calculer la différence entre chaque valeur et la moyenne. Dans un premier temps, on a autant de différences que de valeurs. Dans un second temps, une valeur moyenne est extraite de ces différences.

1. Mettre au carré les différences : qui dit moyenne, dit somme des valeurs divisé par le nombre de valeur. Hors, la somme de valeurs positives et négatives s’annulent. Et dans notre cas, on a bien des valeurs inférieures et des valeurs supérieures à la moyenne. Dans le cas de l’écart-type, toutes les différences sont rendues positives en les multipliant par elles-mêmes (mises au carré).

C’est probablement pour des faciliter la résolution de calculs mathématiques que la mise au carré est préféré à la prise des valeurs absolu. Si vous avez une autre hypothèse, n’hésitez pas à en faire part.

2. Extraire la moyenne : on souhaite une moyenne. Donc la somme des différences (mises au carré) sont divisées par le nombre de différences. Si on s’arrête là, la statistique s’appelle la variance.

3. Exprimer la dispersion dans la même unité que la moyenne : A présent la somme des différences n’est plus dans la même unité que la moyenne. Pour parler dans la même unité, on prend la racine de l’ensemble du calcul.

3. Estimer l’écart-type d’une population à partir d’un échantillon

Contexte : il est fréquent de ne pas travailler sur la population qui nous intéresse mais sur un échantillon. Par exemple, si on mesure la pression artérielle systolique des patients d’une étude clinique pour savoir si la drogue a permis de la réduire, le statisticien n’est pas intéressé par la moyenne de ces patients mais bien celle de tous les personnes pouvant à l’avenir faire appel à cette drogue.

Le problème : De plus, les recherches statistiques ont permis de démontrer qu’en moyenne, la moyenne de tous les échantillons possibles est la même que celle de la population. Pour l’écart-type, c’est un peu moins simple, puisqu’il la moyenne des écarts-types de tous les échantillons n’est pas exactement égale à celle de la population.

La solution : Mais les chercheurs en statistique sont là ! Ils ont réussi à montrer qu’en enlevant 1 au nombre total de valeurs dans chacun des échantillons, on pouvait retrouver l’écart-type de la population (cf. des cours d’inférence qui semble si théorique au prime abord mais qui permettent d’accéder à l’arrière de la scène).

Vocabulaire : Et pour enrichir votre vocabulaire et surtout comprendre ces spécialistes sachez que si l’écart-type sera appelé un estimateur biaisé si le 1 n’est pas enlevé.

L’écart-type le plus utilisé est donc le second, celui où on divise les différences non pas par le nombre de différences mais le nombre de différences moins 1.

4. Comprendre la différence entre l’écart-type et l’erreur-type

Pour ceux qui auront une formation à composante statistique, vous pourrez être amené à expliquer en entretien ou à vos collègues la différence entre l’écart-type (standard deviation) et l’erreur-type (standard error). 

La première différence, c’est que l’écart-type s’applique à des données, alors que l’erreur-type s’applique à la statistique de la moyenne. 

A chaque fois, qu’un échantillon est pris, sa moyenne va servir à estimer la moyenne de la population. Bien-sûr, toutes les moyennes des échantillons ne sont pas identiques. Il existe une variabilité. Certains sont plus proches de la réalité que d’autre. Cette variabilité des résultats entre les échantillons est donnée par l’erreur-type. Ainsi un intervalle à l’intérieur duquel la moyenne de la population se tient pourra être estimé.

5. En langage mathématique, l’écart-type donne quoi ?

Pour désigner nos valeurs, le mathématicien utilise la lettre X.

  • Xi représente la ième valeur de l’échantillon. Si on a 15 valeurs alors i prend les valeurs de 1 à 15 ou plus généralement les valeurs de 1 à n.
  • La moyenne de ces Xi est symbolisée par un X avec une barre au dessus.

Pour calculer l’écart-type, on a parlé de trois étapes :

  1. sum(Xi-Xbar)2 Tout d’abord la différence entre chaque Xi et la moyenne X barre est calculée. Elles sont mises au carré. Puis, la somme de ces valeurs en est faite. On en profite pour préciser que les valeurs de i vont de 1 à n.
  2. sum(Xi-Xbar)2 / (n-1) Après, il s’agit de calculer la moyenne de ces valeurs
  3. racine(sum(Xi-Xbar)2 / (n-1)) Enfin, la statistique est convertie en une unité comparable à celle de la moyenne via la racine carré.

6. Et en langage SAS, comment trouver la valeur d’un écart-type ?

Dans tous les cas suivant, le dénominateur est n-1.

6.1 Fonction STD : La fonction STD (standard deviation) retourne la valeur de l’écart-type.

proc sql;
   select std(age) as std_age
   from sashelp.class;
quit;

6.2 Calcul manuel : dans un premier temps, la différence avec l’âge moyen est calculée pour chaque record. Chaque différence est mise au carré. Dans un deuxième temps, la somme de ces différences est divisée par le nombre de records moins 1. Enfin, la racine carrée du tout est prise.

proc sql;
   create table step1 as
      select (age-mean(age))**2 as step1
      from sashelp.class;
   select sqrt(sum(step1)/(count(*)-1)) as step2_3
      from step1;
quit;

6.3 Les procédures PROC MEANS et PROC SUMMARY : dans les exemples ci-dessous, j’ai volontairement choisi l’instruction ODS OUTPUT pour extraire les statistiques dans un data set.

proc means data=sashelp.class;
var age;
ods output Summary=proc_means;
run;

proc summary data=sashelp.class print;
   var age;
   ods output Summary=proc_summary;
run;

6.4 La procédure PROC UNIVARIATE : deux sorties fournies par la procédure UNIVARIATE sont indifféremment disponible.

proc univariate data=sashelp.class;
   var age;
   ods output Moments=proc_univ_opt1;
   ods output BasicMeasures=proc_univ_opt2;
run;

h1

Insérer quelques nouvelles observations

juin 9, 2008

Dans l’article ‘Ajouter des lignes en combinant des data sets’, nous avons vu comment ajouter des observations en joignant plusieurs data sets. Ici, il s’agit de voir comment ajouter manuellement un nombre limité d’observations en fin de data set. Deux méthodes sont proposées : un data step avec l’option END= et l’instruction OUTPUT d’une part et l’instruction INSERT INTO de PROC SQL d’autre part.

1. Passer par un data step pour ajouter une ou plusieurs observations

L’instruction SET dispose de l’option END=. Celle-ci permet de définir une nouvelle variable. On peut s’imaginer cette variable comme une variable binaire prenant une valeur zéro pour toutes les observations sauf la dernière. Il est courant de nommer cette variable EOF, acronyme pour END OF FILE (fin de fichier).

Dans un premier temps, toutes les observations sont lues et envoyées dans le data set de sortie avec OUTPUT.

Dans un second temps, on précise à SAS que si notre variable binaire a une valeur de 1, l’observation va être de nouveau envoyée dans le data set final après avoir changé les valeurs de son choix.

Lorsque la valeur prise par la variable binaire n’est pas précisée (if eof then…), SAS assume ‘if eof=1 then…’ ou en d’autres termes ‘si la condition est vraie alors…’. Pour information, l’inverse est ‘if not eof then…’ ou ‘if eof=0 then…’.

data class;
   set sashelp.class end=eof;
   output;
   if eof then
      do;
         name=‘SASREF’;
         sex=‘F’;
         age=18;
         height=.;
         weight=100;
         output;
         name=‘SASREF’;
         sex=‘M’;
         age=.;
         height=.;
         weight=.;
         output;
      end;
run;

Cette méthode reste très contraignante. Car il faut redéfinir tous les champs au risque sinon d’avoir une valeur de l’ancienne observation. La procédure SQL est véritablement conçue pour répondre à notre besoin.

2. L’instruction INSERT de la procédure PROC SQL

Deux syntaxes sont disponibles avec l’instruction INSERT selon qu’on liste seulement les valeurs, mais pour toutes les variables ou qu’on liste à la fois les variables et leur valeurs mais seulement celles qui nous intéresse.

Ma préférence va à la seconde solution, car elle évite des erreurs dans l’ordre d’affichage des valeurs. De plus, lorsqu’on relit un programme, il est facile de faire le lien entre une valeur et la variable à laquelle elle réfère.

 2.1 Lister toutes les valeurs pour une ligne d’observation sans le nom des variables : chaque nouvelle ligne d’observation est introduite par le mot-clé VALUES. Il n’y a donc pas besoin de délimiteur entre chaque ligne d’observation. Toutes les valeurs pour une observation donnée sont listées entre parenthèses et séparées par une virgule.

proc sql;
   insert into class
   values (‘SASREF2’,‘F’,18,.,100)
   values (‘REFSAS2’,‘M’,.,.,.);
quit;

 2.2 Lister les variables qui nous intéressent seulement : chaque nouvelle ligne d’observation est introduite par le mot-clé SET. Comme précédemment, il n’y a pas de délimiteur entre chaque ligne d’observation nouvellement définie. Les variables sont listées dans un ordre quelconque. Les variables non listées prennent une valeur manquante.

proc sql;
   insert into class
   set sex=‘F’, name=‘SASREF3’,age=18,weight=100
   set name=‘REFSAS3’, sex=‘M’;
quit;

Pour plus de précisions sur l’instruction INSERT, vous pouvez vous reporter à  la SAS Online Doc. : Insert Statement.

h1

Savoir interpréter une note liée au MERGE

juin 5, 2008

Dans un cas particulier, une jointure many-to-many avec un MERGE, SAS génère une note. Il est très important de savoir l’interpréter. En effet, il est fréquent de vouloir un FULL JOIN (PROC SQL) mais d’utiliser maladroitement le MERGE (Data Step). Je vous propose donc de voir la différence entre les deux approches.

Rappel : si dans chacun des deux data sets à fusionner, il y a des doublons pour les variables communes, il s’agit d’une jointure MANY-TO-MANY.

Données utilisées pour illustrer le sujet : voici deux data sets appelés FRST_MANY et SECOND_MANY avec chacun trois variables. Les variables X et Y sont présentes dans les deux data sets. Dans le premier data set, trois lignes d’observations ont pour valeur x=A, y=B. Dans le second data set, deux lignes d’observations ont pour valeurs x=A, y=B et deux autres x=B, y=D.

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

1. Premier Choix, le FULL JOIN de la PROC SQL : on veut que chaque ligne d’observations de FRST_MANY soit dupliquée s’il y a plusieurs lignes d’observations dans SECOND_MANY ayant les mêmes BY variables ; et inversement.

En d’autres termes, vu que x=A, y=B apparaît trois fois dans le premier data set et deux fois dans le second, il y aura six lignes d’observations après la fusion des jeux de données.

De même, vu qu’il y a une observation x=B, y=D dans le premier jeu et deux dans le second jeux, il y aura deux observations après la fusion et la variable a=5 du premier jeux sera dupliquée pour chaque ligne.

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

En terme de programmation SAS, cela donne :

proc sql;
   create table final as
   select a,
          coalesce(one.x,two.x) as x,
          coalesce(one.y,two.y) as y,
          b
   from   frst_many one
   full join
          second_many two
   on     one.x=two.x and
          one.y=two.y;
quit;

2. Second Choix, MERGE : pour chaque combinaison de BY variables, le nombre de lignes dans le data set final correspond au nombre de ligne le plus important parmi les deux data sets d’origine.

Dans ce cas, SAS génère la note suivante, très importante :

NOTE: MERGE statement has more than one data set with repeats of BY values.

Le mieux est encore de regarder l’exemple pour comprendre ce qui se passe.

Vu qu’il y a trois lignes d’observations pour x=A, y=B dans le premier jeu, et seulement deux dans le second, il y aura trois lignes d’observations au final pour ces BY variables. La deuxième et dernière valeur de la variable b du second data set (b=3) sera dupliquée pour les lignes supplémentaires.

Vu qu’il y a deux lignes d’observations pour x=B, y=D dans le second jeu et une seule dans le premier, la valeur de la variable a du premier data set (a=5) sera également dupliquée pour la ligne supplémentaire.

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

Pour obtenir ce résultat, il faut passer par un MERGe et si besoin trier les données :

proc sort data=frst_many;
   by x y;
run;

proc sort data=second_many;
   by x y;
run;

data final;
   merge frst_many
         second_many;
   by x y;
run;

 

Annexe : syntaxe pour créer les data sets et ainsi tester la PROC SQL et le MERGE.


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 4
A B 3
E F 6
;
run;