*! atip_reglas.ado v1.0.0 Andres Talavera INEI/DNCE 21jul2026 program define atip_reglas, rclass version 14 syntax varname(numeric), GROUP(varlist) /// [RULES(string) SDTHRESH3(real 3) SDTHRESH2(real 2) /// ZTHRESH(real 2) ZSYMMETRIC MAHALTHRESH(real 8) GEN(name) CLASSIC] if "`rules'" == "" local rules "sd3 sd2 zscore iqr mahal" if "`gen'" == "" local gen "OUTLIERS" tempvar touse n_casos desvia media qtl1 qtl3 quietly gen byte `touse' = !missing(`varlist') quietly bys `group': gen `n_casos' = _N if `touse' tempvar media_c desvia_c quietly bys `group': egen `desvia_c' = sd(`varlist') if `touse' quietly bys `group': egen `media_c' = mean(`varlist') if `touse' if "`classic'" != "" { quietly gen double `media' = `media_c' quietly gen double `desvia' = `desvia_c' } else { tempvar absdev media_r desvia_r quietly bys `group': egen `media_r' = median(`varlist') if `touse' quietly gen double `absdev' = abs(`varlist' - `media_r') if `touse' quietly bys `group': egen `desvia_r' = median(`absdev') if `touse' quietly replace `desvia_r' = `desvia_r' * 1.4826 quietly gen double `media' = cond(`desvia_r'==0 & `desvia_c'>0, `media_c', `media_r') quietly gen double `desvia' = cond(`desvia_r'==0 & `desvia_c'>0, `desvia_c', `desvia_r') quietly count if `desvia_r'==0 & `desvia_c'>0 & `touse' if r(N) > 0 { di as text "Aviso: " as res r(N) as text " observaciones con MAD=0 (empates mayoritarios en la mediana del grupo) -- se uso media/DE como respaldo para esos casos" } } if strpos("`rules'","sd3") { capture drop SD_MEAN_3 gen byte SD_MEAN_3 = 1 if (`varlist' > `media'+`sdthresh3'*`desvia' | /// `varlist' < `media'-`sdthresh3'*`desvia') & `desvia'~=. replace SD_MEAN_3 = 0 if (`varlist'<=`media'+`sdthresh3'*`desvia' & /// `varlist'>=`media'-`sdthresh3'*`desvia') & `desvia'~=0 replace SD_MEAN_3 = . if `desvia'==0 | `n_casos'==1 } if strpos("`rules'","sd2") { capture drop SD_MEAN_2 gen byte SD_MEAN_2 = 1 if (`varlist' > `media'+`sdthresh2'*`desvia' | /// `varlist' < `media'-`sdthresh2'*`desvia') & `desvia'~=. replace SD_MEAN_2 = 0 if (`varlist'<=`media'+`sdthresh2'*`desvia' & /// `varlist'>=`media'-`sdthresh2'*`desvia') & `desvia'~=0 replace SD_MEAN_2 = . if `desvia'==0 | `n_casos'==1 } if strpos("`rules'","zscore") { capture drop ZSCORE_I ZSCORE gen double ZSCORE_I = (`varlist' - `media') / `desvia' if "`zsymmetric'" != "" { gen byte ZSCORE = 1 if abs(ZSCORE_I) > `zthresh' & ZSCORE_I~=. replace ZSCORE = 0 if abs(ZSCORE_I) <= `zthresh' } else { gen byte ZSCORE = 1 if ZSCORE_I > `zthresh' & ZSCORE_I~=. replace ZSCORE = 0 if ZSCORE_I <= `zthresh' } replace ZSCORE = . if `desvia'==0 | `n_casos'==1 } if strpos("`rules'","iqr") { capture drop IQR quietly bys `group': egen `qtl1' = pctile(`varlist') if `touse', p(25) quietly bys `group': egen `qtl3' = pctile(`varlist') if `touse', p(75) tempvar iqr_val lim_inf lim_sup quietly gen double `iqr_val' = `qtl3' - `qtl1' quietly gen double `lim_sup' = cond(`iqr_val'==0 & `desvia_c'>0, /// `media_c'+`sdthresh2'*`desvia_c', `qtl3'+1.5*`iqr_val') quietly gen double `lim_inf' = cond(`iqr_val'==0 & `desvia_c'>0, /// `media_c'-`sdthresh2'*`desvia_c', `qtl1'-1.5*`iqr_val') gen byte IQR = 1 if `varlist' > `lim_sup' | `varlist' < `lim_inf' replace IQR = 0 if `varlist' <= `lim_sup' & `varlist' >= `lim_inf' replace IQR = . if `desvia'==0 | `n_casos'==1 quietly count if `iqr_val'==0 & `desvia_c'>0 & `touse' if r(N) > 0 { di as text "Aviso: " as res r(N) as text " observaciones con IQR=0 (empates mayoritarios entre Q1 y Q3) -- se uso media+/-`sdthresh2'*DE como respaldo para la regla IQR en esos casos" } } if strpos("`rules'","mahal") { capture drop DIST MHLBS capture confirm variable ZSCORE_I if _rc { gen double ZSCORE_I = (`varlist' - `media') / `desvia' } gen double DIST = ZSCORE_I^2 gen byte MHLBS = 1 if DIST>`mahalthresh' & DIST~=. replace MHLBS = 0 if DIST<=`mahalthresh' } capture drop `gen' gen byte `gen' = 0 foreach r in SD_MEAN_3 SD_MEAN_2 ZSCORE IQR MHLBS { capture confirm variable `r' if !_rc { replace `gen' = 1 if `r'==1 } } quietly count if `gen'==1 return scalar n_outliers = r(N) di as text "Outliers detectados (`gen'==1): " as res r(N) end