SAS y Microsoft Excel - Parte III - DDE

8 de mayo de 2009

Es tiempo de la última entrega de este pequeño tutorial. Aquí vamos a darle un vistazo al método DDE para exportar datos a Ms Excel además de un par de ejemplos cortos de cómo importar datos desde Excel a SAS.

Dynamic Data Exchange(DDE) es un método de comunicación para transmitir datos entre varias aplicaciones bajo Microsoft Windows. Como usuario SAS puedes usar DDE para importar y exportar data desde SAS, su uso es bastante sencillo y solo se deben agregar algunas sentencias que otras aplicaciones de Windows puedan entender, por ejemplo Ms. Excel.

Ahora bien, para que molestarse en aprender a usar DDE cuando tenemos otros procedimientos disponibles como Proc Import, Proc Export, ODBC e incluso el nuevo motor de librería para Excel? La respuesta puede ser bien sencilla y que se necesita licencias adicionales para todos estos procedimientos además algunos de ellos como Export e Import son muy limitados en la manera en que defines la salida de tus datos.

Para poner en acción DDE necesitamos las siguientes sentencias o componentes:

- OPTIONS
- Comando X
- Sleep()
- FILENAME
- FILE
- DATA STEP y
- PUT


Ahora se los voy a explicar pero en un orden diferente para que se entienda más fácil.

Primeramente, debemos definir un link hacia el archivo de Excel que deseamos crear o modificar, esto lo hacemos usando la sentencia FILENAME de la siguiente manera:

filename myexcel DDE "ExcelC:\[exceldde.xls]sheet1!R1C1:R101C4";

o

filename myexcel DDE "Excelsheet1!R1C1:R101C4";

en el caso de un archivo nuevo



donde myexcel (puedes asignarle el nombre que quieras!) es el nombre del link que estamos haciendo, DDE es el tipo de link y el contenido entre comillas específica que vamos a transferir data a un archivo de Excel que esta en C:\ de nombre exceldde.xls y la información la vamos a escribir en la hoja Sheet1 en el rango de celdas R1C1:R101C4.

Hasta aquí todo muy sencillo, pero si ejecutan directamente esta sentencia junto con el Data Step recibirán un error en el log del tamaño de una casa y la razón es que debemos abrir la aplicación de Excel antes de poder exportar nuestros datos. Esto se puede hacer “manomáticamente” o podemos dejar que SAS lo hago por nosotros.

Tip: Una recomendación general es cerrar cualquier archivo de Excel que estén usando en ese momento, podrían conseguirse con que su datos fueron exportados al archivo incorrecto, especialmente cuando se usa la segunda opción de filename.

Aquí es donde entran la sentencia OPTIONS y el comando X en juego para solventar esa molestia de la siguiente manera:

options noxwait noxsync;

x "C:\Program Files\Microsoft Office\OFFICE11\EXCEL.EXE";

Noxwait ordena a SAS cerrar automáticamente la ventana de DOS después de ejecutar el comando X. Nxsync le dice a SAS continuar con el procesamiento tan pronto como la instrucción haya sido suministrada al sistema operativo (Windows).

El comando X nos permite ejecutar comandos de Windows sin tener que finalizar la sesión de SAS.

Básicamente las 2 sentencias anteriores las estamos usando para abrir la aplicación de Excel antes de utilizar el DDE.

Tip: Dependiendo de la versión de Ms Office que tengan la ruta a Excel puede cambiar a Office10 ó Office12 etc etc…

Tip: Si la instrucción X anterior no invoca Excel correctamente intenten de la siguiente manera:

x 'c:\progra~1\micros~2\office11\excel.exe';

Antes de crear nuestro primer archivo vamos a hablar un poco de la función Sleep(). Esta función suspende la ejecución de un programa SAS por un período determinado de tiempo. Por defecto las unidades de tiempo son en segundos pero se puede modificar.

Así bien si deseas detener la ejecución por 10 segundos sería Sleep(10) o si lo prefieres por 3 horas sería Sleep(3600*3).

