Archivos de la categoría SAS

Pasando de SAS a R. Primer y ultimo elemento de un campo agrupado de un data frame

Las personas que están acostumbradas a trabajar con SAS emplean mucho los elementos first, last y by, en el blog hay ejemplos al respecto, en R podemos hacer este trabajo con la librería “estrella” dplyr de un modo relativamente sencillo. A continuación se presenta un ejemplo para entender mejor como funciona, creamos un conjunto de datos aleatorio:

id <- rpois(100,20)
mes <- rpois(100,3)+1
importe <- abs(rnorm(100))*100

df <- data.frame(cbind(id,mes,importe))

Tenemos un identificador, una variable mes y un importe y deseamos obtener el menor importe por mes el primer paso a realizar es ordenar el data frame de R por ese identificador, el mes y el importe en orden descendente:

df <- df[with(df,order(id,mes,-importe)),]

Una vez ordenado el data frame de R tenemos que seleccionar el último elemento por id para seleccionar aquellos clientes con menor importe:

library(dplyr)
df_bajo_importe <- df %>% group_by(id) %>% filter(row_number()==n())

Si deseamos seleccionar el mayor importe hacemos lo mismo:

library(dplyr)
df_bajo_importe <- df %>% group_by(id) %>% filter(row_number()==1)

Las funciones group_by unidas a filter(row_number) equivalen a esos first y last de SAS. Saludos.

Máximo por registro de una serie de variables carácter en SAS

Un lector del blog preguntaba como obtener el valor máximo dentro de un registro, por fila, de una sucesión de variables caracter; evidentemente la función max no servía porque es específica para variables numéricas. La duda la planteaba del siguiente modo:

Pero tengo una duda que no soy capaz de sacar y no veo ninguna cosa parecida para poder sacarlo, a ver si me puedes ayudar, o si no, pues me dices que no y no hay ningún problema.

Tengo un data de este estilo:
Nombre    Clave1    Clave2    Clave3
Ana             A            A            B
Pepe           H            M            C
Juan           A             A            A

El tema es que necesito calcular el máximo de todas las claves para cada persona, es decir,
Nombre    Clave1    Clave2    Clave3    Max
Ana             A            A            B         B
Pepe           H            M            C        M
Juan           A             A            A         A

Como son letras, no me funciona el max en el proc sql y tampoco sé ninguna función que pueda pasarme las letras a numéricas.
Está claro que la única manera que se me ocurre es transformando las letras a números con un case, hacer el máximo y después transformarlo otra vez a letras, pero es por si sabes alguna manera mejor de hacerlo.

La solución que le planteo se realiza con un array donde seleccionamos sólo las variables clave:

Data datos;
input Nombre $ Clave1 $ Clave2 $ Clave3 $;
datalines;
Ana A A B
Pepe H M C
Juan A A A
;run;

data datos;
set datos;
array cl (*) clave:;
maximo=cl(1);
do i=1 to dim(cl);
if cl(i)>maximo then maximo=cl(i);
end;
drop i;
run;

Inicializamos el máximo al primer elemento del array y vamos recorriendo las variables, si una es mayor que la otra se modifica el máximo. Espero que le sirva a algún otro lector. Saludos.

Truco SAS. Tablas de una librería en una macro variable

Me plantean una duda, como crear un conjunto de datos con las tablas de una librería en sas y posteriormente generar una macro variable con ellos, esta es una entrada análoga a otra del blog pero sirve para recordar como funciona el ODS de SAS y el PROC DATASETS un procedimiento que no he usado habitualmente. Lo primero que vamos a hacer es observar que resultados arroja el PROC DATASETS en su sintaxis más sencilla, ver los contenidos de una librería:

 ods trace on;
proc datasets lib=datos;
quit;
ods trace off;

Recordamos que ODS (Output Delivery System) TRACE ON nos permite ver en la log de SAS los elementos que se obtienen como resultado, en este caso, el más sencillo, tenemos:

