Trucos SAS. Mejor que hash IN para cruzar tablas

El otro día Fernando comentó que los cruces de tablas más rápidos entre tablas grandes y tablas pequeñas son las sentencias condicionales sobre listas. Tiene razón. Es una práctica muy habitual en SAS cuando leemos tablas de Oracle la ralización de listas, esto derivará en otro truco SAS en breves días. El caso es que me gustaría que probárais este código:


data grande;
do i=1 to 20000000;
idcliente=int(ranuni(0)*1000000);
drop i;
output;
end;
run;
*CONJUNTO DE DATOS PEQUEÑO, NO TIENE
 REGISTROS DUPLICADOS;
data pequenio;
do i=1 to 2000000;
idcliente=int(ranuni(34)*1000000);
drop i;
if mod(idcliente,1132)=0 then output;
end;
run;
proc sort data=pequenio nodupkey; by idcliente;quit;
*;
proc sql noprint;
select idcliente into:lista separated by " "
from pequenio ;
quit;
*;
data machea5;
set grande;
if idcliente in (&lista.);
run;

Bueno, el tiempo de ejecución de este cruce es de 3 segundos. Mejora a las soluciones planteadas el otro día y sobre todo es un código fácil, muy fácil. Se trata de crear listas de macrovariables y realizar un paso data con una sentencia condicional. Tiene un problema, el tamaño máximo que nos permite una macrovariable. Y en este punto continúa el truco SAS. ¿Cúal es el tamaño máximo que puede tener una macrovariable? 64K, 65534 characters. Tenemos que evitar a toda costa este error: ERROR: The length of the value of the macro variable LISTA (70356) exceeds the maximum length (65534). The value has been truncated to 65534 characters. Para evitar este problema podemos realizar el siguiente planteamiento: 65.000/la longitud del campo de cruce, en el caso del ejemplo: 65.000/8 = 8.000 más o menos. Hacemos una prueba:


data pequenio;
do i=10000000 to 20000000;
idcliente=int(ranuni(34)*1000000);
drop i;
if ranuni(8)<0.0008 then output;
end;
run;
proc sort data=pequenio nodupkey; by idcliente;quit;
proc sql noprint;
select idcliente into:lista separated by " "
from pequenio ;
quit;

8 caracteres y 8.000 registos más o menos, no hemos obtenido ningún problema. Si tenemos 12 caracteres sería 65.000/12 = 5.400, realizamos una breve comprobación:


data pequenio;
do i=100000000000 to 100000100000;
idcliente=int(ranuni(34)*100000000000);
drop i;
if ranuni(8)<0.054 then output;
end;
run;
proc sort data=pequenio nodupkey; by idcliente;quit;
proc sql noprint;
select idcliente into:lista separated by " "
from pequenio ;
quit;

Si alguien está ejecutando estos códigos a lo mejor ha sentido la curiosidad de modificar la regla de 65.000/longitud y obtiene que no hay problema, el caso no es buscar el límite, si no una regla que lo cumpla. En breve una macro que realice estos cruces.

4 comentarios en “Trucos SAS. Mejor que hash IN para cruzar tablas

  1. ¿Cómo puede ser que una búsqueda secuencial (la que imagino que se hace en un “in”) mejor que una búsqueda en un “hash”?

    Hoy voy a publicar los resultados del ejercicio análogo que he hecho con R en mi blog y se verá cómo los “hashes” son órdenes de magnitud más eficientes que otras aproximaciones.

  2. Por favor me puedes ayudar el problema basicamente es el siguiente:

    tabla1
    NOM
    abc
    efg
    abc
    hij

    tabla2
    CLAV ASIG
    abc 1
    efg 2
    hij 3
    klm 4

    y lo q necesito es que me de la siguiente tabla

    tablaRespuesta
    NOM ASIG!
    abc 1
    efg 2
    abc 1
    hij 3

    1. Lo puedes hacer con un merge o con proc sql. Lo hago con proc sql:

      Proc sql;
      create table NOM_ASIG as select a.*, b.ASIG
      from tabla1 as a left join tabla2 as b
      on a.NOM=b.ASIG;
      quit;

Deja un comentario

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