*** Project. Generalized CV for Varying coefficient models ** change all globals into locals ** NOW IN MATA ** 2.1 This will try to make the Optimization within Mata. May be more efficient ** last change was to modify with a flag for ealy ending mata:mata clear capture program drop vc_bwmt program vc_bwmt, rclass sortpreserve syntax varlist(numeric fv) [if] [in], vcoeff(varname) [ bwi(real -1) weight(varname) knots(real -1) km(real 1) sample(varname) trimsample(varname) kernel(str) plot ] ** varlist will contain the dependent variable and independent variables ** weight will have the survey weights if any ** full_x has the fully observed nonparametric data ** par_x gas the partial data (which is a less detailed variable compared to full_x only for speeding up calculations ** trim_sample to define if any segment of the data will not be used for CV but used in claculations ** sample defines what data will be used in estimation (and what will not) sample>trimsample ** This sets up base information tokenize `varlist' local y `1' //get the rest of the vars macro shift local varx `*' **# Defining 3 levels of Samples 1. Sample to work marksample touse markout `touse' `varlist' `vcoeff' ** Legacy if "`sample'"!="" { qui: replace `touse'=0 if `sample'==0 } ** Sample 3 Sample based on Trimsample (for CV calculation) tempvar touse3 qui:gen `touse3'=1 if "`trimsample'"!="" { qui:replace `touse3'=`trimsample'*`touse' } *local varx `varx' local full_x `vcoeff' *verifying if knots its a sensible number numlist "`knots'", integer range(>=-2) **# This counts how many values vcoeff has tempvar vals qui:bysort `touse' `vcoeff': gen byte `vals' = (_n == 1) * `touse' sum `vals' if `touse', meanonly local vcvls=r(sum) if `vcvls'>=500 & `knots'==-1 { display "the variable `vcoeff' has more than 500 distinct values, using option knots(0) km(2)" display "To force using all distinct values use knots(-2)" local knots=0 local km=2 } **# And this defines groups to be used. if `knots'==-1 | `knots'==-2 { local par_x `vcoeff' } else { tempvar _kn egen `_kn'=vbin(`vcoeff') if `touse', knot(`knots') km(`km') local par_x `_kn' ** Get Number of Implicit knots and Implicit Bandwidth. if (`knots'==0) { sum `vcoeff' if `touse', meanonly local max=r(max) local min=r(min) local N=r(N) local _ikb=min(sqrt(`N'),10*ln(`N')/ln(10)) local _ikb=round(`_ikb'*`km') local _ibw=(`max'-`min')/(`_ikb'+1) } else { sum `vcoeff' if `touse', meanonly local max=r(max) local min=r(min) local N=r(N) local _ikb=(`knots') local _ibw=(`max'-`min')/(`knots'+1) } display "Number of used Knots: `_ikb' " display "Implicit Bandwidth : " %6.5f `_ibw' } ** sample level 3 . Only 1 observation per parx tempvar touse2 qui:bysort `touse' `par_x':gen byte `touse2'=(_n==1)*(`touse'==1) **# Undocumented for weights local sweight 1 * this is undocumented. PLanned for adding sample weights *if "`weight'"!="" { * local sweight `weight' *} *DEFAULT OPTION FOR Kernel if "`kernel'"=="" { local kernel="gaussian" } display "Kernel: `kernel'" ** try adding th other options if "`kernel'"=="gaussian" local k = 1 else if "`kernel'"=="epan" local k = 2 else if "`kernel'"=="epan2" local k = 3 else if "`kernel'"=="biweight" local k = 4 else if "`kernel'"=="cosine" local k = 5 else if "`kernel'"=="cosine2" local k = 6 else if "`kernel'"=="parzen" local k = 7 else if "`kernel'"=="rectan" local k = 8 else if "`kernel'"=="trian" local k = 9 else if "`kernel'"=="logistic" local k = 10 else if "`kernel'"=="tricube" local k = 11 else if "`kernel'"=="triweight" local k = 12 else { display "Kernel function `kernel' not allowed" exit 1 } **# Initial bandwidth based on lpoly if `bwi'==-1 { lpoly `y' `full_x' if `touse', nograph kernel(`kernel') degree(1) ** Initial BW adjusts for number of parameters in the explanatory variable. Better adjustments can be found. ** in principle more explanatory variables require a larger bandwidth local num : word count `varx' local bw1=(1+ln(`num'+1))*r(bwidth) } else { local bw1=`bwi' } local bwi=`bw1' **# Here is the new code for CV *1 call on Mata local fvarx= "`full_x' `varx' c.`full_x'#c.(`varx')" tempname bwmatrix cvbw mata:main_cvreg("`y'","`fvarx'","`full_x'","`par_x'","`touse'", "`touse2'", "`touse3'", `bwi' , `k',"`bwmatrix'","`cvbw'") ** Opt matrix cvbw = `cvbw' global opbw_=`bwmatrix'[1,1] global kernel_="`kernel'" global vcoeff_="`vcoeff'" display "Bandwidth stored in global \$opbw_" display "Kernel function stored in global \$kernel_" display "VC variable name stored in global \$vcoeff_" if "`plot'"!="" { tempname _cv svmat cvbw, name(`_cv') scatter `_cv'2 `_cv'1 , xtitle("bandwidth") ytitle("CV criteria") legend(off) xline($opbw_) } matrix colname `cvbw'= bw cv *macro drop y par_x varx full_x sweight return scalar bandwidth=`bw1' return scalar cv_criteria=`bwmatrix'[1,2] return local kernel="`kernel'" return matrix bwcv_trace=cvbw *capture mata:mata drop x y end ** touse Sample ** touse2 small sample only those needed. ** touse3 trim sample mata: void main_cvreg(string scalar depvar, string scalar indepvar, string scalar fcsvar, string scalar pcsvar, string scalar touse , touse2, touse3, real scalar bwi, real scalar k, string scalar bwm, cvbwm ) { real matrix y,x ,fvc,pvc,vc,trim y=st_data(.,depvar,touse) x=st_data(.,indepvar,touse) x=x,J(rows(x),1,1) fvc= st_data(., fcsvar,touse) pvc= st_data(., pcsvar,touse) vc = st_data(., pcsvar,touse2) trim= st_data(., touse3,touse) pointer matrix fdata fdata=(&y,&x,&fvc,&pvc,&vc,&k,&trim) // From here I can do similar evaluation as I did Before. Slow...but effective // 1st store matrix with results real scalar bw0, bw1, bw2, cbw real scalar cv0, cv1, cv2, ccv real matrix cvbw real scalar df, ddf real scalar chg, dch, pch, iter real scalar flag_imp, cntl, flagbw /// bws bw0=bwi*.99 cbw=bw1=bwi bw2=bwi*1.01 ///cvs cv0=log( mean (cv_looerr(fdata,bw0):^2 ) ) ccv=cv1=log( mean (cv_looerr(fdata,bw1):^2 ) ) cv2=log( mean (cv_looerr(fdata,bw2):^2 ) ) //matrix with all cvbw cvbw = (bw0,cv0)\ (bw1,cv1) \(bw2,cv2) // init iter iter=0 /// First Print out printf("Iteration %f BW: %10.7f CV: %10.7g \n",iter, bw1 , cv1) /// Derivatives (numerical) df =(cv2-cv0)/(bw2-bw0) ddf=(cv2-2*cv1+cv0)/(bw2-bw1)^2 //df1=(cv2-cv1)/(bw2-bw1) //df2=(cv1-cv0)/(bw1-bw0) /// Improvement chg=-df/abs(ddf) /// This is to estimate bw0 and bw2. dch=min( (0.01*bw1, abs(chg)) ) pch=1 // Here we have the loop for improvements iter++ for(flagbw=0 ;( abs(pch)>.0001 & flagbw==0 ) ;iter++) { // verify its not negative if ((bw1+chg)<0) chg=-0.5*bw1 // 1. Change BW so there is an improvment for(cntl=flag_imp=0; (flag_imp==0 & cntl<5) ; cntl++) { bw1=cbw+chg cv1=log( mean (cv_looerr(fdata,bw1):^2 ) ) cvbw = cvbw \ (bw1,cv1) if (cv10.5):*(abs((*z)):<=1)) } else if (k==8) { kz=abs((*z)):<=1 } else if (k==9) { kz=(-abs((*z)):+1):*(abs((*z)):<=1) } else if (k==10) { kz=(exp((*z))+exp((*z):*-1):+2):^-1:*4 } else if (k==11) { kz=(-abs((*z)):^3:+1):^3:*(abs((*z)):<1) } else if (k==12) { kz=(-abs((*z)):^2:+1):^3:*(abs((*z)):<1) } return(kz) } end