miércoles, 30 de abril de 2014

Ejemplo procedimiento ANOVA en SAS


En este post incluímos un ejemplo muy sencillo de empleo de procedimiento anova en SAS. En el ejemplo queremos ver la influencia de la variable ciudad en el consumo de un conjunto de clientes.  Se trata de validar si la ciudad influye en el consumo medio cliente, para ello empleamos el procedimiento anova.



Sin entrar a definir con detalle lo que es el Análisis de Varianza (ANOVA, ANalysis Of VAriance), si indicar que es un método que se basa en el estudio de las varianzas y que se puede utilizar para establecer diferencias entre medias arítmeticas de 2 o más grupos.

En nuestro ejemplo, vamos a analizar las medias de consumo mensual de clientes en base a su ciudad, pudiendo tomar la variable ciudad tres valores (Madrid, Barcelona y Valencia), para saber si existe alguna relación entre la ciudad y el consumo, Queremos comprobar si hay diferencias estadísticas significativas en las medias de los tres grupos o no las hay.

En primer lugar realizamos un proc means en base a la variable ciudad:

proc means data=consumo_mensual noprint nway;
   class ciudad;
   var arpu;
   output out=consumo_mensual_ag(drop=_type_)
   mean(arpu) = arpu_medio;
run;

Obtenemos lo siguiente:

ciudad
_freq_
arpu_medio
Madrid
6835
14,90
Barcelona
5922
15,03
Valencia
5075
15,00

A continuación ejecutamos el procedimiento ANOVA:

proc anova data=consumo_mensual;
  class ciudad;
  model arpu = ciudad; /* variables sobre las que estamos validando la posible dependencia */
  means ciudad;
run;

Obtenemos lo siguiente:
The ANOVA Procedure
Class Level Information
Class
Levels
Values
ciudad
3
Barcelona Madrid Valencia
 
Number of Observations Read
17832
Number of Observations Used
17832
Dependent variable: arpu
 
Source
DF
Sum of Squares
Mean Square
F Value
Pr > F
Model
2
60.24488295
30.12244147
0.40
0.6690
Error
17829
1336051.8277
74.93700307


Corrected Total
17831
1336112.0726



 
R-Square
Coeff Var
Root MSE
arpu Mean
0.000045
57.82388
8.656616
14.970659
 
Source
DF
Anova SS
Mean Square
F Value
Pr > F
ciudad
2
60.24488295
30.12244147
0.40
0.6690
 
Level of ciudad
N
arpu Mean
arpu Std Dev
Barcelona
5922
15.02762749
8.67528423
Madrid
6835
14.89803950
8.62331428
Valencia
5075
15.00198621
8.67954484

Del resultado obtenido nos llama la atención el valor obtenido en el p-valor (Pr > F). El p-valor nos vale para constrastar nuestra hipótesis nula que sería que los tres valores medios no muestren diferencias estadísticamente significativas.

El valor de p-valor es significativo, ya que se suelen dar como valor umbral para rechazar la hipótesis p-valor entorno a 0,05.  Como el p-valor es mayor que 0,05, entonces no podemos rechazar la hipótesis nula de que todas las medias son iguales, aceptando que la provincia del cliente no influye  en el consumo mensual  o al menos no tenemos evidencias estadísticas que apoyen esta hipótesis.

Formación SAS

lunes, 28 de abril de 2014

Calidad de datos: Detectar y eliminar duplicados en tablas SAS

En el siguiente post mostramos diferentes formas de detectar y eliminar duplicados en SAS. Es un problema que nos encontramos con frecuencia en el tratamiento de datos y que debe estar bien controlado para asegurar la calidad de nuestra información.



En primer lugar mostramos diferentes técnicas para detectar duplicados:

/* Creamos tabla de ejemplo */

data ventas(drop = i j);
length oficina $100.;
oficina = 'Madrid';
do j = 1 to 20;
producto = compress('P'||j);
unidades = round(ranuni(1)*20);
fecha = 20140401;
output;
if unidades > 15 then do;
       fecha = 20140402;
       output;
