Archivos de la categoría WPS

Macros SAS. Macro split para partir un conjunto de datos

Debido a problemas con un servidor hace años descubrí la macro Split. Básicamente lo que hace es partir un conjunto de datos SAS en múltiples conjuntos de datos SAS con el mismo número de observaciones, además lo hace en un solo paso data. La forma de particionar el conjunto de datos es muy simple, si alguien tiene dudas con el código que lo comente y lo analizamos mejor. La macro (mejorada) es:

 %macro split(in=, out=, ndsn=2);
data %do i = 1 %to &ndsn.; &out.&i. %end; ;
retain x;
set &in. nobs=nobs;
if _n_ eq 1
then do;
if mod(nobs,&ndsn.) eq 0
then x=int(nobs/&ndsn.);
else x=int(nobs/&ndsn.)+1;
end;
if _n_ le x then output &out.1;
%do i = 2 %to &ndsn.;
else if _n_ le (&i.*x)
then output &out.&i.;
%end;
run;
%mend split;

Un bucle que en función de un contador mete las observaciones donde correspondan, en mi opinión no es un código muy complejo. Como siempre un ejemplo de uso:

 data uno;
do i=1 to 2000000;
output;
end;
run;

%split(in=uno, out=partido, ndsn=4);

Espero que os sea de utilidad, un saludo.

Ayudadme. Importar a SAS texto con comillas

Hoy sois vosotros los que tenéis que ayudar al dinosaurio. Ya no estoy para estas cosas. Tengo un problema. Fichero de texto separado por ; típico csv de toda la vida. Este fichero de texto contiene diversos campos que a mis efectos son de texto. Si abrimos el fichero con una hoja de cálculo tendríamos:

Pues bien, a la hora de importar unos datos con esa estructura desde SAS no soy capaz de que Dato2 siga conservando las comillas. Con IMPORT tampoco me funciona. El código sería algo parecido a esto:

 data WORK.CODIGO                                 ;
infile 'C:\TEMP\prueba_SAS.csv' delimiter = ';'  MISSOVER DSD
 lrecl=32767 firstobs=2 ;
   informat DATO1 $20. ;
   informat DATO2 $20. ;
input
            DATO1 $
            DATO2 $;
run;

Es un tema que he resuelto pero desde los datos de origen. Pero tengo una duda, ¿qué debo poner en el INFILE para evitar este problema? Que nadie me responda “quitar DSD en el INFILE” que no sirve, tengo campos en blanco. O a lo mejor quitar el DSD en combinación con otra instrucción. El caso es que me atasqué y tiré por el camino más sencillo. A ver si me ayudáis, gracias.

Trucos SAS. Particionar y exportar a texto un dataset

Duda que plantea David. Exporta a csv una tabla SAS en varias partes. Ya habrá tiempo para comentarlo:

*TABLA SAS DE EJEMPLO;
data total;
do i=1 to 10000;
importe=ranuni(8)*100;
output;
end;
run;

*MACRO QUE RECORRE LA TABLA, PARTE Y EXPORTA CADA PARTE
NECESITA EL CONJUNTO DE DATOS Y EL TAMAÑO DE CADA PARTE;
%macro parte(ds, tamanio);
%do i = 1 %to 10000 %by &tamanio.;
data parte;
set &ds. (firstobs = &i. obs = %eval(&i. + &tamanio.));
run;

PROC EXPORT DATA= WORK.Parte
OUTFILE= "C:\TEMP\parte&i..csv"
DBMS=CSV REPLACE;
RUN;
proc delete data=parte; quit;
%end;
%mend;

%parte(total, 1000);

Trucos SAS. Primer y último elemento de un array

Breve entrada sobre el uso de arrays en SAS. Dada una tabla SAS como esta con una variable mes1, mes2, … mesN:

arrays_sas.PNG

Necesitamos identificar el primer y el último elemento no nulo de un array y el número de elementos no nulos de ese array. Veamos el ejemplo:

data datos;
input id mes1 mes2 mes3 mes4 mes5 mes6;
datalines;
1 . . . . . .
2 162.18 88.41 919.62 891.25 837.73 163.14
3 . 790.52 160.03 . 60.31 343.30
4 . . 482.45 755.39 . .
5 265.17 963.53 . . 392.06 .
6 . 214.95 616.17 183.01 778.48 57.42
7 191.52 . 208.50 50.55 705.72 .
8 711.76 . . . 193.20 658.45
9 782.67 172.49 539.42 663.28 4.53 358.51
10 695.12 367.74 . 573.47 366.30 951.98
;
run;

Para este proceso creamos un array que recorreremos 2 veces, una hacia delante para identificar el primer elemento y otra a hacia atrás para identificar el último elemento:

data datos;
set datos;
array m(*) mes:;
*PRIMER ELEMENTO;
num=0;
do i = 1 to dim(m);
if m(i) ne . then do;
  primero=m(i);
  num=i;
  i=dim(m);
