AutoReg

Codice di esempio

Al fine di rendere piú chiaro il funzionamento e l'utilizzo della procedura, si riporta qui di seguito un esempio di utilizzo con relativa spiegazione.

I dati utilizzati sono stati presi dal sito UC Irvine Machine Learning Repository (una risorsa molto importante per coloro che hanno necessità di testare le proprie capacità su dati reali).
Per il test abbiamo utilizzato il dataset German Credit Data: come indica il titolo, si tratta di dati relativi a buoni e cattivi creditori.
Poiché lo scopo di queste pagine non é quello di creare effettivamente un modello sul rischio di credito di tali controparti, ma unicamente di presentare la procedura, non spiegheremo nel dettaglio le particolarità dei dati o il loro formato: per tali informazioni si legga l'apposita pagina.
In queste pagine, inoltre, non terremo conto di alcuna matrice dei costi.

Prima parte - Caricamento e veloce analisi dei dati
Innanzi tutto, partendo dal file dei dati, generiamo la tabella sas di partenza.


filename stlog 'C:\ ... INDIRIZZO ... \german.data';


data gcd;
 infile stlog;
 input a1 $ a2 a3 $ a4 $ a5 a6 $ a7 $ a8 a9 $ a10 $
	a11 a12 $ a13 a14 $ a15 $ a16 a17 $ a18 a19 $ a20 $ a21;
run;
/*
NOTE: 1000 records were read from the infile STLOG.
      The minimum record length was 77.
      The maximum record length was 81.
NOTE: The data set WORK.GCD has 1000 observations and 21 variables.
NOTE: DATA statement used (Total process time):
      real time           0.14 seconds
      cpu time            0.04 seconds
*/

Per decidere quale categoria attribuire ad ogni variabile, si procede quindi con una veloce analisi delle colonne.


/*********** Variabili Numeriche ***********/
proc sql;
 create table r_a2 as
  select min(a2) as min, max(a2) as max, mean(a2) as media,
	var(a2) as var, count(distinct a2) as num_dist, count(a2) as num
  from gcd;
quit;
/*
Durata in mesi:
 33 valori distinti su 1000 righe, min=4, max=72, media=20.9
 Variabile X.
*/

proc sql;
 create table r_a5 as
  select min(a5) as min, max(a5) as max, mean(a5) as media,
	var(a5) as var, count(distinct a5) as num_dist, count(a5) as num
  from gcd;
quit;
/*
Ammontare del credito:
 921 valori distinti su 1000 righe, min=250, max=18424, media=3271.26
 Variabile Q.
*/

proc sql;
 create table r_a8 as
  select min(a8) as min, max(a8) as max, mean(a8) as media,
	var(a8) as var, count(distinct a8) as num_dist, count(a8) as num
  from gcd;
quit;

title 'a8';
proc freq data=gcd notitle;
 tables a8 / missing;
run;
title;
/*
Rata/Reddito:
 4 valori distinti su 1000 righe, min=1, max=4, media=2.973
 Variabile O.
*/

proc sql;
 create table r_a11 as
  select min(a11) as min, max(a11) as max, mean(a11) as media,
	var(a11) as var, count(distinct a11) as num_dist, count(a11) as num
  from gcd;
quit;

title 'a11';
proc freq data=gcd notitle;
 tables a11 / missing;
run;
title;
/*
Attualmente residente da ... :
 4 valori distinti su 1000 righe, min=1, max=4, media=2.845
 Variabile O.
*/

proc sql;
 create table r_a13 as
  select min(a13) as min, max(a13) as max, mean(a13) as media,
	var(a13) as var, count(distinct a13) as num_dist, count(a13) as num
  from gcd;
quit;
/*
Eta' in anni:
 53 valori distinti su 1000 righe, min=19, max=75, media=35.5
 Variabile X.
*/

proc sql;
 create table r_a16 as
  select min(a16) as min, max(a16) as max, mean(a16) as media,
	var(a16) as var, count(distinct a16) as num_dist, count(a16) as num
  from gcd;
quit;

title 'a16';
proc freq data=gcd notitle;
 tables a16 / missing;
run;
title;
/*
Numero di crediti esistenti in questa banca:
 4 valori distinti su 1000 righe, min=1, max=4, media=1.4
 Variabile O.
*/

