Annonce

Bienvenue sur le site support de mes ouvrages d'introduction à SAS

La 4ème édition de mon ouvrage est disponible depuis le 11 avril 2019 !

Où trouver cet ouvrage ?


#1 20-03-2019 12:08:46

SAS-SR
Administrateur
Lieu: Université d'Orléans
Date d'inscription: 01-09-2008
Site web

extraire...

La semaine dernière, lorsque j'ai conclu le sujet des beaux mercredis consacré à la conjecture de Syracuse, j'ai indiqué que je n'avais vraiment pas d'idée pour le prochain sujet... et c'était vrai...

et le lendemain, le téléphone a sonné... Un de mes anciens étudiants était au bout du fil avec une question amusante que je m'empresse ici de transformer en sujet des beaux mercredis...

Le programme suivant crée une table qui nous pose un petit problème :

Code:

data test;
input pos texte $25.;
cards;
2 abc,def,ghijk,lm,nop
4 abcd,efg,hi,jk,lmn,op
3 ab,cd,efgh,i,jklmno,p
;

la question est simple : je veux pouvoir disposer d'une nouvelle variable qui, partant de la modalité de TEXTE, aura une modalité égale à un extrait de la modalité de TEXTE, de la première position, à la position qui précède le Xème délimiteur (la virgule dans notre cas).

ce Xème délimiteur est saisi au moyen de la variable POS

Ainsi, pour la première observation, la modalité de cette nouvelle variable devra être abc,def
abcd,efg,hi,jk pour la deuxième observation
et ab,cd,efgh pour la troisième.

Vous effectuerez cette tâche au moyen d'une étape DATA puis, créerez une fonction (qui effectuera cette même tâche) au moyen de PROC FCMP.

amusez vous bien et à la semaine prochaine...

Hors ligne

 

#2 27-03-2019 16:20:31

SAS-SR
Administrateur
Lieu: Université d'Orléans
Date d'inscription: 01-09-2008
Site web

Re: extraire...

pour obtenir le résultat souhaité, il faut extraire (SUBSTR) de la première position dans la modalité de la variable TEXTE à la position du xème délimiteur (la virgule dans notre cas) auquel on aura retiré 1.

si la seconde virgule est présentée en 8ème position, vous allez vouloir extraire de la position 1 à la position 7 pour obtenir, pour la première observation le résultat souhaité.

la question est donc de trouver la position de la xème virgule... Il n'y a pas de fonction qui vous permette de trouver directement cette position mais on peut ruser en mobilisant la fonction FIND.

Code:

temp=FIND(texte,',');

temp aura pour valeur la position de la première virgule dans la modalité.

Code:

temp=FIND(texte,',',temp+1)

temp aura maintenant pour valeur la position de la seconde virgule dans la modalité. Si vous avez compris le principe, il n'y a plus qu'à...

Code:

data test2;
   set test;
   temp=0;
   do i=1 to pos;
     temp=find(texte,",",temp+1);
   end;
   resultat=substr(texte,1,temp-1);
run;

il faut déjà donner une valeur à la variable TEMP : à la première exécution de la boucle, puisque TEMP est argument de la fonction FIND, si vous n'attribuez pas de valeur à TEMP, alors elle est valeur manquante et la fonction FIND renverra une erreur d'exécution puisqu'un de ses arguments est valeur manquante.

Construisons maintenant au moyen de PROC FCMP une fonction EXTRAIT qui réalisera la même tâche :

Code:

proc fcmp outlib=sasuser.funcs.temp;
   function extrait(chaine $,dlm $,pos) $ ;
   temp=0;
      do i=1 to pos;
         temp=find(chaine,dlm,temp+1);
      end;
   return(substr(chaine,1,temp-1));
   endsub;
run;

Si vous avez su l'écrire dans une étape DATA, vous saurez la construire avec PROC FCMP.

l'instruction :

Code:

function extrait(chaine $,dlm $,pos) $ ;

indique que nous allons construire une fonction caractère (signe $ à la fin de l'instruction) qui admet trois arguments : CHAINE, argument caractère (d'où le signe $), DLM, argument caractère et POS argument numérique.

La suite est identique à la programmation de notre étape DATA.

Pour utiliser ensuite votre fonction :

Code:

options cmplib=sasuser.funcs;

data test2;
   set test;
   verif=extrait(texte,',',pos);
run;

comme vous avez lu la section 3.2.9, vous savez qu'il faut obligatoirement utiliser l'instruction globale OPTION qui précède l'étape data.

Si vous regardez le résultat, vous constaterez qu'on obtient bien le résultat souhaité.

pour la semaine prochaine, voici quelques auxquelles vous allez devoir réfléchir...

