Techno-conseil sur SASMD 9
par Peter Eberhardt

Combien de fois avez-vous essayé de simplifier votre code à l'aide des instructions LINK/RETURN ?

Combien de fois vous êtes-vous arraché les cheveux en tentant de créer des fonctions de macros afin d'encapsuler la logique d'affaires ?

Combien de fois avez-vous proféré « Si seulement je pouvais appeler cette étape DATA en tant que fonction » ?

Si l'un de ces énoncés vous décrit, alors vous aimerez les nouvelles caractéristiques de la procédure PROC FCMP. Si aucun de ces énoncés ne vous décrit, alors vous avez vraiment besoin des nouvelles caractéristiques de PROC FCMP.

Ce que je suis sur le point de décrire n'est pas vraiment un techno-conseil, mais plutôt une brève introduction de la procédure SAS PROC FCMP et de sa nouvelle capacité permettant de créer vos propres fonctions à l'étape DATA et routines CALL à l'aide de la syntaxe de l'étape DATA. Si vous êtes statisticien, vous connaissez peut-être déjà PROC FCMP; un certain nombre de procédures SAS/STATMD et SAS/ETSMD avaient déjà la capacité d'appeler des fonctions créées à l'aide de PROC FCMP. Cette capacité s'étend maintenant au programmeur à l'étape DATA. Pour un bref aperçu de PROC FCMP, consultez la communication de Jason Secosky.

Vous pouvez créer des fonctions, comme l'illustre mon exemple. Vous pouvez également créer des routines d'appel (CALL). Vous pouvez passer par des tableaux (ARRAYS). Et vos fonctions peuvent même être récursives. Au bout du compte, PROC FCMP permet l'encapsulation créative de votre logique de programmation et d'affaires.

Pour commencer :
Vous aurez besoin d'une bibliothèque où stocker les fonctions. Dans mon exemple, j'utiliserai une bibliothèque WORK. Même si cela convient au moment de développer vos fonctions, vous devrez les conserver dans un emplacement permanent où elles seront accessibles à tous les utilisateurs. Vous devez également indiquer à SAS où trouver les fonctions; c'est ce que fait l'instruction OPTIONS CMPLIB=.

Exemple :
Je dois fréquemment effectuer la simple division de deux valeurs dans un jeu de données. Rien de plus simple, jusqu'à ce qu'on découvre que certaines des variables ont des valeurs manquantes de façon inattendue, ou une valeur de 0. Le code suivant montre « l'ancienne façon », soit en vérifiant explicitement les valeurs avant chaque division; même avec seulement deux divisions, le code
devient répétitif. La « nouvelle façon » utilise deux fonctions que j'ai créées pour effectuer les mêmes divisions; remarquez à quel point le nouveau code est beaucoup plus dépouillé.

* créez un jeu de données de test;
DATA wonkyData;
drop i;
do i = 1 to 100;
if mod(i,3) = 0
then divisor = 0;
else if mod(i,13) = 0
then divisor = .M;
else divisor = i;
dividend1 = (int(ranuni(1) * 10) * i ) + (int(ranuni(2) * 10) * i );
dividend2 = dividend1 * 2;
output;
end;
RUN;

* traitez les données;
* divByMissingResult réglé à .X manquant pour que ce soit plus facile à voir;
* un problème commun consiste à utiliser IF divisor = . ;
DATA oldWay;
set wonkyData;
retain divByZeroResult 0
divByMissingResult1 .X
divByMissingResult2 .F ;
drop divByZeroResult divByMissingResult1 divByMissingResult2;
* seulement 2 tests, mais il pourrait y en avoir plus;
if divisor = 0 then answer1 = divByZeroResult;
else if missing(divisor) then answer1 = divByMissingResult1;
else answer1 = dividend1 / divisor;

if divisor = 0 then answer2 = divByZeroResult;
else if missing(divisor) then answer2 = divByMissingResult2;
else answer2 = dividend2 / divisor;
run;

*----------------------------------------------;
* répétez, mais utilisez une fonction;
*----------------------------------------------;

* outlib= dit à FCMP où stocker les fonctions;
PROC FCMP outlib=work.funcs.math;

* cette fonction utilise des valeurs explicites pour divByZeroResult et divByMissingResult ;
FUNCTION divX(dividend, divisor, divByZeroResult, divByMissingResult);
if divisor = 0 then answer = divByZeroResult;
else if missing(divisor) then answer = divByMissingResult;
else answer = dividend / divisor;
return(answer);
ENDSUB;

* cette fonction utilise des valeurs fixes pour divByZeroResult et divByMissingResult;
FUNCTION div(dividend, divisor);
if divisor = 0 then answer = 0;
else if missing(divisor) then answer = .F;
else answer = dividend / divisor;
return(answer);
ENDSUB;
RUN;

* indique à SAS où chercher les fonctions;
options cmplib= work.funcs;
* code beaucoup plus simple;
DATA newWay;
set wonkyData;
answer1 = divX(dividend1, divisor, 0, .X);
answer2 = div(dividend2, divisor);
RUN;

* comparez les résultats;
PROC COMPARE BASE=oldWay COMPARE=newWay;
RUN;

SAS et tous les autres noms de produits ou services de SAS Institute Inc. sont des marques de commerce déposées de SAS Institute Inc. aux États-Unis et dans les autres pays. MD indique une homologation aux É.-U. Les autres noms de marque ou de produit sont des marques de commerce déposées ou des marques de commerce de leurs entreprises respectives. Tous droits réservés.