proc sql;
 create table r_a18 as
  select min(a18) as min, max(a18) as max, mean(a18) as media,
	var(a18) as var, count(distinct a18) as num_dist, count(a18) as num
  from gcd;
quit;

title 'a18';
proc freq data=gcd notitle;
 tables a18 / missing;
run;
title;
/*
Num. di persone che devono mantenere:
 2 valori distinti su 1000 righe, min=1, max=2, media=1.155
 Variabile C.
*/

proc sql;
 create table r_a21 as
  select min(a21) as min, max(a21) as max, mean(a21) as media,
	var(a21) as var, count(distinct a21) as num_dist, count(a21) as num
  from gcd;
quit;
/*
Variabile risposta (1=Good, 2=Bad):
 2 valori distinti su 1000 righe, min=1, max=2, media=1.3
 Variabile R.
*/



/*********** Variabili Carattere ***********/
proc sql;
 create table r2_a1 as
  select a1, count(*) as num
  from gcd
  group by a1;
quit;
/*
Stato dei conti esistenti:
 Num: OK
 NOTE: Table WORK.R2_A1 created, with 4 rows and 2 columns.
Variabile O (da trasformare in numerica e inserire A14 nei casi particolari).
*/

proc sql;
 create table r2_a3 as
  select a3, count(*) as num
  from gcd
  group by a3;
quit;
/*
Storia dei crediti:
 Num: OK
 NOTE: Table WORK.R2_A3 created, with 5 rows and 2 columns.
Variabile K.
*/

proc sql;
 create table r2_a4 as
  select a4, count(*) as num
  from gcd
  group by a4;
quit;
/*
Motivo:
 Num: Alcune num. basse
 NOTE: Table WORK.R2_A4 created, with 10 rows and 2 columns.
Variabile K.
*/

proc sql;
 create table r2_a6 as
  select a6, count(*) as num
  from gcd
  group by a6;
quit;
/*
Conto di risparmio/Obbligazioni:
 Num: OK
 NOTE: Table WORK.R2_A6 created, with 5 rows and 2 columns.
Variabile O (da trasformare in numerica e inserire A65 nei casi particolari).
*/

proc sql;
 create table r2_a7 as
  select a7, count(*) as num
  from gcd
  group by a7;
quit;
/*
Anzianita' ultimo impiego:
 Num: OK
 NOTE: Table WORK.R2_A7 created, with 5 rows and 2 columns.
Variabile O (trasforma in numerico).
*/

proc sql;
 create table r2_a9 as
  select a9, count(*) as num
  from gcd
  group by a9;
quit;
/*
Sesso e stato civile:
 Num: OK
 NOTE: Table WORK.R2_A9 created, with 4 rows and 2 columns.
Variabile C.
*/

proc sql;
 create table r2_a10 as
  select a10, count(*) as num
  from gcd
  group by a10;
quit;
/*
Altri debitori/Garanti:
 Num: OK
 NOTE: Table WORK.R2_A10 created, with 3 rows and 2 columns.
Variabile C.
*/

proc sql;
 create table r2_a12 as
  select a12, count(*) as num
  from gcd
  group by a12;
quit;
/*
Proprieta':
 Num: OK
 NOTE: Table WORK.R2_A12 created, with 4 rows and 2 columns.
Variabile C.
*/

proc sql;
 create table r2_a14 as
  select a14, count(*) as num
  from gcd
  group by a14;
quit;
/*
Altri piani di pagamento:
 Num: OK
 NOTE: Table WORK.R2_A14 created, with 3 rows and 2 columns.
Variabile C.
*/

proc sql;
 create table r2_a15 as
  select a15, count(*) as num
  from gcd
  group by a15;
quit;
/*
Abitazione:
 Num: OK
 NOTE: Table WORK.R2_A15 created, with 3 rows and 2 columns.
Variabile C.
*/

proc sql;
 create table r2_a17 as
  select a17, count(*) as num
  from gcd
  group by a17;
quit;
/*
Lavoro:
 Num: A171 con solo 22 righe, il resto OK
 NOTE: Table WORK.R2_A17 created, with 4 rows and 2 columns.
Variabile K.
*/

proc sql;
 create table r2_a19 as
  select a19, count(*) as num
  from gcd
  group by a19;
quit;
/*
Telefono:
 Num: OK
 NOTE: Table WORK.R2_A19 created, with 2 rows and 2 columns.
Variabile C.
*/

