Posts Tagged ‘function’

h1

Passer d’un fichier de l’ODS TAGSET.EXCELXP à un data set SAS avec PROC IMPORT

août 15, 2008

Le programmeur SAS pourra recueillir des informations d’un non spécialiste en créer un fichier Excel. Il importe ensuite les données sous SAS.

Dans un précédent sujet « Mes 1ers pas avec ODS TAGSET.EXCELXP« , nous avons vu comment créer un fichier Excel, ou plutôt un fichier XML lisible sous Excel, à partir de la syntaxe d’ODS TAGSET.EXCELXP.

Maintenant, il s’agit de voir les limites de l’importation d’un fichier XML avec la procédure PROC IMPORT.

Exemple : Nous aurons un fichier LST_STUDY.XLS avec plusieurs feuillets, dont un nommé ‘2000’, comme exemple. Ce feuillet a deux colonnes : le numéro de l’étude (STUDY) et la variable à compléter manuellement (FLAG_EXTERNAL). La première ligne contient le nom de la colonne. La variable STUDY peut contenir des nombres et du texte.

1. PROC IMPORT a besoin de données Excel

Même si le fichier créé par ODS TAGSET.EXCELXP a une extension .xls, ce fichier est en fait un fichier en langage XML. Comment observer cette information ? Voici deux propositions :

  1. Ouvrez un fichier de ce type sous Excel et regarder le type lors que la fenêtre OUVRIR apparaît.
  2. Ouvrez le fichier dans un éditeur de texte

Afin d’utiliser la procédure PROC IMPORT, il faudra donc convertir le fichier en « enregistrant sous » et là choisir le standard Excel.

2. Les options de l’instruction PROC IMPORT

Préciser le nom du fichier d’entrée et celui de sortie : Dans l’instruction PROC IMPORT, le nom du fichier Excel est donné avec l’option DATAFILE=; celui du data set SAS est introduit par l’option OUT=. Pour rendre ce data set permanent, le nom de la bibliothèque précédera le nom du data set comme d’habitude.

Indiquer à SAS le type de fichier à lire : Selon l’environnement et la version Excel utilisée, l’option DBMS= variera. DBMS=XLS est l’option que j’utilise sous Unix et Windows pour Excel2003. Reportez-vous à la documentation en ligne PROC IMPORT Statement) pour connaître toutes les valeurs possibles de cette option.

Remplacer un fichier SAS existant : Si le data set SAS existe déjà, il ne sera pas remplacé, à moins d’ajouter l’option REPLACE.

%let resultats = C:/sasref;

proc import datafile = « &resultats./lst_study.xls »
            out      = lst_study
            dbms     = xls
            replace;
run;

3. Les Instructions sur les données sources

Après avoir vu les options de l’instruction PROC IMPORT, je vous propose trois instructions supplémentaires de la procédure (SAS Online Doc. : Data Source Statements)

Des noms de variables personnalisés avec GETNAMES=YES : SAS se sert de la première ligne du fichier Excel pour définir le nom des variables SAS à moins que l’instruction GETNAMES=NO soit ajoutée.

SAS considère les premières lignes du fichier pour identifier le type des variables sauf si MIXED=YES : Si on a une combinaison de caractères et chiffres, on peut se retrouver avec des valeurs manquantes. Par exemple, si vous avez une variable année contenant les valeurs 2008, 2007, <2007, il faudra importer les variables sous forme de caractère. L’instruction MIXED=YES fera le travail pour vous. 

Importer une feuille de calcul en particulier grâce à SHEET=: Par défaut, SAS importera la première feuille de calcul disponible dans le fichier Excel. Pour choisir un autre feuillet, son nom est donné entre guillemets dans l’instruction  SHEET=.

%let resultats = C:/sasref;

proc import datafile = « &resultats./lst_study.xls »
            out      = lst_study
            dbms     = xls
            replace;
   *getnames = yes;
   mixed    = yes;
   sheet    = ‘2000’;
run;

Vous trouverez plus d’informations sur la procédure PROC IMPORT dans l’aide en ligne : SAS Online Doc, The IMPORT Procedure.

4. Une spécificité de l’ODS TAGSET.EXCELXP à gérer

Lors de la création du fichier avec ODS TAGSET.EXCELXP, une ligne est parfois ajoutée après la dernière ligne de saisie. Cette ligne est incluse dans le data set SAS à l’importation. La raison pour laquelle cette ligne est saisie m’est inconnue.