Para qué necesitamos está función? Bueno si bien es verdad que el comando X ordena a Windows abrir Ms. Excel, también es verdad que podría tardar unos cuantos segundos y si no esperamos a que la aplicación este abierta pues lamentablemente muestro programa fallará!

Ok, Ahora vamos a crear nuestro primer archivo de Excel, este archivo va a contener 3 variables y 20 registros. El programa quedaría de la siguiente manera:

options noxwait noxsync;

x 'c:\progra~1\micros~2\office11\excel.exe';

data _null_;
x=sleep(7);
run;

filename myexcel DDE 'excelsheet1!r1c1:r20c3';

data _null_;
file myexcel;
do i=1 to 20;
x=ranuni(i);
y=0.1+x;
z=x-0.1;
put x y z;
end;
run;



Nuestra hoja de Excel debería que verse así:




Bueno ya hemos creado nuestro archivo de Excel usando DDE, no fue muy complicado verdad? Pero fue un ejemplo muy sencillo.

Hagamos otro ejemplo que se adapte un poco más a la realidad de nuestro día a día. Supongamos que además del ejemplo anterior, tenemos que crear un gráfico y algunos estadísticos como el promedio aritmético, el mínimo y el máximo de cada variable. Adicionalmente queremos agregarle el nombre de la variable en cada columna y guardar el archivo en C:\exceldde.xls (el nombre del archivo es solo para mantener la secuencia de los ejemplos). Todo esto lo hacemos directamente en Excel, pero solo una vez!!!

Olvidaba algo!, exceldde.xls es nuestro template y queremos que cada vez que actualicemos nuestro reporte sea en un archivo nuevo.

La hoja de Excel quedaría así:






Ahora debemos modificar nuestro programa agregando algunas cosas nuevas para que se ajuste a este nuevo formato:

options noxwait noxsync;

x 'c:\progra~1\micros~2\office11\excel.exe';

data _null_;
x=sleep(5);
run;
filename ddecmd dde 'excelsystem';
data _null_;
file ddecmd;
put '[open("C:\exceldde.xls")]';
RUN;

data _null_;
x=sleep(2);
run;

filename myexcel DDE "ExcelC:\[exceldde.xls]sheet1!r2c1:r21c3";

data _null_;
file myexcel;
do i=1 to 20;
x=RANNOR(i);
y=0.1+x;
z=x-0.1;
put x y z;
end;
run;
data _null_;
file ddecmd;
put '[error(false)]';
put '[save.as("C:\Reporte Semanal.xls")]';
RUN;


Como habrán notado todo se mantiene igual (de 7 segundos lo baje a 5) hasta el primer filename.

Este nuevo formato en el filename es una forma génerica de enviarle comandos a Excel:

filename ddecmd dde 'excelsystem';

Seguidamente el Data _null_ ordena abrir nuestro template con el comando o macro OPEN que es propio de Ms Excel.

data _null_;
file ddecmd;
put '[open("C:\exceldde.xls")]';
RUN;

Ponemos a dormir a SAS por un par de segundos y continuamos ejecutando nuestro programa exactamente igual que en el ejemplo anterior. Nótese que el rango de celda cambio de r1c1:r20c3 a r2c1:r20c3 para que la salida comience en la fila 2 (no queremos reescribir los nombres de las columnas que están en la fila 1) y cambie la función ranuni por rannor para que el gráfico se vea diferente.

Posteriormente el programa le indica a SAS que salve nuestro template con un nombre distinto:

data _null_;
file ddecmd;
put '[error(false)]';
put '[save.as("C:\Reporte Semanal.xls")]';
RUN;

El [error(false)] es para indicarle a Excel que no haga preguntas tontas como “si deseas guardar los cambios”.

Tip: Si desean cerrar automáticamente Excel después de finalizado el programa solo debe agregar este código:

data _null_;
file ddecmd;
put '[error(false)]';
put '[quit()]';
RUN;