proc sql;
 create table r2_a20 as
  select a20, count(*) as num
  from gcd
  group by a20;
quit;
/*
Lavoratore straniero:
 Num: OK
 NOTE: Table WORK.R2_A20 created, with 2 rows and 2 columns.
Variabile C.
*/


Seconda parte - Modifiche dei dati
Procediamo quindi con le modifiche alle variabili O e R.


data gcd2;
set gcd;
 /* Trasformiamo a1 (O) in numerico */
 if compress(upcase(a1)) = 'A11' then a1b = 1;
  else if compress(upcase(a1)) = 'A12' then a1b = 2;
   else if compress(upcase(a1)) = 'A13' then a1b = 3;
    else if compress(upcase(a1)) = 'A14' then a1b = 4;
     else a1b = 99;
 /* Trasformiamo a6 (O) in numerico */
 if compress(upcase(a6)) = 'A61' then a6b = 1;
  else if compress(upcase(a6)) = 'A62' then a6b = 2;
   else if compress(upcase(a6)) = 'A63' then a6b = 3;
    else if compress(upcase(a6)) = 'A64' then a6b = 4;
     else if compress(upcase(a6)) = 'A65' then a6b = 5;
      else a6b = 99;
 /* Trasformiamo a7 (O) in numerico */
 if compress(upcase(a7)) = 'A71' then a7b = 1;
  else if compress(upcase(a7)) = 'A72' then a7b = 2;
   else if compress(upcase(a7)) = 'A73' then a7b = 3;
    else if compress(upcase(a7)) = 'A74' then a7b = 4;
     else if compress(upcase(a7)) = 'A75' then a7b = 5;
      else a7b = 99;
 /* Trasformiamo a21 (R) in variabile dummy */
 if a21 = 1 /* Good */ then a21b = 0;
  else if a21 = 2 /* Bad */ then a21b = 1;
run;


Terza parte - Creazione dei file di input
Partendo dalla tabella dei dati e dalle osservazioni fatte in precedenza generiamo le tabelle necessarie alla procedura.


/* Estraiamo dalla tabella i nomi delle colonne */
proc contents data=gcd2 out=gcd3 noprint;
run;


/* Conserviamo unicamente le variabili che ci interessano */
data gcd3;
set gcd3;
keep name type format formatl formatd;
run;


/* Aggiungiamo la colonna relativa all'utilizzo della singola variabile */
data gcd3;
set gcd3;
format utilizzo $1.;
 utilizzo = 'n';
/* Valorizziamo la nuova colonna con la tipologia scelta */
 if compress(name) = 'a1b' then utilizzo = 'o';
 if compress(name) = 'a2' then utilizzo = 'x';
 if compress(name) = 'a3' then utilizzo = 'k';
 if compress(name) = 'a4' then utilizzo = 'k';
 if compress(name) = 'a5' then utilizzo = 'q';
 if compress(name) = 'a6b' then utilizzo = 'o';
 if compress(name) = 'a7b' then utilizzo = 'o';
 if compress(name) = 'a8' then utilizzo = 'o';
 if compress(name) = 'a9' then utilizzo = 'c';
 if compress(name) = 'a10' then utilizzo = 'c';
 if compress(name) = 'a11' then utilizzo = 'o';
 if compress(name) = 'a12' then utilizzo = 'c';
 if compress(name) = 'a13' then utilizzo = 'x';
 if compress(name) = 'a14' then utilizzo = 'c';
 if compress(name) = 'a15' then utilizzo = 'c';
 if compress(name) = 'a16' then utilizzo = 'o';
 if compress(name) = 'a17' then utilizzo = 'k';
 if compress(name) = 'a18' then utilizzo = 'c';
 if compress(name) = 'a19' then utilizzo = 'c';
 if compress(name) = 'a20' then utilizzo = 'c';
 if compress(name) = 'a21b' then utilizzo = 'r';
run;


/* Qui inseriamo i valori "speciali" delle variabili */
data spec;
 length variabile $ 50.;
 length condizione $ 1000.;
 length classe $ 20.;
  variabile = 'a1b';
  condizione = 'a1b = 4';
  classe = 'Missing';
 output;
  variabile = 'a1b';
  condizione = 'a1b = 99';
  classe = 'Anomalo';
 output;
  variabile = 'a6b';
  condizione = 'a6b = 5';
  classe = 'Missing';
 output;
  variabile = 'a6b';
  condizione = 'a6b = 99';
  classe = 'Anomalo';
 output;
  variabile = 'a7b';
  condizione = 'a7b = 99';
  classe = 'Anomalo';
 output;