Output Added:
-------------
Name: Directory
Label: Directory Information
Template: Base.Datasets.Directory
Path: Datasets.Directory
-------------

Output Added:
-------------
Name: Members
Label: Library Members
Template: Base.Datasets.Members
Path: Datasets.Members
-------------

Evidentemente nos interesa Members para poner en una tabla SAS todos los miembros de la librería:

ods output Members=tablas;
proc datasets lib=datos ;
quit;

Ahora tenemos que meter en una macrovariable todos los elementos del campo name de la tabla SAS que hemos generado. Para ello en vez de emplear el habitual PROC SQL podemos usar una concatenación sobre los valores de name que diera como resultado final la macrovariable con la lista de las tablas: Sigue leyendo Truco SAS. Tablas de una librería en una macro variable

Bucle de fechas con SAS para tablas particionadas

Partimos de un mes inicial hasta un mes final es necesario crear una tabla SAS con dos variables, el inicio del mes y el final del mes. Trabajo con fechas en SAS que todos sabemos es una tarea un "poco ardua".  El título de la entrada también es un poco peculiar pero es la respuesta a la duda que planteaba un lector:

Cogemos dos fechas en formato yyyymmaa
Ej: 20150101 a 2016131

Necesito una salida como la siguiente
20150101   20150131
20150201   20150228
20150301   20150331
20150401   20150430
.

20161101  20161130
20161201  20161231

Pero para que los datos pedidos en este periodo salgan en una tabla por mes con un proc sql  ya diseñado que funciona pero sin particionarlo en una tabla por mes en el log

Se me han ocurrido varias formas de hacerlo pero a continuación os planteo la siguiente. Como referencia hemos de irnos a una entrada anterior del blog, una entrada del 2008 cuando puse en marcha analisisydecision.es

*IDENTIFICA EL ULTIMO DIA DE UN MES;
%macro finmes(fec);
intnx("month",&fec.,1)-1
%mend;


data bucle (drop=i);
do i=201501 to 201612;
    if mod(i,100)=13 then i = i + 88;
else do;
    inicio = i * 100 + 1;
    *PRIMERO TRANSFORMAMOS EN FECHA SAS;
    fin = mdy(mod(i,100),1,int(i/100));
    *DESPUES OBTENEMOS EL ULTIMO DIA DEL MES;
    fin = %finmes(fin);
    *POR ULTIMO LO TRANSFORMAMOS A NUMERICO;
    fin = year(fin)*10000+month(fin)*100+day(fin);
    output;
end;
end;
run;

Lo he hecho de una forma sencilla, se trata de un bucle DO desde el mes inicial a el mes final, en realidad son unos 90 números sin embargo si el módulo del número, el mes, está entre 1 y 12 entonces identifica el primer día del mes e identifica el último día del mes transformando el número a fecha SAS primero, obteniendo el último día después y por último lo transforma del modo más sencillo a un número que pueda entender la partición. Es un bucle SAS susceptible de ser parametrizado. Saludos.

Como obtener los centroides de municipios con SAS. Mapas con SGPLOT

mapa_municipios_sas2

Un amigo y lector del blog me ha pedido un mapa de códigos postales donde poder identificar los centroides para andar calculando distancias a otros puntos. Yo no tengo un mapa de España por códigos postales para poder usar con fines comerciales, pero si cuento en el blog como poder obtenerlo bajo ciertas condiciones. Lo que si puedo contar a Juan es como hacer un mapa por municipios con SAS, aunque ya he hablado de ello hay ciertos aspectos que pueden ser interesantes. y todo empieza donde siempre http://www.gadm.org/country la web donde tenemos los mapas "libres" por países, seleccionáis Spain y el formato shapefile una vez descargados los mapas en vuestros equipos empezamos con el trabajo en SAS:

proc mapimport datafile="\directorio\mapa\ESP_adm_shp.shp"
out = work.espania;
run;
proc contents;quit;

mapa_municipios_sas1