Par contre, je vous propose un data step pour vous en débarrasser une fois le data set SAS créé. 

  • L’option END= de l’instruction SET sert à repérer la dernière ligne d’observations.
  • La fonction MISSING permet de ne supprimer la ligne que si notre variable STUDY est vide.

data lst_study;
   set lst_study end=eof;
   if eof and missing(study) then delete;
run;

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

2 syntaxes pour calculer un âge

avril 9, 2008

Quel âge à mon patient au début de l’étude ? Et mon client, quel âge a t-il lors de la signature de son contrat x ? Ces questions simples a priori peuvent vite devenir un casse-tête avec SAS. Je vous propose deux approches qui vous permettront d’aborder succinctement plusieurs fonctions. Les fonctions les plus facile à comprendre MONTH, DAY, YEAR, INT/FLOOR sont traitées sous forme de notes. Les fonctions de base pour le calcul de l’âge YRDIF et INTCK sont introduites dans des sections distinctes. On ne parlera pas ici de la manière de créer une date SAS.

Note : les fonctions YEAR, MONTH et DAY permettent de retrouver respectivement l’année, le mois et le jour d’une date SAS. Elles n’ont donc que la date comme paramètre.

Note : la fonction INT (pour le mot anglais « integer ») retourne la partie entière d’un nombre. La fonction FLOOR retourne l’entier inférieur. En rappel, « floor » signifie le sol en anglais. Dans le cas d’entier positif, les fonctions INT et FLOOR donnent la même valeur. Cela s’applique donc à l’âge.

Présentation du jeu de donné utilisé comme exemple : pour illustrer ce calcul, je vous propose en fin d’article un data set avec :

  • une variable pour la date de début (strt_dt),
  • une autre pour la date de fin (end_dt).

1. La fonction YRDIF : la fonction YRDIF contient trois paramètres : la date de début, la date de fin et le mode de définition des mois et années. Dans notre cas, on choisira des mois et années comme sur le calendrier et non des mois de 30 jours ou des années de 360 jours. Le troisième paramètre aura donc la valeur ‘ACT/ACT’ ou son alias ‘ACTUAL’.

yrdiff_val = yrdif(strt_dt,end_dt,‘ACTUAL’);

La valeur retournée est un nombre avec des virgules. Or il nous faut un nombre entier. Pour ne retirer que la partie entière, on ajoute la fonction INT.

int_yrdiff_val = int(yrdif(strt_dt,end_dt,‘ACTUAL’));

Maintenant, dans le cas particulier du jour anniversaire, on se rend compte que selon qu’il s’agit d’une année bissextile ou non, on a une valeur supérieure ou inférieure à un. On ajoute donc une condition pour ce cas particulier. Si les mois et jours sont identiques, on fait la soustraction entre l’année de fin et celle de début.

if month(strt_dt) = month(end_dt) and
   day(strt_dt) = day(end_dt)
   then age_m1 = year(end_dt)-year(strt_dt);
else age_m1 = int(yrdif(strt_dt,end_dt,‘ACTUAL’));

2. Une seule instruction : calculer la date en une seule instruction, c’est possible. Le calcul a été répertorié sur le forum SAS-L. Je vous propose de la détailler ici.

2.1 La fonction INTCK : la fonction INTCK avec pour premier paramètre ‘month’ a pour valeur minimale 0.  On rencontre ce cas lorsque les mois et année des deux dates coïncident. Pour le même mois un an après, SAS retournera une valeur de 12.

cnt_mois = intck(‘month’,strt_dt,end_dt);

