program define xframeappend /* *!Author: Roger Newson *!Date: 15 March 2021 */ version 16.0 syntax namelist(name=frame_list) [, drop Generate(name) fast] /* drop specifies that the from frame will be dropped. generate() specifies the name of a new variable to be generated, with values 0 for observations from the current frame and k for observations from the kth appended frame. fast speciffies that no work will be done to preserve the to frame if the user presses Break or other failure occurs */ * Check that all frame names belong to frames * foreach frame_name in `frame_list' { confirm frame `frame_name' } * Check that generate variable does not exist in any input frame * if "`generate'"!="" { cap conf new var `generate' if _rc { disp as error "variable `generate' already defined in current frame `c(frame)'" error 110 } foreach frame_name in `frame_list' { frame `frame_name': cap conf new var `generate' if _rc { disp as error "variable `generate' already defined in frame `frame_name''" error 110 } } } * Preserve old dataset if requested * if "`fast'"=="" { preserve } * Create generate() variable if requested * if "`generate'"!="" { qui gene byte `generate'=0 } * Beginning of frame loop * local frameseq=0 foreach frame_name in `frame_list' { * Beginning of main quietly block * quietly { * Set frame sequence number * local frameseq=`frameseq'+1 * Get varlists from old dataset * ds local to_varlist "`r(varlist)'" * Get varlists from dataset to be appended * frame `frame_name': ds local from_varlist "`r(varlist)'" local shared_varlist : list from_varlist & to_varlist local new_varlist : list from_varlist - shared_varlist * Check modes of shared variables (numeric or string) * if "`shared_varlist'" != "" { foreach type in numeric string { ds `shared_varlist', has(type `type') local `type'_to "`r(varlist)'" frame `frame_name': ds `shared_varlist', has(type `type') local `type'_from "`r(varlist)'" local `type'_eq: list `type'_to === `type'_from } if (`numeric_eq' == 0) | (`string_eq' == 0) { di as err "shared variables in frames being combined must be both numeric or both string" error 109 } } * get size of new dataframe * frame `frame_name' : local from_N = _N local to_N = _N local from_start = `to_N' + 1 local new_N = `to_N' + `from_N' * Create variables for linkage in the 2 datasets * set obs `new_N' tempvar temp_n temp_link gen double `temp_n' = _n frame `frame_name' { gen double `temp_n' = _n + `to_N' } * Create linkage between the 2 datasets * frlink 1:1 `temp_n', frame(`frame_name') gen(`temp_link') * Import shared variables to old dataset * if "`shared_varlist'"!="" { tempvar temphome foreach X of varlist `shared_varlist' { frget `temphome'=`X', from(`temp_link') replace `X'=`temphome' in `=`to_N'+1' / `new_N' drop `temphome' } } * Import new variables to old dataset * if "`new_varlist'" != "" { tempvar temphome2 foreach X in `new_varlist' { frget `X'=`X', from(`temp_link') } } * Update generate variable if necessary * if "`generate'"!="" { qui replace `generate'=`frameseq' if _n>`to_N' } * Order variables (old ones first) * local oldto_varlist: list to_varlist - generate order `oldto_varlist' `new_varlist' `generate' } * End of main quietly block * } * End of frame loop * * Restore old dataset if requested and necessary * if "`fast'"=="" { restore, not } * Drop appended frame if requested * if "`drop'" == "drop" { foreach frame_name in `frame_list' { frame drop `frame_name' } } end