end;
end;
*ULTIMO ELEMENTO;
do i = dim(m) to 1 by -1;
if m(i) ne . then do;
  ultimo=m(i);
  num=i-num;
  i=1;
end;
end;
drop i;
num=num+1;
run;

Como hemos indicado el array se recorre dos veces, la primera vez de forma ascendente para buscar el primer elemento y la segunda de forma descendente para encontrar el último, la diferencia más uno es el número de elementos no nulos que tiene ese array. Ejemplo de uso de arrays en SAS. Saludos.

Sentencia del Tribunal de Justicia de la UE sobre el caso WPS y SAS

El caso de WPS y SAS por fin tiene un final. World Programing Software ha vencido (por fin) al todo poderoso SAS Institute Inc. La sentencia establece que:

De este modo, procede señalar que no puede haber infracción del derecho de autor sobre el programa de ordenador cuando, como sucede en el caso de autos, el adquirente legítimo de la licencia no ha tenido acceso al código fuente del programa de ordenador correspondiente a esa licencia, sino que se limitó a estudiar, observar y verificar ese programa con el fin de reproducir su funcionalidad en un segundo programa.

WPS no tiene el código fuente de SAS por lo tanto está dentro de la ley y cuesta mucho menos, esto último no lo dicen, lo digo yo. También hay algún aspecto sobre el software propietario que es muy interesante pero tengo que estudiar si pone lo que pone:

40 En efecto, tal como el Abogado General señala en el punto 57 de sus conclusiones, admitir que el derecho de autor pudiera proteger la funcionalidad de un programa de ordenador supondría ofrecer la posibilidad de monopolizar las ideas, en perjuicio del progreso técnico y del desarrollo industrial.

¿Cómo se puede interpretar este artículo?

En fin, aunque comercialmente la gente de WPS deja mucho que desear ya pueden decir que tienen un software 100% legal.

Trucos SAS. Eliminación de espacios en blanco

Truco SAS práctico para aquellos que os estáis iniciando en el uso de las funciones de texto con SAS. Se trata de eliminar aquellos espacios en blanco que no son necesarios en una variable. Quería plantearos las posibles soluciones que se me han ido ocurriendo. Algunas de ellas no son eficientes pero es necesario que dispongáis de todas. En la línea habitual planteo un ejemplo para que lo ejecutéis y así podáis analizar los resultados:

data ejemplo;
palabra=" EJEMPLO DE ELIMINACIÓN DE BLANCOS CON SAS ";
uso_compress=compress(palabra);
uso_trimn=trimn(palabra);
uso_trimn_left=trimn(left(palabra));
uso_compbl=compbl(palabra);
length uso_rxchange $50.;
rx=rxparse("' ' to ' '"); drop rx;
call rxchange (rx,length(palabra),palabra,uso_rxchange);
run;

La variable palabra tiene tanto espacios por la derecha como por la izquierda y entre las palabras que no son necesarios. La función COMPRESS elimina todos los espacios en blanco. Con TRIMN y LEFT eliminamos los espacios en blanco al inicio y al final de palabra pero mantenemos los espacios en blanco entre palabras.
COMPBL (compress blank) parece más adecuada para eliminar los espacios en blanco sobrantes entre las palabras. La función de reconocimiento de patrones RXCHANGE (que necesita el patrón previamente con RXPARSE) sustituye dos espacios por uno sólo, el resultado no parece muy satisfactorio; esto mismo podríamos hacerlo con la función TRANWRD. A ver si algún lector encuentra un patrón adecuado para
estas funciones. 

¿La combinación óptima de funciones?

data ejemplo;
palabra=" EJEMPLO DE ELIMINACIÓN DE BLANCOS CON SAS ";
optimo=trimn(left(compbl(palabra)));
run;

Combinación de 3 funciones de texto que nos permite eliminar espacios en blanco. A continuación lo ponemos como una macro que realiza una función:

%macro noblanco(pal);
trimn(left(compbl(&pal.)))
%mend;
*;
data ejemplo;
palabra=" EJEMPLO DE ELIMINACIÓN DE BLANCOS CON SAS ";
optimo=%noblanco(palabra);
run;

Espero que sea de vuestra utilidad. Un saludo.

Trucos SAS. Trasponer con SQL para torpes

trasponer_sql1.png

Trasponer datos con SAS es un tema que genera un gran número de consultas en Google, por lo tanto genera un gran número de visitas a este blog. Ya hay un monográfico al respecto pero hoy quería volver a contar la trasposición de datos con SQL y SAS pero a un nivel más bajo para que sea lo más sencillo posible. En el ejemplo partimos de una tabla con 3 variables, un id_cliente, un campo tipo y un campo precio. Cada tipo tiene un precio y necesitamos que nuestro dataset tenga un registro por id_cliente y 3 precios, uno por cada tipo. El ejemplo en código SAS:

data datos;
input id_cliente $ tipo precio;
datalines;
A 1 100
A 2 150
A 3 120
B 1 200
B 2 250
B 3 220
C 1 300
C 2 350
C 3 320
D 1 400
D 2 450
D 3 420
;run;

Esta es nuestra tabla de partida, ahora vamos a generar tres variables en función de la variable tipo:

data datos2;
set datos;
precio_1 = (tipo = 1) * precio;
precio_2 = (tipo = 2) * precio;
precio_3 = (tipo = 3) * precio;
run;

trasponer_sql2.png

Si vemos la tabla resultante tiene una forma de matriz con precios y ceros en función de la variable tipo. Ahora si sumarizamos esas variables y agrupamos por el id_cliente la forma de la tabla resultante es el objetivo deseado:

proc sql;
create table tdatos as select
id_cliente,
sum(precio_1) as precio_1,
sum(precio_2) as precio_2,
sum(precio_3) as precio_3
from datos2
group by 1;
quit;

Bien, pues esta es la “filosofía” de la trasposición con SAS en SQL. Pero esto lo podemos hacer en un solo paso:

proc sql;
create table tdatos as select
id_cliente,
sum((tipo=1)*precio) as precio_1,
sum((tipo=2)*precio) as precio_2,
sum((tipo=3)*precio) as precio_3
from datos
group by 1;
quit;

Y así podemos trasponer de forma sencilla en SAS sin emplear el PROC TRASPOSE que tiene alguna que otra limitación. Y por supuesto nos sirve para trasponer siempre que utilicemos SQL, con ORACLE, POSTGRES,… Creo que esta vez es muy sencillo de entender. Saludos.

Paquete sas7bdat de R. Me pongo con ello

En su día en esta bitácora se habló de la función sas7bdat de R. Ahora han realizado un paquete de R a partir de esa base. Lo tenéis en el blog SAS and R. De momento es experimental en el CRAN.

No solo me gustaría divulgar la noticia, en breve espero tener preparado un repaso al tema a ver si podemos "consolidar" este paquete tan interesante para los usuarios de SAS. También haré nuevas versiones de entradas anteriores para ilustrar el funcionamiento.

Sam I need new WPS evaluation license!!

Trucos SAS. Más usos de INFILE y PIPE directorios en tablas SAS

Puede interesarnos tener directorios y subdirectorios en tablas SAS. Es decir, tabular el resultado de un lm en Unix o poner en una tabla el resultado de un dir de MS DOS / Windows. Ya tengo ejemplos publicados a este respecto:

Pero no está mal volver a poner un truco para analizar las posibilidades del INFILE + PIPE. Vamos a hacer un DIR de todo nuestro C:\ y sacar los archivos de mayor tamaño.
*ESTA ES LA INSTRUCCION DIR QUE EMPLEAMOS,
SUBDIRECTORIOS, AUTORES, ... (?dir);
filename df pipe "dir H:\ /S /O S /Q";
*CREAMOS UNA TABLA SAS CON EL RESULTADO DE
LA INSTRUCCIÓN MS DOS;
data ZZZ_ZZZ;
infile df pad;
input todo $300.;
if _n_=1 then delete;
run;

Tenemos una tabla SAS con el resultado de nuestro dir en una variable de texto todo de 300 bytes . Podemos extraer la información que deseamos trabajando con funciones de texto de SAS:
data archivos;
keep fecha archivo GIGAS autor;
set zzz_zzz;
format fecha ddmmyy10.;
fecha=input(substr(todo,1,10),ddmmyy10.);
GIGAS=compress(scan(todo,3," "),".")*1;
GIGAS=GIGAS/(1024**3);
autor=scan(todo,4," ");
archivo=substr(todo,60,100);
if fecha=. then delete;
if GIGAS=. then delete;
run;

La variable fecha tiene el formato ddmmyy10. y es el resultado de transformar parte de nuestra variable de texto todo a número y con input darle el formato de entrada adecuado, perfecto ejemplo de transformación de texto a fecha con SAS. El tamaño es la 3 parte de la cadena de texto que se obtiene con un dir, pero como lo tenemos en bytes lo transformamos a gigas. Para extraer parte de una cadena de texto con SAS empleamos la función SCAN, otro buen ejemplo es el autor que lo podemos encontrar en la cuarta posición. Recordamos: SCAN(todo,4,” “) -> busca en todo la cadena de texto que esté en cuarta posición cuando el delimitador es “ “ un espacio en blanco. Por último tenemos el nombre del archivo que es la última parte de la cadena todo que genera el dir de MS-DOS.
Buen ejemplo para recordar un par de temas que provocan un gran número de visitas a esta web. Ahora os dejo deberes, tenéis que obtener el directorio en el que se aloja el fichero. No es baladí el tema. A ver si sois capaces, yo lo tengo hecho pero es muy complejo y “poco elegante”. Espero que a alguno de vosotros se os ocurra un mejor método.