Nuestro Reporte Semanal.xls debería verse así:




Bien, hemos creado nuestro reporte personalizado en Excel usando DDE, no más Cortar y Pegar Aleluya!!!

Si llegaste hasta aquí pensarás que estás listo para empezar automatizar tus reportes. La respuesta es SI y NO,

Si están listos para esta tarea, pero no deberían embarcase en ella hasta tomar en cuenta las siguientes consideraciones que estoy seguro que les ahorraran unos cuantos días trabajando hasta tarde (Comentario Aleatorio: Conozco algunas personas cuyo hobby es trabajar hasta las 10pm y le echan la culpa a los datos, a SAS o al sándwich de atún piche que se comieron en el almuerzo ;-).

Hasta ahora nuestros 2 reportes han sido creados a partir de un Loop que genera 3 variables y 20 registros. No hemos utilizado ningún data set para generar nuestro reporte que al fin y al cabo es como lo haríamos en nuestros trabajos.

Veamos el siguiente ejemplo:

- Ejecutemos el programa que utilizamos en el primer ejemplo utilizando sashelp.class como data set de entrada.

options noxwait noxsync;

x 'c:\progra~1\micros~2\office11\excel.exe';

data _null_;
x=sleep(7);
run;

filename myexcel DDE 'excelsheet1!r1c1:r19c5';

data _null_;
file myexcel;
set sashelp.class;
put Name Sex Age Height Weight;
run;

Hasta ahora la única diferencia es que hemos implementado la sentencia SET para leer el data set sashelp.class y la salida en Excel es exactamente igual al data set.

Ahora intentemos el mismo ejemplo pero agregando un formato a la variable Name:

options noxwait noxsync;

x 'c:\progra~1\micros~2\office11\excel.exe';

data _null_;
x=sleep(7);
run;

filename myexcel DDE 'excelsheet1!r1c1:r19c5';

data _null_;
file myexcel;
set sashelp.class;
put Name $20. Sex Age Height Weight;
run;

En este ejemplo podemos notar que las variables Sex Age Height Weight están desaparecidas en acción!

Este problema lo podemos solventar usando la opción NOTAB y el dilimitador entre variables ‘09’x:


options noxwait noxsync;

x 'c:\progra~1\micros~2\office11\excel.exe';

data _null_;
x=sleep(3);
run;

filename myexcel DDE 'excelsheet1!r1c1:r19c5';

data _null_;
file myexcel notab;
set sashelp.class;
put Name $20. '09'x Sex '09'x Age '09'x Height '09'x Weight;
run;


Con esto cubrimos bastante bien el tema de como exportar nuestros datos a Excel usando DDE.


Ahora para finalizar veamos rápidamente 2 métodos para importar datos a SAS desde Excel. El primero será usando DDE y el segundo será usando Proc Import. El libname Excel puede ojearlo en otro post que escribí hace algún tiempo ya.

Con DDE sería de la siguiente manera:


options noxwait noxsync;

x 'c:\progra~1\micros~2\office11\excel.exe';

data _null_;
x=sleep(3);
run;
filename ddecmd dde 'excelsystem';
data _null_;
file ddecmd;
put '[open("C:\exceldde.xls")]';
RUN;

filename myexcel DDE "ExcelC:\[exceldde.xls]sheet1!r2c1:r21c3";

data SASdde;
infile myexcel dlm='09'x notab missover dsd;
input x y z;
run;

data _null_;
file ddecmd;
put '[error(false)]';
put '[quit()]';
RUN;

Con Proc Import:


PROC IMPORT OUT= WORK.Importdata
DATAFILE= "C:\Exceldde.xls"
DBMS=EXCEL REPLACE;
SHEET="Sheet1$";
GETNAMES=YES;
MIXED=NO;
SCANTEXT=YES;
USEDATE=YES;
SCANTIME=YES;
RUN;


Ahora si, listo para un buen merecido fin de semana.

SASludos,

Alberto