1- quelle est la longueur de la variable créée avec notre fonction EXTRAIT ?
2- la variable TEXTE est de longueur 25, ce serait pas mal que la variable créée par la fonction EXTRAIT ait la même longueur que la variable TEXTE (attention, je ne veux pas une bidouille "pour une fois", je souhaite un "truc" qui me garantisse que, quelque soit la longueur de la variable TEXTE, la variable créée par la fonction EXTRAIT aura toujours une longueur identique à la variable citée en premier argument.
3- et si la variable TEXTE est d'une longueur disons égale à 50, que se passe-t-il ? peut-on imaginer quelque chose qui puisse nous protéger (un peu...) de ce petit problème ?

amusez vous bien

Hors ligne

 

#3 03-04-2019 09:33:57

SAS-SR
Administrateur
Lieu: Université d'Orléans
Date d'inscription: 01-09-2008
Site web

Re: extraire...

répondons aux diverses questions posées la semaine dernière...

1- la longueur de la variable crée par notre fonction EXTRAIT ? 33

la question 1bis, c'est : mais pourquoi ?

et bien je n'en ai aucune idée... et n'ai trouvé aucune information sur le sujet. Ce 33 semble être une longueur par défaut utilisée par SAS lorsque la programmation dans PROC FCMP ne donne aucune information à SAS.

reprenons un programme que vous connaissez sûrement :

Code:

PROC FCMP OUTLIB=SASUSER.FUNCS.TEMP;
   FUNCTION divisee(num,deno) $ ;
      IF NMISS(num,deno)>0 OR deno=0 THEN RETURN("non applicable");
      ELSE RETURN(COMPBL(num||" / "||deno||" = "||INT(num/deno)||
            " reste "||ABS(MOD(num,deno))));
   ENDSUB;
RUN;

je l'ai un peu modifié en retirant la longueur par défaut (60) que j'avais précisée après l'option $ de l'instruction FUNCTION.

Si vous utilisez cette fonction, vous constaterez que la longueur de la variable créée est égale à 14. Pourquoi 14 ? parce que "non applicable" s'écrit avec 14 caractères.

avec le programme suivant :

Code:

PROC FCMP OUTLIB=SASUSER.FUNCS.TEMP;
   FUNCTION diviseee(num,deno) $ ;
      IF NMISS(num,deno)=0 THEN RETURN("Zéro");
      ELSE IF deno=0 THEN RETURN("non applicable");
      ELSE RETURN(COMPBL(num||" / "||deno||" = "||INT(num/deno)||
            " reste "||ABS(MOD(num,deno))));
   ENDSUB;
RUN;

les variables créées auront une longueur de 4 puisque la première modalité qui apparaît dans la définition de la fonction est de longueur 4 : les choses fonctionnent donc comme dans une étape data : c'est le premier qui a parlé qui a raison.

Revenons maintenant à notre fonction EXTRAIT : comme le programme ne contient aucune information, il faut bien que SAS décide de la longueur de la variable qui sera créée par la fonction, et, par convention, il semblerait que ce soit 33.

Passons à notre question 2 :

2- la variable TEXTE est de longueur 25, ce serait pas mal que la variable créée par la fonction EXTRAIT ait la même longueur que la variable TEXTE

si vous me dites : il suffit d'ajouter un 25 après le $ de l'instruction FUNCTION, je vous répondrai que c'est de la bidouille... si votre variable TEXTE est de longueur 12, la variable
que vous allez créée au moyen de la fonction EXTRAIT sera toujours de longueur 25...

je vais moi aussi bidouiller mais de façon plus "subtile"...

Code:

data test2;
   set test;
   verif=texte;
   verif=extrait(texte,',',pos);
run;

si c'est la fonction EXTRAIT qui définit la longueur de VERIF, pour empêcher cela, il suffit de définir la longueur de VERIF avant d'utiliser EXTRAIT...

3- et si la variable TEXTE est d'une longueur disons égale à 50, que se passe-t-il ? peut-on imaginer quelque chose qui puisse nous protéger (un peu...) de ce petit problème

testons...

Code:

data test;
input pos texte $84.;
cards;
10 abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop
11 abcd,efg,hi,jk,lmn,op,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop
12 ab,cd,efgh,i,jklmno,p,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop
;
options cmplib=sasuser.funcs;
data test2;
   set test;
   verif=extrait(texte,',',pos);
run;

la variable VERIF et de longueur 33 ce qui est insuffisant pour stocker la vraie modalité que devrait prendre cette variable.

essayons maintenant avec notre petite ruse :

Code:

data test2;
   set test;
   verif=texte;
   verif=extrait(texte,',',pos);
run;

même résultat : même si la variable VERIF est de longueur 84, la fonction ne donne pas le bon résultat et ne peut pas aller lire au delà de 33 caractères.

notre seule possibilité, c'est d'agir dans PROC FCMP en précisant une valeur arbitrairement longue en option de FUNCTION, puis, dans l'étape DATA, fixer au moyen de notre petite ruse, la longueur de la variable à créer à la valeur adéquate.

Code:

proc fcmp outlib=sasuser.funcs.temp;
   function extrait(chaine $,dlm $,pos) $ 200 ;
   temp=0;
      do i=1 to pos;
         temp=find(chaine,dlm,temp+1);
      end;
   return(substr(chaine,1,temp-1));
   endsub;
run;

data test;
input pos texte $84.;
cards;
10 abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop
11 abcd,efg,hi,jk,lmn,op,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop
12 ab,cd,efgh,i,jklmno,p,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop,abc,def,ghijk,lm,nop
;
options cmplib=sasuser.funcs;
data test2;
   set test;
   verif=texte;
   verif=extrait(texte,',',pos);
run;

et vous obtenez le bon résultat (tant que la variable TEXTE présente une longueur inférieure à 200...)

Ce sujet des beaux mercredis est maintenant terminé - à la semaine prochaine pour un nouveau sujet

Hors ligne

 

Pied de page des forums

Propulsé par FluxBB
Traduction par FluxBB.fr
Flux RSS