Trucos SAS. Unión de múltiples tablas SAS con un nombre parecido

7 Dic

Ha llegado una búsqueda y ponemos el truco. Creo que ya lo puse pero no está mal de recordarlo.

1. Creamos 20 datasets aleatorios con 10 observaciones cada uno:
%macro doit;
%do i=1 %to 20;
data zzdatos_&i.;
do i=1 to 10;
output;
end;
run;
%end;
%mend;

Un bucle fácil de macros ha generado 20 dataset que se llaman ZZDATOS_n. Recomiendo siempre emplear nombres «absurdos» para el trabajo con esta metodología.

2. Empleamos un PROC SQL sobre la librería DICTIONARY, en concreto sobre la tabla tables.


proc sql noprint;
select memname into:lista_tablas separated by " "
from dictionary.tables
where index(memname,"ZZDATO")>0;
quit;

Recordemos que en SASHELP tenemos vistas de estas tablas, el SEPARATED BY es el que indica que hacemos una selección múltiple.

3. Realizamos la unión:


data tablon;
set &lista_tablas.;
run;
proc delete data=&lista_tablas.;run;

Sólo tenemos que emplear la macrovariable &lista_tablas. y podemos realizar cualquier operación con esta lista. Sé que soy un poco pesado con esta metodología pero me parece importante usarla porque puede ayudarnos a ahorrar código y automatizar procesos. Por supuesto si alguien tiene dudas sugerencias o un trabajo sin «over time» (ahora se llama así a echar más horas que un tonto) ya sabe rvaquerizo@analisisydecision.es