end;
end;
oficina = 'Barcelona';
do j = 1 to 20;
producto = compress('P'||j);
unidades = round(ranuni(1)*20);
fecha = 20140401;
output;
if unidades > 15 then do;
       fecha = 20140402;
       output;
end;
end;
oficina = 'Valencia';
do j = 1 to 20;
producto = compress('P'||j);
unidades = round(ranuni(1)*20);
fecha = 20140401;
output;
if unidades > 15 then do;
       fecha = 20140402;
       output;
end;
end;
oficina = 'Lisboa';
do j = 1 to 20;
producto = compress('P'||j);
unidades = round(ranuni(1)*20);
fecha = 20140401;
output;
if unidades > 15 then do;
       fecha = 20140402;
       output;
end;
end;
run;

/* 1. Detección duplicados vía proc sort:
Ordenamos por los campos en base a los que queremos detectar duplicados
con la clausula nodupkeys indicamos que elimine duplicados en la salida
en la clausula dupout indicamos dónde debe dejar los duplicados */

proc sort data=ventas out=ventas1 nodupkeys dupout=duplicados;
by oficina producto;
run;

/* 2. Detección duplicados vía proc sql:
Realizamos una agregado por los campos en base a los que queremos detectar duplicados
con la condición having nos quedamos con los que aparezcan más de 1 vez.
 */

proc sql;
create table duplicados as
(select oficina, producto, count(*)
from ventas
group by oficina, producto
having count(*) > 1);
quit;



/* 3. Combinar proc sort con sentencias fisrt y last. Detectamos los duplicados ordenando por la clave y buscando aquellos cuyo primer registro de una clave es diferente al último registro de esa clave. */

data ventas1;
set ventas;
clave = compress(oficina||producto);
run;

proc sort data=ventas1;
by clave;
run;

data duplicados;
set ventas1;
by clave;
if first.clave and not last.clave; /* Si no está duplicado el registro único debe ser first y last */
run;

/* 4. Empleando el procedimiento proc summary y quedandonos con los que tengan frecuencia mayor a 1 para la clave fijada */

proc summary data=ventas nway;
class oficina producto;
output out=duplicados(where=(_FREQ_>=2));
run;


El siguiente paso se trata de eliminar estos duplicados, según el criterio que establezcamos:


/* Eliminar */

/* Nos quedamos sólo con uno de los registros duplicados. Realizamos la eliminación
en base a diferentes criterios */

/* 1. Nos quedamos con el que tenga fecha más reciente */

proc sort data=ventas;
by oficina producto descending fecha; /* ordenamos por la fecha de forma descendente */
run;

proc sort data=ventas out=ventas1 nodupkeys;
by oficina producto;
run;


/* 2. Nos quedamos con el que cumpla un criterio fijado */

proc sort data=ventas;
by oficina producto;
run;


data ventas1;
retain oficina_ant producto_ant;
set ventas;
if (oficina_ant = oficina) and (producto_ant = producto) then do;
       if fecha <= 20140402 then delete; /* Borramos los registros duplicados que cumplan determinada condición */
end;
oficina_ant = oficina;
producto_ant = producto;
run;




miércoles, 23 de abril de 2014

¿Soporta tu plataforma B.I. el origen de datos que quieres analizar?


Aunque las últimas versiones de la mayoría de las plataformas B.I. soportan una amplia gama de orígenes de datos, esta es una pregunta frecuente en la cual intervienen la versión de tu herramienta B.I, la versión de la base de datos, formato de fichero o ERP que actúa como origen de datos y el sistema operativo.



En el caso de SAS se pueden resolver estas dudas consultando la SAS/ACCESS Validation Matrix: seleccionas la versión de SAS, la base de datos, el sistema operativo y tienes la respuesta.

support.sas.com/matrix/list?SAS=All&Engine=All&OS=All&googleTrack=on



Procedimientos de agregación en SAS (proc sql, proc summary y proc means)