run;


data escc;
 length var1 $ 50.;
 length var2 $ 50.;
 if _n_ < 1;
run;


/* Quindi creiamo una libreria di esecuzione */
libname loc "C:\ ... INDIRIZZO ... \Data_In";

/* E vi inseriamo i file di input */
/* File dei dati */
data loc.db;
set gcd2;
run;

/* File con le variabili e il loro utilizzo */
data loc.db_var;
set gcd3;
run;

/* File con le condizioni particolari */
data loc.db_cond;
set spec;
run;

/* File delle esclusioni condizionali */
data loc.db_esccon;
set escc;
run;


Quarta parte - Chiamata
Dopo aver fatto girare le definizioni delle tre macro (simpson_c, mod_b_meno_a e classizz), effettuiamo la chiamata.
Nel nostro caso abbiamo supposto di voler avere la possibilità di osservare i singoli passi del processo, quindi inseriamo alcune opzioni utili per il debug (mprint, symbolgen e mlogic) e scriviamo su file di testo i due log generati.
I restanti parametri di input sono stati inseriti secondo nostra discrezione.


option mprint symbolgen mlogic;

%classizz(in=loc.db, taglio_correlazione=0.4, distribuzione=binomial, alfa=0.05, passo=15,
	   log_0 = "C:\ ... INDIRIZZO ... \Log_Out\Out_0.log",
	   output_log = "C:\ ... INDIRIZZO ... \Log_Out\Out_1.log",
	   max_giri=0, max_format=$5000, simpson=1);

option nomprint nosymbolgen nomlogic;


Quinta parte - Il log e l'output
Poiché i due log generati dal processo sono troppo lunghi (più di 3.000 righe il primo e più di 300.000 il secondo), non verranno riportati qui.
Da un'analisi dei singoli passi é possibile osservare più in dettaglio il lavoro della procedura; nel caso non siate interessati a tale analisi si consiglia comunque una ricerca di errori o warning nei file: le uniche anomalie presenti dovrebbero essere quelle relative a sequenze non valide di macro variabili, conseguenza di tabelle senza righe (si veda l'esempio qui sotto).


MLOGIC(CLASSIZZ):  %LET (variable name is NUORI)
SYMBOLGEN:  Macro variable NUORI resolves to        0
MPRINT(CLASSIZZ):   proc sql noprint;
SYMBOLGEN:  Macro variable V_INSERITA resolves to CL_A1B
SYMBOLGEN:  Macro variable NUORI resolves to 0
SYMBOLGEN:  Macro variable NUORI resolves to 0
SYMBOLGEN:  Macro variable NUORI resolves to 0
SYMBOLGEN:  Macro variable IN resolves to loc.db
MPRINT(CLASSIZZ):   select CL_A1B_c, minimo, massimo into :nuov1 - :nuov0, :nuomi1 - :nuomi0, :nuoma1 
- :nuoma0 from loc.db_nuoco2;
WARNING: INTO Clause :nuov1 thru :nuov0 does not specify a valid sequence of macro variables.
WARNING: INTO Clause :nuomi1 thru :nuomi0 does not specify a valid sequence of macro variables.
WARNING: INTO Clause :nuoma1 thru :nuoma0 does not specify a valid sequence of macro variables.
NOTE: No rows were selected.
MPRINT(CLASSIZZ):   quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds

I file di output, come descritto nell'apposita pagina, sono numerosi.
Quelli più interessanti dal punto di vista statistico sono relativi al modello trovato, ma molto utili per l'applicazione del risultato sono le tabelle riassuntive.
Per quel che riguarda il primo gruppo, possiamo ritrovare il modello stimato (dataset &in._mcorr):