El procedimiento MAPIMPORT ha creado un conjunto de datos SAS donde tenemos caracterizados todos los polígonos que componen el shapefile. Entonces si tenemos que calcular el centroide de un municipio con SAS sugiero realizar un PROC SQL de la siguiente forma Sigue leyendo Como obtener los centroides de municipios con SAS. Mapas con SGPLOT

Qué pasa si uso una regresión de poisson en vez de una regresión logística

Para un tema de mi trabajo voy a utilizar una regresión de poisson en vez de una regresión logística, el evento es si o no y no tiene nada que ver el tiempo, ni se puede contabilizar como un número, pero a efectos prácticos es mejor para mi usar una regresión de poisson. Entonces, ¿qué pasa si hago una poisson en vez de binomial? Como siempre si mi n es muy grande hay relación entre ambas distribuciones. Pero yo quiero saber si me puede clasificar mis registros igual una regresión de poisson y una binomial y se me ha ocurrido hacer un ejercicio teórico muy simple.

Construyo con SAS 10.000 datos aleatorios con las variables independientes x e y normalmente distribuidas y la variable dependiente z que es una función logística "perfecta" de x e y:

data logistica;
do i=1 to 10000;
x=rannor(8);
y=rannor(2);
prob=1/(1+exp(-(-10+5*x-5*y)));
z=ranbin(8,1,prob);
output;
end;
drop i;
run;

data entrenamiento test;
set logistica;
if ranuni(6)>0.8 then output test;
else output entrenamiento;
run;

proc freq data=entrenamiento;
tables z;
quit;

Separo los datos en entrenamiento y test y vemos que un 8% aproximadamente de mis registros tienen valor 1. Sobre estos datos hago una logística y una poisson y veo los parámetros Sigue leyendo Qué pasa si uso una regresión de poisson en vez de una regresión logística

Test de bondad de ajuste con SAS

Pregunta que me han hecho hoy. Cómo hacer un test de bondad de ajuste con SAS y la respuesta que he dado:

data datos_aleatorios;
do i=1 to 200000;
*GENERAMOS UNAS VARIABLES ALEATORIAS;
variable_gamma = rangam(89,450); 
variable_exponencial = ranexp(23)*100+0.17045;
output;
end;
run;

*ods select ParameterEstimates GoodnessOfFit ;
proc univariate data=datos_aleatorios;
   var var:;
   histogram /   gamma;
run;

Mucho cuidado con estos test de hipótesis. Yo suelo conformarme con ver la tabla de cuantiles. Saludos.

 

Truco SAS. Como leer PC Axis con SAS

Estoy leyendo información del INE que tiene que terminar cargándose en SAS y estos datos están en formato PC Axis. Existen macros en SAS para generar datasets a partir de PC Axis pero la verdad es que no he llegado a entender muy bien como funcionan y tras varios errores la mejor opción que he encontrado es emplear R y el paquete pxR que han creado algunos miembros de la Comunidad de R-Hispano. Como realizo esta tarea es más que sencillo:

En R realizamos la importación del archivo *.px:

nacionalidad = read.px("ubicacion\\seccion_censal_nacionalidad.px")
nacionalidad = data.frame(nacionalidad)
write.csv( nacionalidad, file = "ubicacion\\nacionalidad.csv" )

Hemos generado un csv que importamos desde SAS:

proc import datafile="ubicacion\nacionalidad.csv"
     out=nacionalidades
     dbms=csv
     replace;
     getnames=yes;
run;

También quería aprovechar esta entrada para comentaros que es preferible usar los viejos csv para mover archivos entre R  y SAS que usar librerías como SASxport que generan ficheros "portables" de SAS, aunque los ficheros "portables" garantizan que se puedan leer con distintas versiones de SAS este paquete tarda mucho (demasiado) tiempo en crear los archivos. Y si alguien tiene una versión más sencilla de la macro de SAS que mande el link. Saludos.

KNN con SAS. Mejorando K-Means

Imagen de previsualización de YouTube