En el siguiente post mostramos con ejemplos los diferentes métodos de agregación de los que disponemos en el módulo BASE de SAS. Se incluye un script con ejemplos utilizando el procedimientos generalista 'proc sql' o bien procedimientos propios de agregación como 'proc means' o 'proc summary'


Agregados en SAS

En primer lugar mostramos el mismo agregado realizado en vía proc sql y vía proc summary o proc means:

proc sql:
proc sql;
create table agregado as
(select dimension1, dimension2, sum(indicador1), avg(indicador2), max(indicador3)
from detalle
group by dimension1, dimension2);
quit;

proc summary:
proc summary data=detalle nway;
   class dimension1 dimension2;
   var indicador1 indicador2 indicador3;
   output out=agregado(drop=_type_ _freq_)
   sum=indicador1 ;
run;

proc means:
proc means data=detalle noprint nway;
   class dimension1 dimension2;
   var indicador1 indicador2 indicador3;;
   output out=agregado(drop=_type_ _freq_)
   sum(indicador1) = suma_indicador1 mean(indicador2) = media_ indicador2 max(indicador3) = max_indicador3 ;
run;

Un resumen de equivalencias sería:
  


Funcionalidad

Procedimiento agregación

Proc sql

Tabla entrada

data=tabla_detalle

from tabla_detalle

Tabla salida

output out =tabla_agregado

create table tabla_agregado as

Indicadores de análisis

var indicador1 indicador2 …

Select sum(indicador1), mean(indicador2), ..

Variables de agregación

class indicador1, indicador2,…

group by dimension1, dimension2, …

Estadísticas a obtener

Especificadas en el procedimiento agregación

Especificadas en el proc sql

En los procedimientos de agregación tenemos la opción de dar la salida a último nivel de agregación o mostrando todos los niveles. Para quedarnos con el últimos nivel de agregación en base a la clave definida añadimos la claúsula nway. La clave de agregación queda definida en la claúsula class y en el output  indicamos la tabla de salida. Utilizamos la claúsula drop para eliminar las variables internas que genera el procedimiento y no queremos mostrar en la salida.  Finalmente definimos los valores estadísticos a obtener que en el caso del ejemplo son sum, mean y max y las variables donde los alojamos.  Ambos procedimientos dan la opción de obtener otros muchos valores estadísticos tales como:  rango (range), varianza(var), desviación estándar(stddev), etc..

En el procedimiento proc means se añade la claúsula noprint ya que por defecto da la salida en el output de SAS.  La principal diferencia entre proc summary y proc means es ésta de la salida y que proc means por defecto calcula valores estadísticos para todas las variables numéricas aunque no sean detalladas explícitamente en la salida.

Existen notables diferencias entre el uso e proc sql o de un procedimiento de agregación, siendo quizás la principal que el procedimiento de agregación obtiene agregados a todos los niveles y combinaciones posibles para las variables definidas en la clave, pudiendo manejar el nivel que queramos utilizando la variable interna _type_.
A continuación se incluye un script en el que partiendo de una tabla de detalle que se construye en el propio script de obtienen agregados a diferentes niveles utilizando tanto proc summary como proc means.

data ventas(drop = i j);
length oficina $100.;
oficina = 'Madrid';
do i = 1 to 10;
vendedor = compress('m_vendedor'||i);
       do j = 1 to 20;
       producto = compress('P'||j);
       unidades = round(ranuni(1)*20);
       output;
       end;
end;
oficina = 'Barcelona';
do i = 1 to 10;
vendedor = compress('b_vendedor'||i);
       do j = 1 to 20;
       producto = compress('P'||j);
       unidades = round(ranuni(1)*20);
       output;
       end;
end;
oficina = 'Valencia';
do i = 1 to 10;
vendedor = compress('v_vendedor'||i);
       do j = 1 to 20;
       producto = compress('P'||j);
       unidades = round(ranuni(1)*20);
       output;
       end;
end;
oficina = 'Lisboa';
do i = 1 to 10;
vendedor = compress('l_vendedor'||i);
       do j = 1 to 20;
       producto = compress('P'||j);
       unidades = round(ranuni(1)*20);
       output;
       end;
