Configuración SAS Guide para usuarios

Como usuarios hay varias configuraciones de nuestro cliente SAS Guide que puedo recomendaros hacer. La configuración de SAS Guide se realiza desde el menú: «Herramientas > Opciones…»

  • General > Mostrar la ventana de bienvenida al inicio:
  • Permite recuperar la ventana de bienvenida en el caso de que la hubieras quitado.

    Pantalla_bienvenida

  • General > Configuración de idioma de SAS Enterprise Guide:
  • Aquí es donde podemos cambiar el idioma de SAS Guide.

    Configuracion_idioma

  • Resultados > General > Formatos:
  • Creo que esto no es muy conocido pero es muy útil cuando hacemos análisis univariante, por ejemplo. Si necesitamos guardar todos esos informes en determinado formato podemos definirlo aquí. Podemos seleccionar tantos como queramos y en el layout del proyecto se generarán tantos nodos con tantos formatos como indiquemos. En predeterminado indicaremos aquel que se abrirá por defecto entre todos los anteriores. Tenemos opción de marcar informes tipo SAS, PDF, HTML, Excel, PowerPoint, ficheros planos…

    Configuracion_formatos

  • Programas SAS > botón «Opciones del editor»
  • Para una mejor visualización y compatibilidad a la hora de exportar código recomiendo marcar las siguientes opciones:

    configuracion_editor

  • Comparación de archivos:
  • Finalmente, aquí podemos definir el comparador de archivos por defecto que queremos usar: WinMerge o Notepad++, por ejemplo.

  • Cerrar tablas abiertas por SAS Guide
  • Añado una opción importante más, que puede ayudarte a evitar problemas cuando una tabla que se haya generado ya anteriormente con Guide siga abierta y genere un error al volver a ejecutar. La opción se encuentra en Herramientas > Opciones > Programas SAS. Dentro de esta pantalla asegúrate de tener seleccionada la opción «Cerrar todos los datos abiertos antes de ejecutar código».

Automatización de procesos con SAS

SAS Enterprise Guide tiene un planificador de tareas que no está mal pero también se puede planificar tareas en el propio servidor. La solución que os ofrezco está basada en un servidor con un sistema operativo Linux.

Una automatización tiene 3 partes: el código SAS a ejecutar, un script que llama al programa y un planificador de tareas que llama al anterior.

Programa SAS (fichero .sas)

Es necesario eliminar de él cualquier rastro de parámetro o fecha variable que hallamos estado empleando cuando lo desarrollamos y debemos calcular esos valores en función de la información que tendremos en la máquina en el momento de la ejecución: como por ejemplo la fecha de sistema. Si queremos hacer un cierre de mes lo más sensato será calcular la fecha de cierre como:

fecha_ejecución = intnx('month',date(),-1,'E');

 
Además recomiendo incluir al principio del programa la siguiente línea que imprime en el log del proceso el número de tarea que se está ejecutando. De esa manera podremos matar un proceso colgado con un comando kill.

%put &sysjobid;

 
TIP: Al hacer una primera automatización es recomendable verificar cual es la fecha del sistema en el servidor SAS, porque podría ser distinta a la actual y eso puede generar problemas.

Script (fichero .sh)

Este pequeño programa es el encargado de identificar la ruta donde se encuentra [sas.exe] y en este caso utiliza además otro script [sas.sh] para que se ejecuten los parámetros de ejecución de sas.exe. También necesita la ruta y el nombre del programa .sas a ejecutar y la ruta y nombre del fichero que se creará con el log de la ejecución. Es importante tener en cuenta esto porque sino nunca sabremos por qué fallan nuestros procesos.

#!/bin/sh
f_job=[ruta_programa]/[nombre_programa].sas
f_log_sas=[ruta_log]/`date +%Y%m%d%H%M`_"$1"[nombre_log].log
sh [ruta_base_sas]/sas.sh -sysin ${f_job} -log ${f_log_sas}

 
Ambos ficheros el .sas y el .sh deben ser copiados con FileZilla u otra herramienta similar a las rutas que hemos visto.

Planificador de tareas

Utilizo el planificador de tareas de Linuz, CRON. Cron tiene un demonio corriendo en el sistema operativo que lanza las tareas que se han indicado en un fichero de texto: crontab. Cada usuario del sistema tiene un fichero crontab para sus propias tareas.

En nuestro caso, para que SAS tenga permisos sobre las ficheros y tablas que necesitamos utilizar en nuestro proceso es necesario utilizar un usuario de Linux que sea también usuario en SAS. Existen varios usuarios de esta naturaleza en una instalación de SAS: sasbatch, sasdemo, etc. El adecuado para planificar un proceso es sasbatch.

Para editar el fichero crontab debemos logarnos en la máquina y acceder al shell para introducir este comando:

crontab

El programa se abre con el visor vi. Para editarlo pulsamos [INSERT]. A continuación introduciremos una línea por programa sas a planificar. Finalmente introducimos las combinaciones de teclas: [END]:q! para salir sin guardar y [ESC]:wq para salvar y salir. No voy a entrar definir aquí como se planifica una tarea pero os dejo un enlace a la Wikipedia donde se explica muy bien:

En un próximo post comentaré errores típicos que se pueden producen al planificar.

Coordenada dentro de un polígono

Si alguna vez os encontráis con el problema a resolver de saber si una coordenada, una dirección, una población, etc. se encuentra dentro de un polígono o de una área cualquiera que tienes definida por un listado de coordenadas esta pequeña macro hecha con SAS puede ayudarte.