Parameter Level1 DF Estimate StdErr LowerWaldCL UpperWaldCL ChiSq ProbChiSq
INTERCEPT   1 0.0871419419 0.6398 -1.1667 1.3410 0.02 0.8917
CL_A1B_C -1 1 -0.6878394348 0.3595 -1.3925 0.0168 3.66 0.0557
CL_A1B_C 1 1 0.9365198021 0.3453 0.2597 1.6133 7.36 0.0067
CL_A1B_C 2 1 0.5423718235 0.3486 -0.1408 1.2256 2.42 0.1197
CL_A1B_C 3 0 0.0000000000 0.0000 0.0000 0.0000 . .
CL_K_A3_C 1 1 -1.6149056559 0.2974 -2.1978 -1.0320 29.48 <.0001
CL_K_A3_C 2 1 -0.9109392644 0.2619 -1.4242 -0.3977 12.10 0.0005
CL_K_A3_C 4 0 0.0000000000 0.0000 0.0000 0.0000 . .
CL_A2_C 1 1 -1.5230586715 0.2740 -2.0600 -0.9861 30.91 <.0001
CL_A2_C 3 1 -0.7443621414 0.1857 -1.1083 -0.3805 16.07 <.0001
CL_A2_C 7 0 0.0000000000 0.0000 0.0000 0.0000 . .
CL_K_A4_C 1 1 -1.0327863506 0.1948 -1.4146 -0.6509 28.10 <.0001
CL_K_A4_C 4 1 -0.6236875600 0.1975 -1.0108 -0.2366 9.97 0.0016
CL_K_A4_C 6 0 0.0000000000 0.0000 0.0000 0.0000 . .
CL_A6B_C -3 1 0.2796669659 0.5191 -0.7378 1.2972 0.29 0.5901
CL_A6B_C 1 1 1.0564306705 0.4790 0.1176 1.9952 4.86 0.0274
CL_A6B_C 4 0 0.0000000000 0.0000 0.0000 0.0000 . .
CL_A13_C 1 1 0.5725908596 0.1914 0.1974 0.9478 8.95 0.0028
CL_A13_C 3 0 0.0000000000 0.0000 0.0000 0.0000 . .
SCALE   0 1.0000000000 0.0000 1.0000 1.0000 _ _


e gli indicatori sintetici di bontà del modello (dataset &in._smcorr):


Criterion DF Value ValueDF
Deviance 987 974.5303 0.9874
Scaled Deviance 987 974.5303 0.9874
Pearson Chi-Square 987 992.7954 1.0059
Scaled Pearson X2 987 992.7954 1.0059
Log Likelihood _ -487.2651 _
AIC _ 1000.5303 _


Per quanto riguarda le tabelle riassuntive, se si vuole capire il cammino effettuato dalla procedura nella scelta delle variabili può risultare utile il dataset &in._passi (qui sotto si é voluto evidenziare in neretto la variabile entrata ad ogni passo):


Passo Modello
1 CL_A1B
2 CL_A1B CL_K_A3
3 CL_A1B CL_A2 CL_K_A3
4 CL_A1B CL_A2 CL_K_A3 CL_K_A4
5 CL_A1B CL_A2 CL_A6B CL_K_A3 CL_K_A4
6 CL_A13 CL_A1B CL_A2 CL_A6B CL_K_A3 CL_K_A4


Se si vuole applicare ad altri dati il modello ottenuto, é invece utile la tabella &in._zgri:


Nome Level1 Kvar_d Estimate Condizione DF Utilizzo
A13 3 25 < A13 <= Max 0.0000000000 25 < A13 0 X
A13 1 Min < A13 <= 25 0.5725908596 A13 <= 25 1 X
A1B 3 3 0.0000000000 A1B = 3 0 O
A1B 2 2 0.5423718235 A1B = 2 1 O
A1B 1 1 0.9365198021 A1B = 1 1 O
A1B -1 Missing -0.6878394348 a1b = 4 1 O
A2 7 24 < A2 <= Max 0.0000000000 24 < A2 0 X
A2 3 10 < A2 <= 24 -0.7443621414 10 < A2 <= 24 1 X
A2 1 Min < A2 <= 10 -1.5230586715 A2 <= 10 1 X
A3 4   0.0000000000 A3 in ('A31', 'A30') 0 K
A3 2   -0.9109392644 A3 in ('A33', 'A32') 1 K
A3 1   -1.6149056559 A3 in ('A34') 1 K
A4 6   0.0000000000 A4 in ('A45', 'A40', 'A410', 'A46') 0 K
A4 4   -0.6236875600 A4 in ('A42', 'A44', 'A49') 1 K
A4 1   -1.0327863506 A4 in ('A48', 'A41', 'A43') 1 K
A6B 4 4 0.0000000000 A6B = 4 0 O
A6B 1 1 <= CL_A6B <= 3 1.0564306705 1 <= A6B <= 3 1 O
A6B -3 Missing 0.2796669659 a6b = 5 1 O
INTERCEPT     0.0871419419 1 1  



Sesta parte - Possibile utilizzo dei file di output: verifica dell'accuratezza del modello
Le tabelle descritte sopra (e le altre fornite in output) possono essere utilizzate sia per osservare il nuovo modello, sia per applicarlo ai dati.
Nel codice qui sotto presentiamo uno dei loro possibili utilizzi: in particolare utilizzeremo la tabella &in._zgri per applicare il modello trovato ad un dataset (che in questo caso, per semplicità, sarà lo stesso dataset di stima) al fine di valutare l'accuratezza delle nostre previsioni.
Tale verifica sarà supportata nell'esempio dalla curva ROC relativa alla stima: la macro che la genera non sarà qui esposta (a voi il divertimento di scriverla).


/* Copia dei file utilizzati su work */
libname locale "C:\ ... INDIRIZZO ... \Data_In";

data db;
set locale.db_dcorr;
run;

data zgri;
set locale.db_zgri;
run;

libname locale;


/* Prendiamo in memoria i coefficienti relativi alle differenti variabili */
proc sql noprint;
 select count(*) into :c
 from zgri;
quit;

proc sql noprint;
 select count(distinct nome) into :cn
 from zgri;
quit;

%let c = &c;
%let cn = &cn;

proc sql noprint;
 select nome, estimate, condizione, utilizzo into
	:nome1 - :nome&c, :stima1 - :stima&c, :cond1 - :cond&c, :util1 - :util&c
 from zgri;
quit;

proc sql noprint;
 select distinct nome into :nom1 - :nom&cn
 from zgri;
quit;



%macro giro(dsin=db);

data &dsin.2;
set &dsin;
/* Inizializzazione */
 %do i=1 %to &cn;
  &&nom&i.._b = 0;
 %end;
/* Valorizzazione --> N.B.: le condizioni particolari dovrebbero sovrascrivere le altre */
 %do i=1 %to &c;
  %if "&&util&i" = "X" or "&&util&i" = "O" or "&&util&i" = "C" or "&&util&i" = "K" %then %do;
    if &&cond&i then &&nome&i.._b = &&stima&i;
   %end;
  %if "&&util&i" = "Q" %then %do;
    &&nome&i.._b = &&stima&i * &&nome&i;
   %end;
  %if "&&util&i" = "" %then %do;	/* Intercetta */
    &&nome&i.._b = &&stima&i;
   %end;
 %end;
/* Ora uniamo il tutto */
 xbeta = sum(
	%do i=1 %to &cn;
	  &&nom&i.._b,
	 %end;
	  0);
/* E calcoliamo il valore di probabilita */
 prob = exp(xbeta) / (1 + exp(xbeta));
run;

%mend giro;


%giro(dsin=db);
/*
NOTE: There were 1000 observations read from the data set WORK.DB.
NOTE: The data set WORK.DB2 has 1000 observations and 71 variables.
*/


/********************************************************************/
/* Ora dobbiamo verificare di aver inserito correttamente gli score */
/********************************************************************/

data ddbb;
set db2;
 diff = predetti - prob;
 if diff ^= 0;
run;

proc sort data=ddbb;
 by diff;
run;

proc sql;
 select min(diff) format=commax30.15 as min,
	max(diff) format=commax30.15 as max
 from ddbb;
quit;

proc delete data=ddbb;
run;

/************** OK **************/


/* Facciamo la ROC per il modello */
data db3;
set db2;
 if a21b = 1 then do;
   si = 1;
   no = 0;
  end;
 if a21b = 0 then do;
   si = 0;
   no = 1;
  end;
 peso = 1;
run;
/*
NOTE: There were 1000 observations read from the data set WORK.DB2.
NOTE: The data set WORK.DB3 has 1000 observations and 74 variables.
*/

%let ca = C:\ ... INDIRIZZO ... \Esempio;

%ROC(in=db3, buoni=no, cattivi=si, stima=prob, peso=peso, punti_bad=alti,
     migliore=vicin_perf, alpha=1.96, cartella_out="&ca", nomefile="ROC");

