*! version 7.3 01FEB2024 DIME Analytics dimeanalytics@worldbank.org capture program drop iedropone program define iedropone , qui { syntax [if] , [Numobs(numlist int min=1 max=1 >0) mvar(varname) mval(string) zerook] version 12 /*********************************** Set constants ***********************************/ *Set a constant for the multi option being used local MULTI_USED 0 if ("`mvar'" != "" & "`mval'" != "") local MULTI_USED 1 *Set a constant for the zerook option being used local ZEROOK_USED 0 if ("`zerook'" != "") local ZEROOK_USED 1 *Set a constant for if an IF-condition used local IF_USED 0 if (`"`if'"' != "") local IF_USED 1 /*********************************** Test input ***********************************/ *Test that mvar() and mval() was used in combination if ("`mvar'" != "" & "`mval'" == "") { di as error "{pstd}The option mval() is required when using the option mvar()" error 197 } if ("`mvar'" == "" & "`mval'" != "") { di as error "{pstd}The option mvar() is required when using the option mval()" error 197 } *Test that either an if condition was specified or of the multi values option was used if `IF_USED' == 0 & `MULTI_USED' == 0 { di as error "{pstd}An {it:if} condition is required when mvar() and mval() is not used." error 197 } /*********************************** Set locals ***********************************/ *If number of obs to drop is not set, then use the default which is 1 if "`numobs'" == "" { local numobs = 1 } *Test if the var in mvar() is string or not if `MULTI_USED' == 1 { cap confirm string variable `mvar' if _rc == 0 local MULTI_STRING 1 if _rc != 0 local MULTI_STRING 0 } /*********************************** Test the number of obs matching ***********************************/ *Subfunction options local subfunc_opts "nobs(`numobs') zero(`ZEROOK_USED')" if `MULTI_USED' == 0 { ** Use the functon iedropone_test_match to * test if the number of observations to drop * is correct iedropone_test_match `if' , `subfunc_opts' local num_obs_dropped `r(numtodrop)' } else { **Create a counter that will noitify * the user how many observations were * dropped local num_obs_dropped = 0 *Loop over all values in mval() foreach mvalue of local mval { *Run the sub-function that test number of observation to be dropped is OK *Is mvar() is numeric if `MULTI_STRING' == 0 { ** Use the functon iedropone_test_match to * test if the number of observations to drop * is correct if `IF_USED' == 1 iedropone_test_match `if' & `mvar' == `mvalue' , `subfunc_opts' if `IF_USED' == 0 iedropone_test_match if `mvar' == `mvalue' , `subfunc_opts' } *Is mvar() is numeric else { ** Use the functon iedropone_test_match to * test if the number of observations to drop * is correct if `IF_USED' == 1 iedropone_test_match `if' & `mvar' == "`mvalue'" , `subfunc_opts' if `IF_USED' == 0 iedropone_test_match if `mvar' == "`mvalue'" , `subfunc_opts' } *Add to the counter how many observations will be dropped local num_obs_dropped = `num_obs_dropped' + `r(numtodrop)' } } /*********************************** Drop observation(s) ***********************************/ *Test if multivars are used if `MULTI_USED' == 0 { *The observations to be dropped drop `if' } else { *Loop over all values in mval and drop observations foreach mvalue of local mval { if `MULTI_STRING' == 0 { if `IF_USED' == 1 drop `if' & `mvar' == `mvalue' if `IF_USED' == 0 drop if `mvar' == `mvalue' } else { if `IF_USED' == 1 drop `if' & `mvar' == "`mvalue'" if `IF_USED' == 0 drop if `mvar' == "`mvalue'" } } } *Output to user how many observations were dropped if `num_obs_dropped' == 1 noi di "`num_obs_dropped' observation was dropped" if `num_obs_dropped' != 1 noi di "`num_obs_dropped' observations were dropped" } end **Sub function that checks that the number of observations * to drop is correct and it returns the number of observations * that will be dropped. capture program drop iedropone_test_match program define iedropone_test_match , rclass syntax [if] , nobs(int) zero(int) **Count how many obs fits the drop condition (this * function is called once for each value in mval) count `if' local count_match `r(N)' *Test if no match and zerook not used if `count_match' == 0 & `zero' == 0 { *Return error message noi di as error `"{pstd}No observation matches the drop condition " `if'". Consider using option zerook to surpress this error. No observations dropped."' error 2000 } *Test if no match but zerook used else if `count_match' == 0 & `zero' == 1 { *No observation dropped but that is allowed byt zero_used. Return 0. return scalar numtodrop = `count_match' } *Test if the number of match is less than it is supposed to be else if `count_match' < `nobs' { *Return error message noi di as error `"{pstd}There are less than exactly `nobs' observations that match the drop condition " `if'". No observations dropped."' error 910 } *Test if the number of match is more than it is supposed to be else if `count_match' > `nobs' { *Return error message if `nobs' == 1 noi di as error `"{pstd}There are more than exactly `nobs' observation that match the drop condition " `if'". No observations dropped."' if `nobs' > 1 noi di as error `"{pstd}There are more than exactly `nobs' observations that match the drop condition " `if'". No observations dropped."' error 912 } *Test if the number of match exactly what it is suppsed to be else if `count_match' == `nobs' { *Return the number of obs that will be dropped return scalar numtodrop = `count_match' } *The options above should be mutually exclusive, so this should never happen. else { *Return error message noi di as error "{pstd}The command is never supposed to reach this point, please notify the author of the command at kbjarkefur@worldbank.org" error 197 } end