end;
run;


/* Agregado empleando proc sql */
/* Nivel.1 */
/* Agregado a nivel de variable oficina */
proc sql;
create table ag1_oficina as (
select oficina, sum(unidades) as sum1
from ventas
group by oficina);
quit;

proc summary data=ventas  NWAY ;
class oficina;
var unidades;
output out=ag1_oficina(drop = _type_ _freq_ ) sum = unidades;
run;

/* Agregado a nivel de variable vendedor */
proc sql;
create table ag1_vendedor as (
select vendedor, sum(unidades) as sum1
from ventas
group by vendedor);
quit;

proc summary data=ventas  NWAY ;
class vendedor;
var unidades;
output out=ag1_vendedor(drop = _type_ _freq_ ) sum = unidades;
run;

/* Agregado a nivel de variable producto */
proc sql;
create table ag1_producto as (
select producto, sum(unidades) as sum1
from ventas
group by producto);
quit;

proc summary data=ventas  NWAY ;
class producto;
var unidades;
output out=ag1_producto(drop = _type_ _freq_ ) sum = unidades;
run;

/* Nivel.2 */
/* Agregado a nivel de variables oficina y vendedor */
proc sql;
create table ag2_ofi_vend as (
select oficina, vendedor, sum(unidades) as sum2
from ventas
group by oficina, vendedor);
quit;

proc summary data=ventas  NWAY ;
class oficina vendedor;
var unidades;
output out=ag2_ofi_vend(drop = _type_ _freq_ ) sum = unidades;
run;

/* Agregado a nivel de variables oficina y producto */
proc sql;
create table ag2_ofi_prod as (
select oficina, producto, sum(unidades) as sum2
from ventas
group by oficina, producto);
quit;

proc summary data=ventas  NWAY ;
class oficina producto;
var unidades;
output out=ag2_ofi_prod(drop = _type_ _freq_ ) sum = unidades;
run;

/* Nivel.3 */
/* Agregado a nivel de variables oficina, vendedor y producto */
proc sql;
create table ag3_ofi_vend_prod as (
select oficina, vendedor, producto, sum(unidades) as sum3
from ventas
group by oficina, vendedor, producto);
quit;

proc summary data=ventas  NWAY ;
class oficina vendedor producto;
var unidades;
output out=ag3_ofi_vend_prod(drop = _type_ _freq_ ) sum = unidades;
run;

/* Empleamos proc summary para obtener todos los niveles de agregación y después
filtramos. Para obtener todos los niveles elimnamos clausul NWAY y controlamos en la
salida el nivel con la variable de sistema _type_ */
proc summary data=ventas ;
class oficina vendedor producto;
var unidades;
output out=ag_total(drop = _freq_ ) sum = unidades;
run;

/* A partir del agregado total y conociendo el valor de la variable _type_ para los diferentes
niveles, podemos sacar agregados a distintos niveles.
Por ejemplo, para obtener el agregado total por oficina, el agregado por oficina_vendedor
y el agregado a más bajo nivel sería */
data ag1_oficina (keep = oficina unidades)
ag2_ofi_vend (keep = oficina vendedor unidades)
ag3_ofi_vend_prod (keep = oficina vendedor producto unidades);
set ag_total;
if _type_ = 4 then output ag1_oficina;
if _type_ = 6 then output ag2_ofi_vend;
if _type_ = 7 then output ag3_ofi_vend_prod;
run;
  
/* Obtenemos el proc means equivalente al proc summary obtenido para el agregado
a más bajo nivel */
proc summary data=ventas  NWAY ;
class oficina vendedor producto;
var unidades;
output out=ag3_ofi_vend_prod(drop = _type_ _freq_ ) sum = unidades;
run;

proc means data=ventas noprint nway;
   class oficina vendedor  producto ;
   output out=ag3_ofi_vend_prod(drop=_type_ _freq_) sum = unidades ;
run;