La curva ROC risultante é la seguente:

Curva ROC di esempio

Chiudiamo l'analisi testando, attraverso un confronto tra i casi stimati e i casi reali, la soglia suggerita nel grafico e la soglia più banale del 50%.


/* Proviamo il cutoff al 50% */
data db4;
set db3;
 if prob > 0.5 then stim = 1;
  else stim = 0;
run;

proc freq data=db4;
 tables a21b*stim / missing norow nocol nopercent;
run;
/*
		stim
	a21b	0,	1,	Total
	0,	618,	82,	700
	1,	160,	140,	300
	Total	778	222	1000
*/


/* Cutoff al 29.5275% */
data db4;
set db3;
 if prob > 0.295275 then stim = 1;
  else stim = 0;
run;

proc freq data=db4;
 tables a21b*stim / missing norow nocol nopercent;
run;
/*
		stim
	a21b	0,	1,	Total
	0,	511,	189,	700
	1,	75,	225,	300
	Total	586	414	1000
*/

Nel primo caso (soglia del 50%) avremo un valore di sensibilità pari a 140/300 = 46,7% e una specificità del 618/700 = 88,3%.
Nel secondo caso (soglia del 29.5275%) la sensibilità sarà del 225/300 = 75% e la specificità del 511/700 = 73%.


Settima parte - Utilizzo della tabella &in._passi per indirizzare il processo
Spesso, nella ricerca di un modello di regressione, é possibile che si abbia il desiderio di favorire alcune variabili rispetto ad altre; in altri casi i dati sono cosí tanti da dover procedere una variabile dopo l'altra interrompendo il calcolo dopo un certo numero di ore di elaborazione per poi riprendere successivamente.
Per aiutare l'utente in questi ed altri casi, é stato inserito il parametro di input passi: per concludere l'esempio mostreremo come si puó utilizzare la tabella di output &in._passi per tale scopo.


Passo Modello
1 CL_A1B
2 CL_A1B CL_K_A3
3 CL_A1B CL_A2 CL_K_A3
4 CL_A1B CL_A2 CL_K_A3 CL_K_A4
5 CL_A1B CL_A2 CL_A6B CL_K_A3 CL_K_A4
6 CL_A13 CL_A1B CL_A2 CL_A6B CL_K_A3 CL_K_A4


Nel codice sottostante creeremo le stesse tabelle di input del primo giro, aggiungendo la tabella &passi a partire dalla tabella di output &in._passi (qui sopra).


/* Assegnazione libreria primo giro */
libname old "C:\ ... INDIRIZZO ... \Data_In";

/* Assegnazione libreria giro attuale */
libname new "C:\ ... INDIRIZZO ... \Data_In2";


/* Copiamo il file dei dati originali */
data new.db;
set old.db;
run;


/* Analogamente al primo giro, creiamo i file di input */

/* Estraiamo dalla tabella i nomi delle colonne */
proc contents data=old.db out=gcd3 noprint;
run;


/* Conserviamo unicamente le variabili che ci interessano */
data gcd3;
set gcd3;
keep name type format formatl formatd;
run;


/* Aggiungiamo la colonna relativa all'utilizzo della singola variabile */
data gcd3;
set gcd3;
format utilizzo $1.;
 utilizzo = 'n';
/* Valorizziamo la nuova colonna con la tipologia scelta */
 if compress(name) = 'a1b' then utilizzo = 'o';
 if compress(name) = 'a2' then utilizzo = 'x';
 if compress(name) = 'a3' then utilizzo = 'k';
 if compress(name) = 'a4' then utilizzo = 'k';
 if compress(name) = 'a5' then utilizzo = 'q';
 if compress(name) = 'a6b' then utilizzo = 'o';
 if compress(name) = 'a7b' then utilizzo = 'o';
 if compress(name) = 'a8' then utilizzo = 'o';
 if compress(name) = 'a9' then utilizzo = 'c';
 if compress(name) = 'a10' then utilizzo = 'c';
 if compress(name) = 'a11' then utilizzo = 'o';
 if compress(name) = 'a12' then utilizzo = 'c';
 if compress(name) = 'a13' then utilizzo = 'x';
 if compress(name) = 'a14' then utilizzo = 'c';
 if compress(name) = 'a15' then utilizzo = 'c';
 if compress(name) = 'a16' then utilizzo = 'o';
 if compress(name) = 'a17' then utilizzo = 'k';
 if compress(name) = 'a18' then utilizzo = 'c';
 if compress(name) = 'a19' then utilizzo = 'c';
 if compress(name) = 'a20' then utilizzo = 'c';
 if compress(name) = 'a21b' then utilizzo = 'r';