¿Se encuentran estos puntos fuera o dentro del polígono?

Tenemos para este ejercicio los siguientes datos. Una tabla que contiene el polígono y otra que contiene el listado de coordenadas a evaluar.

data POLIGONO;
    poligono="40.52,-4.32 40.55,-4.32 40.56,-4.29 40.53,-4.24 40.52,-4.26 40.52,-4.32";
run;
 
data COORDENADAS;
    input coor_x coor_y;
    datalines;
40.54548 -4.29331 /*dentro*/
40.56126 -4.34155 /*fuera*/
40.54 -4.32 /*se encuentra en el borde de una línea horizontal*/
40.52 -4.30 /*se encuentra en el borde de una línea vertical*/
40.49994 -4.28138 /*fuera*/
40.70045 -3.91955 /*fuera*/
40.52 -4.32 /*vértice*/
;
run;

Hay varias maneras de determinar si un punto se encuentra dentro o fuera de un polígono, utilizaremos una válida tanto para polígonos cóncavos como convexos, basada en el teorema de la curva de Jordan. De esta manera, si trazamos una línea cualquiera a partir de nuestro punto a evaluar, esta cortará el polígono un número impar de veces si el punto se encuentra dentro del mismo o 0 o un número par en caso contrario.

Lo primero que haremos será transponer el contenido del polígono que está representado en el formato siguiente (las coordenadas separados por coma y cada dupla separada por un espacio):

40.52,-4.32 40.55,-4.32 40.56,-4.29 40.53,-4.24 40.51,-4.26 40.52,-4.32

Para ello calculamos el número de vértices calculando el número de espacios con un compress y luego transponemos el contenido de forma que la salida es una tabla en la que cada registro contiene un segmento del polígono.

Así nuestro cálculo consistirá en saber si una línea horizontal trazada a partir de nuestro punto cruza una o varias veces. Utilizamos la siguiente macro:.

%macro poligono;
    data POLIGONO;
        set POLIGONO;
        num_vertices = length(poligono) - length(compress(poligono));
        do x=1 to num_vertices;
            origen_x = input(scan(poligono,(x*2)-1,' ,'),8.2);
            origen_y = input(scan(poligono,(x*2),' ,'),8.2);
            destino_x = input(scan(poligono,((x+1)*2)-1,' ,'),8.2);
            destino_y = input(scan(poligono,((x+1)*2),' ,'),8.2);
            output;
        end;
    run;

    %let dsid = %sysfunc(open(POLIGONO));
    data SALIDA (drop=xinters);
        retain id_alerta;
        set COORDENADAS;
        retain borde interseccion;

        %let nobs = %sysfunc(attrn(&dsid,NOBS));
        %let rc = %sysfunc(fetchobs(&dsid,1));
        %do i=1 %to &nobs;
            %let num_vertices = %sysfunc(getvarn(&dsid,%sysfunc(varnum(&dsid,num_vertices))));
            %let x = %sysfunc(getvarn(&dsid,%sysfunc(varnum(&dsid,x))));
            %let origen_x = %sysfunc(getvarn(&dsid,%sysfunc(varnum(&dsid,origen_x))));
            %let origen_y = %sysfunc(getvarn(&dsid,%sysfunc(varnum(&dsid,origen_y))));
            %let destino_x = %sysfunc(getvarn(&dsid,%sysfunc(varnum(&dsid,destino_x))));
            %let destino_y = %sysfunc(getvarn(&dsid,%sysfunc(varnum(&dsid,destino_y))));

            %let rc = %sysfunc(fetch(&dsid));
            if &x=1 then do;
                borde=0;
                interseccion=0;
            end;

            if coor_x = &origen_x and coor_y = &origen_y then do;
                resultado='VERTICE';
                output;
            end;
            else if not(coor_x = &destino_x and coor_y = &destino_y) then do;
                if &origen_y = &destino_y and 
                    &origen_y = coor_y and 
                    coor_x > min(&origen_x,&destino_x) and 
                    coor_y <= min(&origen_y,&destino_y) and
                    coor_y <= max(&origen_y,&destino_y) and
                    coor_x <= max(&origen_x,&destino_x) and
                    &origen_y ne &destino_y then do;
                        xinters = (coor_y - &origen_y) * (&destino_x - &origen_x) / (&destino_y - &origen_y) + &origen_x;

                    if xinters = coor_x then borde=1;
                    if (&origen_x = &destino_x or coor_x <= xinters) then interseccion=interseccion+1;
                end;

                if &x = &num_vertices then do;
                    if mod(interseccion,2)=1 or borde=1 then do;
                        resultado='DENTRO';
                        output;
                    end;
                    else do;
                        resultado='FUERA';
                        borde=0;
                        interseccion=0;
                        output;
                    end;
                end;
            end;
        %end;
    run;
    %let rc = %sysfunc(close(&dsid));
%mend;
%poligono;

 
Y el resultado que obtenemos es el siguiente:

Esta macro tiene la particularidad de que identifica correctamente cuando un punto se encuentra en un borde, pero en esos casos el indicador interseccion indicaría que se encuentran fuera del área del polígono. Hay que tener en cuenta esto y considerarlo en función de nuestros intereses. Yo, aquí, los estoy asumiendo como dentro.

 

Referencias: 

Basado en el código de AssemblySys dataServices: https://assemblysys.com/es/algoritmo-punto-en-poligono