Hace tiempo ya os propuse una chapuza para eliminar outliers de forma multivariante. Por supuesto, quedaba eliminar outliers en una variable: recortar los valores extremos en aquellas variables cuantitativas que deseemos. Para ello os propongo una macro que no considero muy compleja y que analizaré con mayor detalle; pero, lo primero, la macro al completo:
%macro elimina_outliers(
varib, /* VARIABLE PARA ELIMINAR EL OUTLIER */
entrada, /* DATASET DE ENTRADA */
salida, /* DATASET DE SALIDA, PUEDE SER EL MISMO DE ENTRADA */
corte_inferior, /* % DE CORTE INFERIOR */
corte_superior /* % DE CORTE SUPERIOR */
);
* CREAMOS LOS PERCENTILES;
data _null_;
call symput ("lim1", compress(0 + &corte_inferior.));
call symput ("lim2", compress(100 - &corte_superior.));
run;
* PREPARAMOS MV CON LOS NOMBRES QUE OBTENDREMOS DEL PROC UNIVARIATE;
data _null_;
call symput ('nom_lim1', compress("P_" || tranwrd("&lim1.", '.', '_')));
call symput ('nom_lim2', compress("P_" || tranwrd("&lim2.", '.', '_')));
run;
* EL UNIVARIATE GENERA UNA SALIDA SOLO CON LOS PERCENTILES DESEADOS;
proc univariate data=&entrada. noprint;
var &varib.;
output out=sal pctlpre=P_ pctlpts=&lim1., &lim2.;
run;
* CREAMOS MV CON LOS CORTES DESEADOS;
data _null_;
set sal;
call symput("inf", &nom_lim1.);
call symput("sup", &nom_lim2.);
run;
* REALIZAMOS EL FILTRO;
data &salida.;
set &entrada.;
if &varib. > &inf. and &varib. < &sup.;
run;
proc delete data=sal;
run;
%mend;
Su ejemplo de uso correspondiente:
data ent;
do i = 1 to 10000;
importe = rannor(2) * 1000;
if ranuni(3) >= 0.95 then importe = importe * 10;
if ranuni(4) >= 0.05 then importe = importe / 10;
output;
end;
run;
* ANALIZAMOS LA VARIABLE IMPORTE;
proc univariate data=ent;
var importe;
histogram;
run;
* RECORTAMOS UN 5% POR ARRIBA Y UN 5% POR DEBAJO;
%elimina_outliers(importe, ent, salida, 5, 5);
proc univariate data=salida;
var importe;
histogram;
run;
Tenemos dos histogramas: uno es “imposible”, pero el otro nos permite conocer un poco mejor la distribución de la variable importe tras recortar un 5% las observaciones tanto por arriba como por abajo. Analicemos brevemente el código utilizado en la macro:
%macro elimina_outliers(
varib, /* VARIABLE PARA ELIMINAR EL OUTLIER */
entrada, /* DATASET DE ENTRADA */
salida, /* DATASET DE SALIDA, PUEDE SER EL MISMO DE ENTRADA */
corte_inferior, /* % DE CORTE INFERIOR */
corte_superior /* % DE CORTE SUPERIOR */
);
Los parámetros de la macro son sencillos: la variable que recortamos (varib), el dataset de entrada (entrada), el dataset de salida (salida) que puede ser el mismo, el corte inferior y el corte superior en porcentaje (si no se quiere recortar, ponemos 0).
* CREAMOS LOS PERCENTILES;
data _null_;
call symput ("lim1", compress(0 + &corte_inferior.));
call symput ("lim2", compress(100 - &corte_superior.));
run;
* PREPARAMOS MV CON LOS NOMBRES QUE OBTENDREMOS DEL PROC UNIVARIATE;
data _null_;
call symput ('nom_lim1', compress("P_" || tranwrd("&lim1.", '.', '_')));
call symput ('nom_lim2', compress("P_" || tranwrd("&lim2.", '.', '_')));
run;
En los parámetros ponemos los porcentajes, y esos porcentajes se tienen que transformar en percentiles. Después creamos los nombres para los percentiles de forma que el PROC UNIVARIATE los “entienda”, es decir, con la forma P_97_5 o P_5. Os lo pongo en dos pasos para hacerlo lo más comprensible posible.
* EL UNIVARIATE GENERA UNA SALIDA SOLO CON LOS PERCENTILES DESEADOS;
proc univariate data=&entrada. noprint;
var &varib.;
output out=sal pctlpre=P_ pctlpts=&lim1., &lim2.;
run;
Ésta es la parte más interesante de este proceso: el PROC UNIVARIATE va a crear un dataset con el valor de los percentiles que queremos recortar; esto se realiza en la sentencia OUTPUT con la instrucción pctlpre=P_ y pctlpts=limite_inferior, limite_superior. Los límites están en unas macrovariables y el conjunto de datos sal contendrá los valores sobre los que recortamos la variable.
* CREAMOS MV CON LOS CORTES DESEADOS;
data _null_;
set sal;
call symput("inf", &nom_lim1.);
call symput("sup", &nom_lim2.);
run;
* REALIZAMOS EL FILTRO;
data &salida.;
set &entrada.;
if &varib. > &inf. and &varib. < &sup.;
run;
Por último, creamos dos macrovariables con los valores de corte y realizamos un paso DATA donde filtramos por esos valores.
Creo que es un código con algún aspecto interesante y que puede seros práctico a la hora de analizar de forma univariante algunas variables. Saludos.