18 respuestas a «Trucos SAS. Unión de múltiples tablas SAS con un nombre parecido»

  1. Hola Raul y demás lectores/as,
    si hay algo realmente importante que haya aprendido con la gente del foro SAS-L (en el participo de vez en cuando) es precisamente evitar usar SAS macros si se puede hacer de otra manera.
    Así que a modo de ejemplo, y sea tambien para poner mi granito de arena en esta fabulosa web,
    me he tomado la molestia de escribir el siguiente código usando 2 ficheros planos temporales, uno para crear esas 20 tablas aleatorias que mencionas más arriba, y otra para guardar los nombres de esas tablas en el otro fichero plano, de manera que no tengamos que crear una macrovariable que nos de la lista que empleas, ni tampoco tener que acudir al
    dictionary:

    filename w temp;
    filename zzdatos temp;
    data _null_;
    do k=1 to 20;
    file w;
    put «data zzdatos_» k «; do i=1 to 10; output;end; run;»;
    file zzdatos;
    put «zzdatos_» k ;
    end;
    call execute (‘%inc w/source2;’);
    run;

    data tablon;
    set %inc zzdatos/source2;;
    run;
    proc datasets nolist;
    delete %inc zzdatos/source2;;
    run;

    Espero sea de ayuda.
    Dani Fernández.

  2. Me ha gustado mucho el ejemplo de call execute. De hecho se va a transformar en un trudo SAS.

    Interesante método para la creación de listas que se pasan a Oracle. Otro truco más. Gracias.

  3. Gracias a ti por crear esta web y por ponerme en contacto y actualidad con el mundo profesional de SAS cuando yo era un novato hace 3 años. La verdad mi carrera profesional ha subido mucho. Ahora sigo siendo el mismo pero un ‘epsilon’ menos novato, jaja!
    saludos

  4. Aunque intuitivamente entiendo que se ha de evitar el uso de macros si hay otra manera de hacerlo, en este caso no acabo de ver por qué es mejor utilizar un fichero plano a macrovariables. ¿Me sacáis de mi incultura?. Tampoco se por qué no es bueno utilizar el dictionary, me parece muy util si no conoces de antemano la estructura que viene detrás de zzdatos_, que puede ser una fecha, letras, códigos alfanuméricos… y si no conoces el número exacto de tablas (supongamos que es variable, porque es una carga mensual del dato que sea…). Seguro que sobre tu ejemplo se puede incorporar todo esto con pocas modificaciones pero ahora mismo estoy bastante espeso…

  5. hola FF, no tiene porque ser un fichero plano, se puede hacer desde un paso data tal
    cual, el caso es evitar usar macros en la medida de lo posible, y si se usa macro que
    sea en su correcto uso. No abusemos de las macros más cuando suelen ser engorrosos
    sobretodo si se enlazan unos con otros (nested macros).Entiendo que Raul hiciera esa
    macro (si bien no era ese el tema de su post sino como ‘rescatar’ los nombres de las
    tablas en una lista mediante el PROC SQL que hizo), cada cual puede programar como
    quiera, solo faltaba!, pero esa no es la mejor manera porque acabamos llenando la
    sesion SAS de macros temporales, montones de codigos encapsulados, etc… Lo que
    sucede es que a modo de ejemplo, no solo Raul sino tambien algunos manuales de SAS y/o
    páginas web que circulan por ahí, para los más iniciados (yo tambien pasé por ahí)
    se nos muestra constantemente como aprender a crear una macro, darle parámetros y
    llamarla de esa misma manera. Supongo que se enseña así porque es más intuitivo y fácil de digerir ya que hace repetir tantas veces como queramos todo el codigo SAS Base contenido entre el %DO… y el %End, de manera que facilita a los programadores SAS adentrarse en el mundo SAS macro sin mucho esfuerzo.Ya no digamos cuando un programador escribe cosas del estilo:

    %macro haz_esto(i);
    data zzdatos_&i.;
    do i=1 to 10;
    output;
    end;
    run;
    %mend;

    %haz_esto(1);
    %haz_esto(2);
    %haz_esto(3);

    Entonces luego acaba llegando a la conclusión que podría usar una nested macro
    para dejarlo más chulo que un 8 y demostrar como le encanta llenarlo todo de macros:

    %macro haz_esto(i);
    data zzdatos_&i.;
    do i=1 to 10;
    output;
    end;
    run;
    %mend;

    %macro repite;
    %do a=1 %to 20;
    %haz_esto(&a);
    %end;
    %mend;

    %repite;

    A este anterior código le sumas unos parámetros más, valores numericos y literales, algunos procedures por medio y la exasperación por depurar y entender el motivo inicial del programa aumenta exponencialmente. Así que usar lenguaje SAS Macro debería hacerse cuando realmente el proyecto lo requiere, por unos motivos concretos.

    Para ello recomiendo enfurecidamente dar un vistazo a esta página en donde se
    explica cuando y cuando NO usar una macro, y los tipos de macros:

    http://www.datasavantconsulting.com/roland/macrotips.html

    Una vez leido a Roland, y opiniones de otros dinosaurios del SAS que circulan por USA con más de 30 años de experiencia, si os preguntais como podría haber sido hecha
    una macro en el código de Raul, la solución no es única, pero aquí os dejo una idea:
    crear una ‘utility’ macro que nos sirva PARA SIEMPRE (la podemos guardar en nuestra librería de macros del tipo ‘compiled stored macros’), es decir, la podemos usar en cualquier programa que nos pueda hacer falta de ahora en adelante, el cual nos da una lista de nombres con un mismo sufijo desde un primer numero inicial hasta un numero final:

    %macro lista(nombre,num_ini,num_fin);
    %do i=&num_ini %to &num_fin;
    &nombre&i
    %end;
    %mend;

    Así de sencillo!
    Ya podemos usarla para nuesto propósito:

    data %lista(zzdatos_,1,20);
    do i=1 to 10;
    output %lista(zzdatos_,1,20);
    end;
    run;

    data tablon;
    set %lista(zzdatos_,1,20);
    run;
    proc delete data= %lista(zzdatos_,1,20); run;

    …pero como cualquier mortal, uno se hartará de ir llamando la misma macro
    una y otra vez dentro de nuestro pequeño programa (aqui la llamamos 4 veces),
    entonces sería más facil, cómodo y limpio solo tener que cambiar la siguiente macrovariable zz, por ejemplo cada vez que queramos darle un nombre nuevo, o aumentar la simulación de 1 a 100 datasets en vez de 1 a 20:

    %let zz=%lista(zzdatos_,1,20);
    data &zz;
    do i=1 to 10;
    output &zz;
    end;
    run;

    data tablon;
    set &zz;
    run;
    proc delete data= &zz; run;

    Para concluir, y aquí vuelvo al inicio (se puede hacer sin macro y tampoco no tiene porque usarse ficheros planos temporales si se desea, si partimos de la situación de que estamos en poder de controlar los nombres de nuestras tablas dentro de una lista (ya se que el dictionary es útil, no obstante hago incapié que aquí partimos de que somos capaces de conocer los nombres de nuestras tablas desde el código), y dado que veo que a Raul le gusta los call execute, aquí os lo dejo servido todo en un solo paso data:

    data _null_;
    do z=1 to 20;
    call symputx(‘z’,z);
    call execute(‘data zzdatos_&z ;do i=1 to 10;output;end;run;’);
    call execute(‘proc append base=tablon data=zzdatos_&z force; run;’);
    call execute(‘proc delete data=zzdatos_&z; run;’);
    end;
    run;

    Si nos topamos con la situación de que la lista de nombres de tablas es aleatoria o bien no sigue un sufijo concreto, eso quedo por ahora fuera de mi comentario; tal vez otro día será.

    un saludo,
    Dani Fernández.

  6. Yo nunca utilizaría el método de Dani, si queréis mi opinión. Al final no creas bytes en una tabla de símbolos, lo que haces es crear bytes en archivos temporales de la work del tipo #LN00021 y esto no me gusta mucho. Me parece interesante la metodología para crear HTML.

    Pero ya sabéis más formas de hacerlo.

  7. hola Raul, supongo que te refieres al metodo de ficheros planos temporales. Su ventaja es que son faciles de programar sin hacer uso de macrovariables, se crean y se destruyen por sí solos en cada ejecución, es decir que una vez ejecutado tienen un nombre pero si lo vuelves a ejecutar se vuelve a crear otro nuevo desapareciendo el anterior. Finalmente desaparecen una vez cerrada la sesion SAS al estar en la librería work.
    saludos,
    Dani Fernández.

  8. Cómo podría hacer lo mismo pero con:
    %DO i=1 %TO 10;
    CREATE TABLE WORK.ALEATORIO&i AS
    ……

    Cuando ejecuto ese query el nombre de la tabla no es reconocido. Intenté con ‘WORK.ALEATORIO&i’ pero tampoco es posible.

  9. A ver si te sirve de ejemplo:
    %macro doit;
    %do i=1 %to 20;
    data zzdatos_&i.;
    do i=1 to 10;
    output;
    end;
    run;
    %end;
    %mend;

    %doit;

    %macro sub;
    %do i=1 %to 10;
    proc sql;
    create table datos&i. as select
    a.*
    from zzdatos_&i. a
    where mod(i,&i.)=0;
    quit;
    %end;
    %mend sub;

    %sub;

    Saludos.

  10. Gracias, Raúl pero sucede que en el log de mi código obtengo lo siguiente:

    57 PROC SQL NOPRINT;
    58 CREATE TABLE WORK.TEMPNODEDATA AS SELECT * FROM WORK.NODEDATA WHERE 0=1;
    NOTE: Table WORK.TEMPNODEDATA created, with 0 rows and 8 columns.

    59 CREATE TABLE WORK.TEMPLINKDATA AS SELECT * FROM WORK.LINKDATA WHERE 0=1;
    NOTE: Table WORK.TEMPLINKDATA created, with 0 rows and 5 columns.

    60 QUIT;
    NOTE: PROCEDURE SQL used (Total process time):
    real time 0.00 seconds
    cpu time 0.01 seconds

    ERROR: Invalid symbolic variable name ,.
    61 %initGrafo;
    NOTE: PROCEDURE SQL used (Total process time):
    real time 0.00 seconds

    No entiendo el error «Error: Invalid symbolic variable name».

  11. Hola, no se donde poner mi duda, y visto que esto es lo más parecido a lo que ando buscando, pues haber si alguien me puede ayudar.
    Necesito comparar dos conjuntos de datos, que tienen diferentes campos.
    ej: 1- nombre apellido1 apellido2
    2- nombre apellido1 calle
    Quiero saber si es posible de alguna manera comparar el contenido de estas dos tablas, y lo que le falta a una de la otra se añada y viceversa. Dando como resultado que las dos tablas sean iguales en lo que refiere a formato.

  12. Revisión comentario anterior.

    Hay diversas formas de intentar solucionar es problema que expones, pero creo que la más simple sería cruzar las dos tablas con un PROC SQL.

    Lo primero que haría es tratar los campos de ambas tablas para hacerlos homogeneos para poder realizar el cruce. Por ejemplo, si el campo nombre de la primera tabla está en mayusculas y el de la otra tabla tiene otro formato, tendremos que transformar uno de ellos para poder cruzar.

    Una vez hecho ese tratamiento en los campos comunes (nombre apellido1) ya podriamos cruzar las tablas.

    PROC SQL;
    CREATE TABLE MI_TABLA_COMPLETA AS
    SELECT DISTINCT
    COALESCE(A.NOMBRE, B.NOMBRE) AS NOMBRE
    , COALESCE(A.APELLIDO1, B.APELLIDO1)
    AS APELLIDO1
    , APELLIDO2
    , CALLE
    FROM LIBRERIA.NOMBRE_TABLA1 A
    FULL JOIN
    LIBRERIA.NOMBRE_TABLA2 A
    ON A.NOMBRE = B.NOMBRE
    AND A.APELLIDO1 = B.APELLIDO1;
    QUIT;

    Si tienes este tipo de casos, vas a tener un problema (pongo el distinct para eliminar duplicados):

    TABLA1
    NOMBRE APELLIDO1 APELLIDO2
    JUAN PEREZ ARIZA
    JUAN PEREZ MARTINEZ

    TABLA2
    NOMBRE APELLIDO1 CALLE
    JUAN PEREZ C\ALCALA, 365
    JUAN PEREZ C\PEZ, 13

    En la tabla resultante tendrías un registro para cada uno con las dos calles.

    PD: Raúl, si puedes, borra el comentario anterior.

  13. Haz lo que plantea Salva. Pero ten en cuenta que, trabajar con campos de texto tipo nombre, apellidos, calle,… implica una «normalización» y esta normalización puede ir desde poner mayúsculas y eliminar tildes y caracteres especiales hasta la realización de claves fonéticas. Busca en el blog hay algunas macros que pueden interesarte.

  14. Buenas a todos:

    He encontrado el paraíso en SAS. Muchas gracias a Raúl y Dani por el tiempo y compartir su trabajo y saber. Admiro a las personas que enseñan sin pedir nada a cambio.

    He buscado en el blog sobre bucles. No encuentro nada en SAS (eso o no sé buscar que puede ser xD)

    Cuento mi problemilla:

    Necesito recorrer una lista con un doble bucle, pues necesito comparar los registros actuales con los que se añadan nuevos.(el fichero se llama control) Usando la variable «codce» (codigoconcesionario) comparo los registros mediante el bucle. Si se repiten los registros quiero copiar toda la fila (con todas las variables) en otro conjunto de datos al que llamaré «Concesionarios».

    Si pudiérais ayudarme sería genial. Sobre todo quiero saber si hace falta usar macro o no. El artículo del que se habla más arriba me lo estoy leyendo pero con paciencia que es en inglés jejeje

    Un saludo y repito muchas gracias.

  15. Buenos días,
    Estoy intentando crear un bucle dentro de una macro que me junte todas las tablas que he creado en un paso anterior, pero el problema es que el número de tablas que se crean (y que son las que quiero anidar) es variable y al hacerlo así, la última me solapa las anteriores.

    La macro variable total_tablas es variable.
    Tengo ‘total_tablas’ tablas creadas en cada caso (que se llaman tabla_1, tabla_2, tabla_3, …) que quiero anidar en una unica tabla que se llame ‘tabla_todo’

    %do r=1 %to &total_tablas.;
    data tabla_todo;
    set tabla_&r.;
    run;
    %end;

    Muchas gracias de antemano.nn1

  16. Cordial Saludo Muchachos.
    Tengo las siguientes tablas:
    TablaA =>columnas key A1 B1 C1 y TablaB=>columnas key A2 B2 C2
    Necesito crear una gran tabla que tengas las columnas en orden es decir:
    TablaAB con columnas: key A1 A2 B1 B2 C1 C2.
    Me pueden apoyar con el código y si requiero tener la suma de cada columna como sería?

    Gracias.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *