SAS: Uso de macrovariables

Las macrovariables de SAS son las variables que en SAS Base se utilizan, como en otros lenguajes de programación para almacenar ciertos valores. En SAS hay una particularidad que hace que su manejo sea distinto que en otros lenguajes que seguramente ya conocemos, y es que las variables de SAS no tienen formato. Así que cuando almacenamos un número, un texto o una fecha en una macrovariable se almacena exactamente ese contenido sin alteración.

Esta particularidad tiene dos consecuencias: debemos saber tratar cada variables en función del tipo de valor que almacena cuando queremos compararlo con otros valores para comparar cosas del mismo tipo. Y por otro lado no podemos asignar el resultado de una función a una macrovariable directamente.

Manejar macrovariables con números es fácil porque se pueden comparar directamente con números o con el contenidos de campos numéricos porque los números no se expresan con ningún formato especial:

%let a = 1;

data tabla1;
    numero = 1;
    if numero = &a then valida1 = 1;
    if &a = 1 then valida2 = 1;
run;

En el caso de los textos, estos tienen que ir entre comillas (dobles o simples da igual) y hay que tener en cuenta eso cuando utilicemos variables con SAS. Además hay que tener en cuenta que una macrovariable se resuelve si va sin comillas o dentro de comillas dobles, pero no lo hace si va dentro de comillas simples. Veremos esto primero porque puede generar algunas confusiones:

%let texto = Ojo por ojo y el mundo acabará ciego;
ComandoResultado
%put &texto;Ojo por ojo y el mundo acabará ciego
%put «&texto»;«Ojo por ojo y el mundo acabará ciego»
%put ‘&texto’;‘&texto’

Como veis el hecho de incluir las dobles comillas o no afecta al contenido de la macrovariable y eso hay que tenerlo en cuenta al usar la macrovariable y compararla con otro valor. Por ejemplo, tomemos estas variables:

%let ciudad1 = Madrid;
%let ciudad2 = "Barcelona";
%let ciudad3 = 'Sevilla';
OperaciónResultado
&ciudad1 = «Madrid»FALSEciudad1 contiene Madrid, y así escrito SAS lo confunde con el nombre de un campo de una tabla.
&ciudad1 = MadridTRUEInesperado resultado. Tanto el contenido de la macrovariable como el valor con el que se compara están sin comillas. SAS confunde eso con el nombre de un campo y está un campo consigo mismo. Si esta comparación estuviera en un if dentro de un paso data, se crearía el campo Madrid a nulo en la tabla de salida y esta comparación sería cierta.
«&ciudad2» = «Barcelona»TRUEEsto es correcto ambas partes de la igualdad están entre comillas dobles.
«&ciudad2» = ‘Barcelona’TRUEEste caso también es correcto porque un texto debe estar delimitado entre comillas, da igual si son simples o dobles.
‘&ciudad2’ = «Barcelona»FALSELo contrario no funciona porque no se resuelve el contenido de una macrovariable dentro de comillas simples: estaría comparando el nombre de la macrovariable con la cadena de texto.
&ciudad3 = «Sevilla»TRUELa variable ciudad3 contiene una cadena entre comillas simples lo que es comparable a un valor de texto. Este caso parece muy parecido al anterior ya que el contenido de la variable está entre comillas simples, pero nótese que no es lo mismo poner las comillas simples conteniendo la variable que si está dentro de la variable.

Con todo lo anterior, lo que os recomiendo es que utilicéis siempre las macrovariables de texto de la misma manera en vuestro código de forma general para no confundiros en como debéis expresar las igualdades. Por ejemplo, esta forma de hacerlo es una de las válidas:

%let nombre= Jose Luis;
data salida1;
    if "&nombre" = "Jose Luis" then sexo = 'H';
run;

Con las fechas pasa algo similar a lo anterior teniendo en cuenta que las fechas se indican entre comillas (dobles o simples) con una -d- detrás si es fecha o con -dt- si es una fecha-hora. Para trabajar con fechas recomiendo asignar a la macrovariable un valor como si estuviera en formato date9., pero sin las comas ni la -d-.

%let fecha= 15may2021;
data salida2;
    if '1jan2021'd <= "&fecha"d <= "31dec2021"d then anyo = 2021;
run;

De todo lo anterior se desprende que una macrovariable guarda todo lo que se le asigna sin interpretarlo y sin tener en cuenta su formato. Por esa misma razón una macrovariable puede contener también una función o un pequeño trozo de código que puede construirse y hacerse ejecutar en tiempo de ejecución… aunque esto lo veremos en otro momento.

Truco SAS: Incrementales

Un pequeño truco, ¿cómo podemos crear un nuevo campo incremental con SAS? Un incremental es un campo que contiene un número que se incrementa de valor de 1 en 1 con cada nuevo registro y se utiliza para crear una clave única de una tabla.
En SAS tenemos dos formas para hacer esto: una para pasos data y otra para procedimientos SQL. En el paso data lo más fácil es usar el comando _N_ que te indica el número de registro en el que te encuentras.

data salida;
set entrada;
incremental = _N_;
run;

Pero _N_ solo funciona dentro de un paso data. Para el proc sql debemos usar la función monotonic() que hace exactamente la misma función.

proc sql;
create table salida as
select monotonic() as incremental, a.*
from entrada a;
quit;

SAS: Operaciones con cadenas de texto

La verdad es que SAS tiene un montón de funciones para tratar cadenas de texto. Y algunas de ellas son muy divertidas a la hora de programar por la cantidad de trabajo que te ahorran.
Vamos a empezar por algunas de las más típicas, las funciones para limpiar cadenas y quitar espacios. Vamos a utilizar la cadena como ejemplo:

%let cad = '  En un lugar de la  Mancha...  ';
Función Resultado Comentario
strip(&cad) ‘En un lugar de la Mancha…’ Elimina los espacios de los extremos, pero no los duplicados en el medio de la cadena.
compress(&cad) ‘EnunlugardelaMancha…’ Elimina todos los espacios de la cadena.
compbl(&cad) ‘ En un lugar de la Mancha… ‘ Elimina los espacios duplicados dejando uno solo. Deja al menos un espacio en los extremos si existían previamente.

Probablemente en algunas ocasiones sea bueno combinar algunas de ellas, como por ejemplo, para eliminar los espacios del inicio y del final y asegurarse de que no existen espacios dobles en la cadena podemos utilizar: strip(compbl(&cad)).

Tenemos también funciones para manejar mayúsculas y minúsculas.

%let cad = 'Iñigo fernández gonzález';
Función Resultado Comentario
upcase(&cad) ‘IÑIGO FERNÁNDEZ GONZÁLEZ’ Transforma la cadena a mayúsculas.
lowcase(&cad) ‘iñigo fernández gonzález’ Transforma la cadena a minúsculas.
propcase(&cad) ‘Iñigo Fernández González’ ‘Capitaliza’ todas las palabras de la cadena.

Otro tipo de funciones de texto son las que obtienen una subcadena a partir de una dada:
%let cad = 'Hélice de Watson & Crick';
%let fecha = "12/04/2021";

Función Resultado Comentario
substr(&cad,11,14) Watson & Crick Genera una cadena desde la posición 11 de la original y con 14 caracteres de longitud.
scan(&fecha,2,’/’) 04 Genera una cadena tomando el segundo trozo resultante de dividir la cadena indicada (&fecha) en trozos en función del carácter «/».

También tenemos funciones de búsqueda de una subcadena o de un carácter dentro de una cadena. Estas funciones pueden devolvernos la posición de la cadena buscada o sustituir la cadena buscada por otro valor:

%let cad = 'El sr. y la sra. Smith';
%let fecha = "12/04/2021";
Función Resultado Comentario
find(&cad,’Smith’) 18 Devuelve la posición de la primera ocurrencia de la subcadena buscada dentro de la cadena original.
index(&cad,’Smith’) 18 Devuelve la posición de la primera ocurrencia de la subcadena indicada.
translate(&fecha,’-‘,’/’) ’12-04-2021′ Sustituye uno a uno, uno o varios caracteres por otros en la cadena indicada.
tranwrd(&cad,’sr.’,’señor’) ‘El señor y la sra. Smith’ Hace lo mismo que la anterior, pero con grupos completos de caracteres.

Continuo mostrando más de estas funciones en la segunda parte de este artículo.

Cómo detener la ejecución en SAS

Hola de nuevo a todos. El tema de hoy es cómo detener el flujo de ejecución de un programa SAS. Normalmente lo haremos cuando se den determinadas condiciones, por ejemplo cuando un fichero no esté disponible, cuando se haya superado una hora de ejecución, etc.
Hay dos formas principales de detener la ejecución y utilizar una y otra depende del contexto dentro del programa:
Si estamos dentro de un paso data podemos utilizar el comando STOP.

data salida;
set entrada;
if _N_ = 100 then stop;
run;

Este ejemplo detendrá el proceso al llegar al registro número 100. La tabla de salida se obtendrá, pero sin el registro 100, por lo que se obtiene una tabla de 99 registros. El comando stop detiene la ejecución del paso data en el sentido de que no se procesan más registros pero no detiene la ejecución totalmente (ya que la tabla se genera).
En cambio, si estamos dentro de una macro utilizaremos una etiqueta y un comando %goto. Las etiquetas son identificadores a lo largo del código que identifican un punto determinado. En este caso tendríamos que situar esa etiqueta antes del %mend que termina la macro, y una condición que si se cumple lleve a esa etiqueta:


%macro ejecucion;
data salida1;
set entrada1;
run;
%if not %sysfunc(exist(entrada2)) %then %goto fin;
data salida2;
set entrada2;
run;
data salida3;
set entrada3;
run;
%fin:
%mend;
%ejecucion;

En este ejemplo el programa comprueba que exista la tabla «entrada2» antes de seguir con el proceso porque en caso contrario lleva el control de ejecución al final de la macro y la ejecución termina.