La clasificación por k vecinos más cercanos es EL MÉTODO supervisado no paramétrico. El KNN, si empleamos las siglas en inglés, clasifica las observaciones en función de su probabilidad de pertenecer a uno u otro grupo, en el video que encabeza la entrada queda muy bien explicado. El caso es que tenemos la posibilidad de realizar esta clasificación con SAS STAT y el PROC DISCRIM y me parece interesante dedicarle unas líneas. Hace años ya hablamos de segmentación con SAS  y vamos a emplear los mismos datos para ilustrar esta entrada. Primero generamos un conjunto de datos con datos simulados de 3 esferas que clasificamos en 3 grupos:

data pelota;
do i = 1 to 1000;
a=0; b=5; x = a+(b-a)*ranuni(34);
a=0; b=5; y = a+(b-a)*ranuni(14);
grupo=1;
distancia = sqrt(((x-2.5)**2)+((y-2.5)**2));
if distancia < 2.5 then output;
end;
run;

data pelota1;
set pelota;
grupo=1;
run;

data pelota2;
set pelota;
x = x+4.5;
grupo=2;
run;

data pelota3;
set pelota;
x = x+2.5;
y = y+3.5;
grupo=3;
run;

data datos;
set pelota1 pelota2 pelota3;
run;

proc gplot data=datos;
	plot y * x = grupo; 
run;quit;

KNN_SAS1

Si realizamos un análisis mediante k-means sin asignar centroides obtenemos esta clasificación Sigue leyendo KNN con SAS. Mejorando K-Means

Interpretación de los parámetros de un modelo GLM

Muchos estudiantes  terminarán trabajando con GLM que siguen buscando relaciones lineales en multitud de organizaciones a lo largo del planeta. Y hoy quería ayudar a esos estudiantes  a interpretar los parámetros resultantes de un GLM, más concretamente los resultados de un PROC GENMOD de SAS aunque lo que vaya a contar ahora se puede extrapolar a otras salidas de SAS o R. En la línea de siempre no entro en aspectos teóricos y os remito a los apuntes del profesor Juan Miguel Marín. Con un GLM al final lo que buscamos (como siempre) es distinguir lo que es aleatorio de lo que es debido al azar a través de relaciones lineales de un modo similar a como lo hace una regresión lineal, sin embargo los GLM nos permiten que nuestra variable dependiente no sólo siga una distribución normal, puede seguir otras distribuciones como Gamma, Poisson o Binomial. Además un GLM puede trabajar indistintamente con variables categóricas y numéricas pero yo recomiendo trabajar siempre con variables categóricas y en la práctica cuando realizamos un modelo de esta tipo siempre realizaremos agrupaciones de variables numéricas. Si disponemos de variables agrupadas, de factores, los parámetros de los modelos nos servirán para saber como se comporta nuestra variable dependiente a lo largo de cada nivel del factor.

El modelo siempre fija un nivel base del factor, un nivel que promedia nuestros datos y el resto de niveles corrigen el promedio en base al coeficiente estimado. Imaginemos que modelizamos el número de abandonos en 3 carreras de coches, cuando la carrera se disputa en un circuito A el número de abandonos es 5, sin embargo en el circuito B son 10 y en el circuito C son 15. Si fijamos como nivel base de nuestro factor circuito el B tendríamos un modelo de este modo abandonos = 10 + 0.5*es circuito A + 1*es circuito B + 1.5*es circuito C + Error. El nivel base promedia nuestro modelo por lo que va multiplicado por 1 y el resto de niveles se corrigen por su multiplicador. Esta es la base de la modelización multivariante en el sector asegurador. Veamos en un ejemplo como se articulan los parámetros de estos modelos. Simulamos unos datos con la probabilidad de tener un siniestro por edad, zona y edad:

data datos_aleatorios;
do idcliente = 1 to 2000;
if ranuni(1) >= 0.75 then sexo = "F";
else sexo="M";
edad = ranpoi(45,40);
if ranuni(8)>=0.9 then zona=1;
else if ranuni(8)>0.7 then zona=2;
else if ranuni(8)>0.4 then zona=3;
else zona=4;
output;end;
run;