2.2 Comparer les jours : si le jour de début est supérieur au jour de fin, le mois entier ne s’est pas écoulé. On enlève donc un mois au calcul précédent. Le résultat de la parenthèse (day(strt_dt) > day(end_dt) est soit 0, soit 1.

comp_jr = day(strt_dt) > day(end_dt);

2.3 S’exprimer en années : comme on veut le résultat en années et non en mois, le tout est divisé par 12.

2.4 Avoir un nombre entier : l’individu prenant un an de plus seulement quand l’âge est révolu, seule la partie entière nous intéresse. Une fonction INT ou FLOOR fera l’affaire.

age_m2 = int((intck(‘month’,strt_dt,end_dt)-(day(strt_dt)>day(end_dt)))/12);

3. Illustration avec des données : En plus des variables strt_dt (start date) et end_dt (end date), les observations sous regroupées en 4 catégories (variable flag).

  • Les dates anniversaires,
  • La veille et le lendemain de ces dates anniversaires.
  • Des dates de début et de fin identiques.

Des années bissextiles et des années de 365 jours sont incluses.

data strt_end;
  length flag $3;
  format strt_dt end_dt date9.;
  informat strt_dt end_dt date9.;
  input strt_dt end_dt flag $;
  datalines;
02APR1979 02APR1980 =1
02APR1980 02APR1981 =1
02APR1981 02APR1982 =1
02APR1979 01APR1980 <1
02APR1980 01APR1981 <1
02APR1981 01APR1982 <1
02APR1979 03APR1980 >1
02APR1980 03APR1981 >1
02APR1981 03APR1982 >1
02APR1979 02APR1979 =0
02APR1980 02APR1980 =0
02APR1981 02APR1981 =0
;
run;

Dans cette sortie, on trouve les dates de début et de fin. Puis, on distingue d’un côté les deux variables illustrant la méthode 1 et ensuite celles de la méthode 2.

                   __METHODE 1__ ____METHODE 2____
                   yrdiff_ age_ cnt_ comp par_ age
strt_dt    end_dt   val     m1  mois _jr annee  m2


02APR1979 02APR1980 1.00205  1  12   0  1.00000  1
02APR1980 02APR1981 0.99795  1  12   0  1.00000  1
02APR1981 02APR1982 1.00000  1  12   0  1.00000  1

02APR1979 01APR1980 0.99932  0  12   1  0.91667  0
02APR1980 01APR1981 0.99521  0  12   1  0.91667  0
02APR1981 01APR1982 0.99726  0  12   1  0.91667  0

02APR1979 03APR1980 1.00478  1  12   0  1.00000  1
02APR1980 03APR1981 1.00069  1  12   0  1.00000  1
02APR1981 03APR1982 1.00274  1  12   0  1.00000  1

02APR1979 02APR1979 0.00000  0   0   0  0.00000  0
02APR1980 02APR1980 0.00000  0   0   0  0.00000  0
02APR1981 02APR1981 0.00000  0   0   0  0.00000  0

h1

Faire des petits avec l’instruction OUTPUT

mars 1, 2008

bouchons_fr.jpg 

Imaginez devoir créer plusieurs fois le même tableau avec des fréquences, des tests statistiques, etc. Pourquoi plusieurs fois ? Parce que chaque tableau inclus une population différente.

1. Une observation par tableau

Dans le cas le plus simple, chaque observation n’apparaît que dans un seul des tableaux à produire. Une variable identifie le tableau auquel l’observation est allouée et l’instruction BY utilise cette variable avec la procédure report ou autre.

Voici un exemple où les données sont publiées par pays dans des tableaux distincts. L’utilisation d’une valeur numérique pour le pays est optionnelle mais ajoute de la flexibilité pour trier les données.

proc format;
value cntry
1=’Chine’
2=’Malaisie’;
run;

proc report data=lab;
by cntry;
columns test cnt;

format cntry cntry.;

run;

2. Plusieurs tableaux pour une observation

Exemple : maintenant voici deux exemples des essais cliniques pour lesquels une observation peut servir à plusieurs tableaux.

  • Construire un tableau pour la population ‘per protocol’, un pour ‘intent-to-treat’ et un pour ‘safety’ : un patient valide par protocole est aussi pour l’analyse de sûreté mais tous les patients valides pour la sûreté ne répondent pas forcément aux critères du protocole.
  • créer un tableau par zone géographique : un tableau pour tous les pays couverts par l’étude clinique, un tableau par pays et un par centre dans un pays donné.

Dans ces deux cas, une observation peut être utilisée par plusieurs tableaux. La méthode précédente n’est plus suffisante. Voici la solution que je vous propose :

  • 1er tableau : extraire les observations correspondant au premier tableau en utilisant une condition listant le critère de sélection si besoin.
  • rendre ces observations uniques en ajoutant une nouvelle variable avec un numéro unique
  • sauvegarder ces observations dans un jeu de données
  • 2ème tableau : extraire les observations pour le second tableau
  • donner un numéro différent à ces observations
  • ajouter ces observations à celles précédemment sélectionnées
  • 3ème tableau : etc.

Dans l’exemple qui suit, quatre tableaux différents sont à produits. On intercalera le code suivant entre les procédures format et report mentionnées précédemment.

data lab;
set lab;
grp=1;
output;
if cntry=’CN’ then
do;
grp=2;
output;
end;
else if cntry=’MY’ then
do;
grp=3;
output;
end;
if cntr=1 and cntry=’CN’ then
do;
grp=4;
output;
end;
run;

3. Créer plusieurs jeux de données

L’instruction OUTPUT dirige, par défaut, les données vers le jeu de données nommé dans l’instruction DATA. Parfois, il faut créer plusieurs data sets à partir à partir d’un seul jeu de données. Par exemple, on créera un data set contenant les patients inclus dans l’étude et un autre ceux exclus. Pour cela, il suffit de nommer les nouveaux jeux de données dans l’instruction DATA et de rappeler leur nom dans chaque instruction OUTPUT.

data incl excl;
set pat_lst;
if missing (randomno) then output excl;
else output incl;
run;

4. Créer une variable numérique avec des valeurs continues

En combinaison avec une boucle, l’instruction OUTPUT permet de générer des nombres. Par exemple, j’ai eu besoin de tester une macro via un data set listant toutes les valeurs ASCII imprimables. Ces valeurs ont un numéro allant de 32 à 126. La fonction BYTE retrouve la valeur ASCII avec ces numéros.

data ascii (drop=i);
do i=31 to 126;
ascii=byte(i);
output;
end;
run;

Autres lectures : vous pouvez étendre votre lecture sur l’instruction DO et DELETE. L’instruction DO dispose d’un mot-clé BY. L’instruction DELETE est, quant à elle, l’inverse de l’OUTPUT.

h1

Fini le Moyen-Age pour compter les mots

février 20, 2008

ulrepforte.jpg 

Vous avez une variable composée d’un ou plusieurs mots pour chaque observation, le tout séparé par des espaces, des virgules ou autre. Et vous voulez savoir combien il y a de mots. Avec SAS 9, il est très rapide de trouver le nombre de mots. Avec SAS 8.2, il faut pallier à l’absence de la fonction COUNT. Dans les deux cas, l’idée est la même : on compte les délimiteurs et on ajoute 1.

Prenons le cas des espaces. Dans un premier temps, on enlève les blancs de début et de fin de chaîne. Puis on enlève les doublons entre les mots. Enfin on compte les espaces restants. Il faudra aussi tenir compte du fait qu’une observation puisse ne contenir aucune valeur.

Voici les données qui serviront d’exemple :

data ds_orig;
   x=‘ AB CD GH ‘;
   output;
   x=‘ ‘;
   output;
   x=‘AB ‘;
   output;
run;

1. Avec SAS 9, un calcul rapide : il faudra faire appel à:

  • la fonction STRIP pour les blancs aux extrémités,
  • la fonction COMPBL pour enlever les blancs doublons,
  • la fonction COUNT  pour compter le nombre d’occurrence d’un caractère donné,
  • la fonction MISSING ou un simple = ‘ ‘ pour savoir si la chaîne est vide.

Voici un exemple avec des espaces pour délimiteur.

data sas9 ;
   set ds_orig;
   if x = ‘ ‘ then nb_mots = 0;
   else nb_mots=count(compbl(strip(x)),‘ ‘) + 1;
run;

2. Avec SAS 8.2, il faut contourner l’absence de la fonction COUNT: il existe plusieurs options dont celle-ci utilisant une boucle DO WHILE

  • Les fonctions TRIM et LEFT remplacent la fonction STRIP.
  • Les fonctions COMPLB et MISSING restent valide.
  • Une boucle remplacera la fonction COUNT.

On extrait le 1er mot de la chaîne, puis le 2nd, etc. via la fonction SCAN. Si la valeur trouvée est une valeur manquante, alors le nombre de mots est égal au nombre du boucles sans valeur manquante, c’est-à-dire le nombre de boucles actuel moins 1. Le compteur, ici nommé i, est réinitialisé pour arrêter la boucle DO WHILE.


data sas8_2;
   set ds_orig;
   if missing(x)then cnt_mots=0;
   else
      do;
         z=compbl(trim(left(x)));
         i=1;
         do until (i=0);
         if missing(scan(z,i,‘ ‘))then
            do;
               cnt_mots=i-1;i=->1;
               end;
               i=i+1;
            end;
         drop i z;
   end;
run;