run;


/* Inseriamo i valori "speciali" delle variabili */
data spec;
 length variabile $ 50.;
 length condizione $ 1000.;
 length classe $ 20.;
  variabile = 'a1b';
  condizione = 'a1b = 4';
  classe = 'Missing';
 output;
  variabile = 'a1b';
  condizione = 'a1b = 99';
  classe = 'Anomalo';
 output;
  variabile = 'a6b';
  condizione = 'a6b = 5';
  classe = 'Missing';
 output;
  variabile = 'a6b';
  condizione = 'a6b = 99';
  classe = 'Anomalo';
 output;
  variabile = 'a7b';
  condizione = 'a7b = 99';
  classe = 'Anomalo';
 output;
run;


data escc;
 length var1 $ 50.;
 length var2 $ 50.;
 if _n_ < 1;
run;


/* File con le variabili e il loro utilizzo */
data new.db_var;
set gcd3;
run;

/* File con le condizioni particolari */
data new.db_cond;
set spec;
run;

/* File delle esclusioni condizionali */
data new.db_esccon;
set escc;
run;


/* Eliminiamo i file temporanei */
proc delete data=gcd3 spec escc;
run;


/* Partendo dal file db_passi generato in output al primo giro,
    creiamo un analogo file che indichi alla procedura i passi da fare. */
data new.passi;
set old.db_passi;
 if passo <= 2;
run;


/* Il passo successivo del primo giro portava all'inserimento della colonna
    "CL_A2" all'interno del modello. Noi proveremo a favorire l'entrata
    della variabile "CL_K_A4" */
proc sql;
 insert into new.passi values (3, "CL_A1B CL_K_A3 CL_K_A4");
quit;

/*
N.B.: come si puo' notare dalle righe precedenti, le variabili devono essere 
      inserite nella tabella non con il nome originale, ma con il nome derivato,
      cosi' come e' stato descritto nella descrizione dei tipi di variabile
*/


/* Disassegnamo quindi le due librerie e riassegnamo la nuova */
libname old;
libname new;
libname locale "C:\ ... INDIRIZZO ... \Data_In2";


/* Ora effettuiamo una nuova chiamata alla procedura indicando tra i parametri
    di input anche la tabella con i passi da seguire */

option mprint symbolgen mlogic;

%classizz(in=locale.db, taglio_correlazione=0.4, distribuzione=binomial, alfa=0.05, passo=15,
 log_0 = "C:\ ... INDIRIZZO ... \Out_0.log",
 output_log = "C:\ ... INDIRIZZO ... \Out_1.log",
 max_giri=0, max_format=$5000, simpson=1, passi=locale.passi);

option nomprint nosymbolgen nomlogic;

In questo particolare caso la procedura, prendendo in input la tabella locale.passi (qui sotto a sinistra), genererà un flusso analogo al precedente, tranne per il terzo e quarto ciclo che risulteranno invertiti (qui sotto a destra).
Si sottolinea che non é detto che il modello finale sia, come in questo caso, sempre lo stesso. Camminando su due "percorsi" differenti possiamo ottenere per gli stessi dati modelli con variabili differenti o con le medesime variabili e classi differenti.
La procedura inoltre "favorisce" le variabili indicate nell'input, ma non le forza ad entrare se non risultano significative.


Passo Modello
1 CL_A1B
2 CL_A1B CL_K_A3
3 CL_A1B CL_K_A3 CL_K_A4


Passo Modello
1 CL_A1B
2 CL_A1B CL_K_A3
3 CL_A1B CL_K_A3 CL_K_A4
4 CL_A1B CL_A2 CL_K_A3 CL_K_A4
5 CL_A1B CL_A2 CL_A6B CL_K_A3 CL_K_A4
6 CL_A13 CL_A1B CL_A2 CL_A6B CL_K_A3 CL_K_A4





  Indice principale     Indice dei Programmi     Indice dell'Autoreg  
Go to English version

Data creazione: 17 Settembre 2010
Data ultima modifica: 27 Gennaio 2013