Paper 2014-002
MACROS EN SAS
JUAN RADHAMES
G
ÓMEZM
ARTÍNEZR
ESUMEN(A
BSTRACT)
El macro lenguaje de SAS es una de las herramientas más poderosas que provee SAS. Este lenguaje si es utilizado de una manera correcta nos permite la realización de una serie de actividades que de otra forma implicarían la escritura de un sinnúmero de líneas de código y sobre todo la eficientización de procesos.
En este documento presentamos el concepto de macros y macro variables, como pueden ser utilizadas, así como también dos ejemplos puntuales de cómo los macros nos pueden ayudar en términos de eficiencia y estandarización.
Al concluir este documento pretendemos que usted conozca la diferencia entre una macro variable y un macro y la manera en que estos pueden interactuar para la generación de un programa eficiente.
C
ONCEPTOSMacros, macro variables.
MACROS
EN
SAS
CONCEPTO DE MACRO
Una macro (del griego μακρο, makro, que significa ‘grande’) - es una abreviatura de macroinstrucción- y aparte es una serie de instrucciones que se almacenan para que se puedan ejecutar de manera secuencial mediante una sola llamada u orden de ejecución. Dicho de otra manera, una macroinstrucción es una instrucción compleja, formada por otras instrucciones más sencillas. Esto permite la automatización de tareas repetitivas. (Wikipedia).
Más propiamente dentro del lenguaje, SAS provee una herramienta para la utilización de macros que permite reducir la cantidad de texto que debemos escribir para realizar tareas comunes. El procesador de macros lee el macro lenguaje y transforma el código ya revisado en un programa de SAS.
En SAS existen dos formas en las que nos referimos a los macros:
Las macro variables, las cuales se pueden referenciar bajo la siguiente nomenclatura &name. Y los macros que pueden ser referenciados utilizando la nomenclatura %name.
MACRO VARIABLES
Las macro variables se utilizan en SAS para reemplazar cadenas de texto dentro del código. La manera mas simple de definir una macro variable es utilizando la sentencia %LET.
%LET texto = Esto es una prueba %PUT el valor de texto es &texto.
En este ejemplo al hacer referencia a la variable texto, lo que se imprime en el log de SAS es: el valor de texto es Esto es una prueba.
Otra manera de crear macro variables dentro de un data step es utilizando la expresión SYMPUT. Al llamar a esta expresión debemos pasar dos parámetros, el primero el nombre la macro variable que queremos crear y asignar un valor y el segundo parámetro es el valor a asignar.
Es bueno destacar que el valor de la macro variable solo estará disponible luego de ejecutado el data step. DATA _NULL_;
Fecha = today();
Call symput(“fecha”, fecha); RUN;
MACROS
Los macros son programas compilados que pueden ser llamados dentro de un programa de SAS o desde la línea de comandos de SAS. Al igual que con las macro variables, los macros se utilizan para generar texto. Sin embargo los macros poseen cualidades adicionales como:
Los macros pueden tener sentencias de programación que nos permiten indicar cómo y cuando el texto va a ser generado.
Los macros aceptan parámetros. Estos nos permite generar macros genéricos que pueden tener múltiples usos.
Para que un macro pueda ser compilado, se debe generar una definición de macro. La forma general para esta definición es la siguiente:
%MACRO nombre_macro; <texto_macro>
%MEND <nombre_macro>;
Donde nombre_macro es el nombre único de SAS que identifica el macro y texto_macro es cualquier combinación de sentencias, llamadas a macros, expresiones o constantes de texto.
%MACRO primer_macro; PROC PRINT DATA=SALES;
TITLE1 “TESTING MY FIRST MACRO”; %MEND primer_macro;
%primer_macro;
POR QUE UTILIZAR MACROS
La utilización de macros provee un sinnúmero de ventajas dentro de las que podemos mencionar; Generación automática de código SAS.
Reducen el esfuerzo requerido para leer y escribir código SAS. Reutilización de código.
Permite la utilización de parámetros.
Permiten controlar el flujo de ejecución de un programa, dentro de un macro podemos utilizar sentencias iterativas y de condición que abarcan a más de un data step.
Eficientizacion de Código.
EJEMPLOS DE MACROS EN SAS
Reporte personalizado a una lista de correos.
%macro reporte(codigo,nombre,email); %ventas(&codigo.);
%let mailTo = "&email.";
%let mailFrom = "[email protected]"; %let mailCC = "[email protected]";
filename mymail EMAIL from = &mailFrom.
To = (&mailTo.) CC = &mailCC.
Subject = "Reporte" ct ='text/html' ;
ods html body=mymail style=seaside rs=none ;
ods html text='<div align="left">Buenos Dias </div>'; ods html text="<div>Sres. &nombre. </div>";
ods html text='<br>'; ods html text='<br>';
ods html text='<div align="left">Distinguido Cliente, </div>'; ods html text='<br>';
ods html text="<div align=>En cumplimiento los acuerdos realizados, anexo los reportes de ventas al <b> &mesano. </b>.
ods html text="<div align=>Cualquier duda o sugerencia acerca del Reporte por favor canalícenla a través de su representante</div>";
ods html text="<br><br>";
ods html text="<div align=>Saludos!</div>"; ods html text="<br><br>";
proc report data=sales HEADLINE HEADSKIP nowd missing split="\"
style(header)=[foreground=white background=crimson bordercolor=black] style(column)=[bordercolor=black ];
columns nombre (ventas, MES_BO); DEFINE nombre / GROUP 'Cliente'; DEFINE Mes / across 'Mes';
define ventas / Ventas Brutas' format=comma10.2; title1 '<b>Ventas Brutas </b>';
run; ods html close; quit; %mend; DATA WORK.correo_clientes; LENGTH Codigo $ 4 Nombre $ 64 Estatus $ 8 email $ 105 ; FORMAT
codigo $CHAR4. Nombre $CHAR64. Estatus $CHAR8. email $CHAR105. ; INFORMAT codigo $CHAR4. nombre $CHAR64. Estatus $CHAR8. email $CHAR105. ; INFILE &ruta..correo_clientes.txt" DELIMITER='09'x LRECL=400 MISSOVER DSD firstobs=2; INPUT Codigo $ Nombre $ Estatus $ email $ RUN; proc sql;
select count(codigo) into :cantidadclientes
from correo_clientes(where=(^ missing(email))); %let cantidadClientes = & cantidadclientes;
quit; proc sql;
select codigo, nombre_dealer, email
into :codigo1 - :codigo& cantidadclientes, :nombre1 - :nombre& cantidadclientes., :email1 - :email& cantidadclientes.
from correo_clientes (where=(^ missing(email))); quit;
%macro enviaEmail;
%do i=1 %to & cantidadclientes; %let codigo = &&codigo&i; %let nombre = &&nombre&i; %let email = &&email&i;
%reporte(&codigo.,&nombre.,&email.); %end;
%mend;
Ejemplo de Eficiencia de Código
%let enero2013 = work.SALE_EST_JAN2013; %let febrero2013 = work.SALE_EST_FEB2013; %let marzo2013 = work.SALE_EST_MAR2013; %let abril2013 = work.SALE_EST_APR2013; %let mayo2013 = work.SALE_EST_MAY2013; %let junio2013 = work.SALE_EST_JUN2013; %let julio2013 = work.SALE_EST_JUL2013; %let agosto2013 = work.SALE_EST_AUG2013; %let septiembre2013 = work.SALE_EST_SEP2013; %let octubre2013 = work.SALE_EST_OCT2013; %let noviembre2013 = work.SALE_EST_NOV2013; %let diciembre2013 = work.SALE_EST_DEC2013; %let enero2014 = work.SALE_EST_JAN2014; %let febrero2014 = work.SALE_EST_FEB2014; %let marzo2014 = work.SALE_EST_MAR2014; %let Abril2014 = work.SALE_EST_APR2014; %let Mayo2014 = work.SALE_EST_MAY2014; %let junio2014 = work.SALE_EST_JUN2014; %let julio2014 = work.SALE_EST_JUL2014;
data resultado; set ventas; run;
proc sql noprint;
create table estatus_enero2013 as
select a.*, b.sale_status as estatus_enero2013
from resultado a
left join &enero2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_febrero2013 as
select a.*, b.sale_status as estatus_febrero2013
from estatus_enero2013 a left join &febrero2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_marzo2013 as
select a.*, b.sale_status as estatus_marzo2013
from estatus_febrero2013 a left join &marzo2013. b on a.cuenta = b.cuenta and a.producto = b.producto
and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_abril2013 as
select a.*, b.sale_status as estatus_abril2013
from estatus_marzo2013 a left join &abril2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_mayo2013 as
select a.*, b.sale_status as estatus_mayo2013
from estatus_abril2013 a left join &mayo2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_junio2013 as
select a.*, b.sale_status as estatus_junio2013
from estatus_mayo2013 a left join &junio2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_julio2013 as
select a.*, b.sale_status as estatus_julio2013
from estatus_junio2013 a left join &julio2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_agosto2013 as
select a.*, b.sale_status as estatus_agosto2013
from estatus_julio2013 a left join &agosto2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan;
quit;
proc sql noprint;
create table estatus_septiembre2013 as
select a.*, b.sale_status as estatus_septiembre2013
from estatus_agosto2013 a left join &septiembre2013. b on a.cuenta = b.cuenta
and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_octubre2013 as
select a.*, b.sale_status as estatus_octubre2013
from estatus_septiembre2013 a left join &octubre2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_noviembre2013 as
select a.*, b.sale_status as estatus_noviembre2013
from estatus_octubre2013 a left join &noviembre2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_diciembre2013 as
select a.*, b.sale_status as estatus_diciembre2013
from estatus_noviembre2013 a left join &diciembre2013. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_enero2014 as
select a.*, b.sale_status as estatus_enero2014
from estatus_diciembre2013 a left join &enero2014. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
create table estatus_febrero2014 as
select a.*, b.sale_status as estatus_febrero2014
from estatus_enero2014 a left join &febrero2014. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_marzo2014 as
select a.*, b.sale_status as estatus_marzo2014
from estatus_febrero2014 a left join &marzo2014. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_abril2014 as
select a.*, b.sale_status as estatus_abril2014
from estatus_marzo2014 a left join &Abril2014. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_mayo2014 as
select a.*, b.sale_status as estatus_mayo2014
from estatus_abril2014 a left join &Mayo2014. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
proc sql noprint;
create table estatus_junio2014 as
select a.*, b.sale_status as estatus_junio2014
from estatus_mayo2014 a left join &junio2014. b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
Código Optimizado proc sql;
create table estructura as
select MEMNAME,
input(substr(MEMNAME, 13), monyy7.) AS FECHA format=date9.,
compress(put(input(substr(MEMNAME, 13), monyy7.), espdfmn10.) ||
put(year(input(substr(MEMNAME, 13), monyy7.)), 4.)) AS nombre_var from dictionary.tables
where libname = 'WORK' AND
memname like 'SALE_EST_%'
ORDER BY input(substr(MEMNAME, 13), monyy7.);
quit;
proc sql noprint;
select count(*) INTO :Cant_Tablas
from estructura ; select memname, nombre_var into :tabla1-:tabla%left(&cant_tablas), :nombre1-:nombre%left(&Cant_tablas) from estructura; quit; %macro status ;
%do i=1 %to &cant_tablas; %if &i=1 %then %do;
proc sql noprint;
create table estatus_venta as
select a.*, b.sale_status as estatus_&&nombre&i from resultado a
left join WORK.&&tabla&i b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
%end;
%else %do;
proc sql noprint;
create table estatus_venta as
select a.*, b.sale_status as estatus_&&nombre&i from estatus_venta a
left join work.&&tabla&i b on a.cuenta = b.cuenta and a.producto = b.producto and a.plan = b.cod_plan; quit;
%end;
%end; %mend status; %status;
C
ONCLUSIONESLa herramienta de macros que provee SAS es muy poderosa si sabemos dar un uso adecuado de la misma. Puede ayudarnos como lo demuestran los ejemplos a agilizar la ejecución de procesos, así como también a reducir las líneas de código que necesitamos, a través de la reutilización de procesos en los que solo debemos cambiar el o los parámetros de entrada.
Lo importante es que podamos dar un uso adecuado a esta poderosa herramienta que nos provee SAS para hacer nuestro trabajo más llevadero. Es un nuevo lenguaje dentro del lenguaje de SAS, pues presenta sus reglas particulares, y debemos estar preparados para asumir el reto de utilizarlo.
R
EFERENCIASBreheny, Patrick. Writing cleaner and more powerful SAS code using macros. Consultado en fecha 01/11/2014 en http://myweb.uiowa.edu/pbreheny/talks/macros.pdf
Cohen, John. A TUTORIAL ON THE SAS MACRO LANGUAGE. AstraZENECA LP.
SAS Institute Inc. 2004. SAS® 9.1 Macro Language: Reference. Cary, NC: SAS Institute Inc.
I
NFORMACIÓN DEC
ONTACTOComentarios y preguntas son apreciados. Contacte al autor Nombre Autor
Juan Radhames Gómez Martínez Claro RD.
INTEC
Email Trabajo: [email protected] Email Personal: [email protected] Redes Sociales
Twitter:radhixz LikedIn:juan-gomez
SAS y cualquier otro nombre de producto o servicio de SAS Institute Inc., son marcas registradas de SAS Institute Inc. Otras marcas y productos son marcar registradas de sus respectivas compañías.