data datos_aleatorios;
set datos_aleatorios;
if zona=1 then incremento_zona = 0.1+(0.5-0.1)*ranuni(8);
if zona=2 then incremento_zona = 0.1+(0.7-0.1)*ranuni(8);
if zona=3 then incremento_zona = 0.1+(0.2-0.1)*ranuni(8);
if zona=4 then incremento_zona = 0.1+(0.9-0.1)*ranuni(8);

incremento_edad=exp(1/edad*10)-1;

sini = (ranuni(9) - sum(incremento_zona, -incremento_edad))>0.8 ;
run;

Se da una probabilidad aleatoria de tener un siniestro que se ve incrementada o decrementada por la zona y la edad, el sexo, aunque aparece, no influye. El número de siniestros suponemos que sigue una distribución de poisson. Para entender mejor como funciona un GLM vamos a agregar los datos por los factores en estudio y contamos el número de clientes sumando el número de siniestros:

proc sql;
create table datos_agregados as select
sexo,
case
when edad<= 30 then "1 menos 30"
when edad<= 40 then "2 31-40"
when edad<= 50 then "3 41-50"
else "4 mas 50" end as edad,
zona,
log(count(idcliente)) as exposicion,
sum(sini) as sini
from datos_aleatorios
group by 1,2,3;
quit;

Nuestros datos tienen que ir ponderados por el logaritmo del número de clientes, será nuestro offset, ya que no es lo mismo tener un siniestro en un grupo de 2 clientes que un siniestro en un grupo de 20 clientes. Ponderados por el logaritmo porque siempre cuesta menos trabajar con números pequeños y además tienen unos superpoderes de los que no somos conscientes hasta que trabajamos con ellos. Ahora estos datos son los que emplearemos para el modelo:

proc genmod data=datos_agregados;
class sexo edad zona;
model sini = sexo edad zona / dist = poisson
link = log
offset = exposicion;
run;

GENMOD como todos los procedimientos de SAS necesita que le indiquemos las variables categóricas, en model especificamos el modelo y las opciones son los aspectos más interesantes. En dist especificamos la distribución de nuestra variable dependiente en link la función de enlace que vamos a emplear y como offset para ponderar la variable exposición que hemos creado a la hora de agregar los datos. Y los parámetros que estima este modelo son:

parametros_modelo_GENMOD1

El intervalo de confianza de algunos estimadores contiene el valor 0, esos factores no son significativos como es el caso del sexo o bien puede haber agrupación de niveles de factores como es el caso de la edad entre 31 y 50 años o la zona 2 que puede unirse con otra zona. Vemos que el estimador del nivel base siempre es el último nivel del factor (esto puede cambiarse) y toma valores 0 y no 1 como habíamos usado en el ejemplo, para transformarlo en 1 sólo hemos de realizar el exponencial:

Parameter   Estimate
Intercept 0.062
sexo F 0.932
sexo M 1.000
edad 1 menos 30 2.951
edad 2 31-40 2.062
edad 3 41-50 1.494
edad 4 mas 50 1.000
zona 1 1.854
zona 2 1.218
zona 3 3.091
zona 4 1.000
Scale 1.000

Vemos que la zona 3 tiene casi el triple de siniestralidad que la zona 4 y lo mismo sucede con las edades jóvenes frente a las mayores edades, en cuanto al sexo que no fue significativo tenemos que las mujeres tienen un 7% menos de siniestralidad. Algunos resultados, aunque no salgan estadísticamente significativos, es evidente que pueden interesarnos comercialmente ya que mi producto puede dar un descuento a las mujeres y aunque sea pequeño se puede mantener, igual razonamiento para algunas zonas o grupos de edad susceptibles de unirse entre ellos. Este ejemplo es muy burdo pero aquellos que empiecen a trabajar con GLM se van a encontrar situaciones de este tipo, es necesario interpretar los parámetros estimados para describir como funciona el modelo pero igual de  importante es la agrupación de factores y el posterior suavizado de los parámetros.