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 */ |
/*********** 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 |
|
|
|
|
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"); |
/* 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 */ |
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.
|
/* 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; |
|
|
Indice principale | Indice dei Programmi | Indice dell'Autoreg |
Go to English version |
Data creazione: 17 Settembre 2010
Data ultima modifica: 27 Gennaio 2013