*! version 1.06 16May2012 Minh Cong Nguyen
* version 1.06: 16May2012 - adding more statistics for continuous variables.
* version 1.05 19Nov2011 - basic command
# delimit ;
cap program drop tabmult;
program define tabmult, rclass;
version 9, missing;
local version : di "version " string(_caller()) ", missing:";
syntax [anything] [if] [in] [aw], [cat(varlist) cont(varlist)] by(varlist) [Row Col SCol SRow Missing STATCont(string) save(string) SHeet(string) replace append SAVEMat(string)];
local cmdline: copy local 0 ;
marksample touse;
if `"`weight'"' != "" {;
local wgt `"[`weight'`exp']"';
};
if ~missing(`"`missing'"') {;
local missopt missing;
local mcount = 1;
local mstr _VX_;
};
else {;
local missopt;
local mcount = 0;
local mstr ;
};
if ("`row'"!="") + ("`col'"!="") >1 {;
di in red "choose only one option: row or col. The default is freq";
exit 198 ;
};
local cat : list uniq cat;
local cont : list uniq cont;
local by : list uniq by;
if "`:list cat & cont'"!="" {;
di as err "Variables `:list cat & cont' cannot be both in the categorial and continuous lists";
error 198 ;
};
local vlist : list cat | cont;
if "`vlist'"=="" {;
di as err "There are no variables in the categorial and/or continuous list";
error 198 ;
};
// checking variables;
foreach var of local cont {;
qui su `var', meanonly;
if r(N)==0 local vbad "`vbad' `var'";
};
if `: word count `vbad''>0 {;
dis in ye _n "The following variable(s) have no numeric observations or is/are string variables: `vbad'";
error 109 ;
};
if "`:list vlist & by'"!="" { ;
di as err "Variables `:list vlist & by' cannot be both in the categorial and/or continuous lists and the by list";
error 198 ;
};
if (`"`row'"'==`""' & `"`col'"'==`""') {;
local opts;
local weight1;
local topt count;
local rla "(count)";
if ~missing("`statcont'") local topt "`statcont'";
};
else if (`"`row'"'!=`""' & `"`col'"'==`""') {;
local opts nof row;
local weight1 `wgt';
local topt mean;
local rla "(mean)";
if ~missing("`statcont'") local topt "`statcont'";
};
else if (`"`row'"'==`""' & `"`col'"'!=`""') {;
local opts nof col;
local weight1 `wgt';
local topt mean;
local rla "(mean)";
if ~missing("`statcont'") local topt "`statcont'";
};
if ~missing(`"`scol'"') {;
tempname __all;
tempvar __alll;
qui gen `__all' = 1 if `touse';
qui la var `__all' "All";
qui la def `__alll' 1 "sample";
qui la val `__all' `__alll';
local by `"`by' `__all'"';
};
// Saving and Excel options;
if missing("`save'") {;
local save "stata_tabmult.xml";
local replace "replace";
};
else {;
_getfilename "`save'";
if `"`r(filename)'"'==`"`save'"' {;
local save "`c(pwd)'/`save'";
};
else {;
if index(`"`r(filename)'"', ".")==0 {;
local save `"`save'.xml"';
};
};
if ~missing("`append'") {;
capture confirm file "`save'";
if _rc {;
local replace "replace";
};
else {;
local append "append";
};
};
if missing("`append'`replace'") {;
capture confirm file "`save'";
if ~_rc {;
display as error `"Error 602: file `save' already exists"';
exit 198 ;
};
else {;
local replace "replace";
};
};
};
local filename = cond(strpos("`save'", "\") + strpos("`save'", "/") > 0, "`save'", "`c(pwd)'\\`save'");
// get sheet options
local 0 `sheet';
syntax [anything(name=sheet)] [, COlor(integer -1) noGridlines];
// check if sheet name is valid
gettoken a b : sheet, parse(":\/?*[]");
if ~missing("`b'") | length("`sheet'")>31 Error 198 `"not a valid Excel sheet name"';
if missing("`sheet'") local sheet "Sheet1";
// Excel options;
local excelpath;
capture local dirs : dir "C:/Program Files (x86)/Microsoft Office" dirs "office*";
if ~_rc {;
local x86 " (x86)";
};
else {;
capture local dirs : dir "C:/Program Files/Microsoft Office" dirs "office*";
};
foreach d of local dirs {;
capture confirm file "C:/Program Files`x86'/Microsoft Office/`d'/excel.exe";
if ~_rc {;
local excelpath "C:/Program Files`x86'/Microsoft Office/`d'/excel.exe";
continue, break;
};
};
// Check variables;
preserve;
local vlist2 : list cat | by;
local vlist3 : list cont | vlist2;
foreach v2 of local vlist3 {;
local varl : variable label `v2';
local varl : subinstr local varl `"`=char(34)'"' "", all;
if (`"`varl'"'==`""') la var `v2' "`v2'";
capture confirm string variable `v2';
if !_rc {;
qui encode `v2' if `touse', gen(`v2'_XXX) label(`v2');
qui drop `v2';
qui ren `v2'_XXX `v2';
};
};
// Process the labels for by groups;
foreach v1 of local by {;
qui levelsof `v1' if `touse', local(l_`v1');
local lbl0 : variable label `v1';
local rn0 = proper("`lbl0'");
local bynames "`bynames' "`rn0'"";
foreach rn of local l_`v1' {;
local lbl : label (`v1') `rn';
local lbl = proper("`lbl'");
local rn1 "`lbl'";
local cnames "`cnames' "`rn1'"";
};
if ~missing(`"`missing'"') local cnames "`cnames' "`missopt'"";
};
// Process the labels for cat groups;
foreach v2 of local cat {;
qui levelsof `v2' if `touse', local(l_`v2');
local lbl0 : variable label `v2';
local lbl0 = proper("`lbl0'");
local rn0 "_GX_`lbl0'";
local rnames "`rnames' "`rn0'"";
foreach rn of local l_`v2' {;
local lbl : label (`v2') `rn';
local lbl = proper("`lbl'");
local rn1 "_VX_`lbl'";
local rnames "`rnames' "`rn1'"";
};
if ~missing(`"`missing'"') local rnames "`rnames' "`mstr'`missopt'"";
if ~missing(`"`srow'"') local rnames "`rnames' "_VX_Total"";
};
// Process the labels for cont groups;
foreach v2 of local cont {;
local lbl0 : variable label `v2';
local lbl0 = proper("`lbl0'");
if ("`statcont'"=="") {;
local rn0 "_GZ_`rla'_`lbl0'";
local rnames "`rnames' "`rn0'"";
};
else {;
foreach nr of local statcont {;
local rn0 "_GZ_(`nr')_`lbl0'";
local rnames "`rnames' "`rn0'"";
};
};
};
// Process the numbers in cells for cat groups;
cap mat drop mall2;
if ("`cat'"!="") {;
foreach v2 of local cat {;
cap mat drop mall1;
foreach v1 of local by {;
qui ta `v2' `v1' if `touse' `weight1', `opts' `missopt' matcell(mcell) matrow(mrow) matcol(mcol);
local strmcoll;
forv i=1(1)`=colsof(mcol)' {;
local xbs = mcol[1,`i'];
local strmcoll "`strmcoll' `xbs'";
};
local strmrow;
forv i=1(1)`=rowsof(mrow)' {;
local xbs = mrow[`i',1];
local strmrow "`strmrow' `xbs'";
};
//Process the mcell matrix;
local tot2 = 0;
if ~missing(`"`srow'"') {;
local misrow = 1;
mat mrowt = J(1,`=colsof(mcell)',.);
forv i=1(1)`=colsof(mcell)' {;
local tot1 = 0;
forv j=1(1)`=rowsof(mcell)' {;
local tot1 = `tot1' + mcell[`j',`i'];
};
local tot2 = `tot2' + `tot1';
mat mrowt[1,`i'] = `tot1';
};
if (("`col'"=="col") | ("`row'"=="row")) mat mrowt = (100/`tot2')*mrowt;
};
if "`v1'"~="`__all'" {;
if ("`row'"=="row") {;
// row options, sum to 100 in rows;
forv i=1(1)`=rowsof(mcell)' {;
local tot = 0;
forv j=1(1)`=colsof(mcell)' {;
local tot = `tot' + mcell[`i',`j'];
};
forv j=1(1)`=colsof(mcell)' {;
mat mcell[`i',`j'] = (mcell[`i',`j']/`tot')*100;
};
};
};
if ("`col'"=="col") {;
// col options, sum to 100 in columns;
forv i=1(1)`=colsof(mcell)' {;
local tot = 0;
forv j=1(1)`=rowsof(mcell)' {;
local tot = `tot' + mcell[`j',`i'];
};
forv j=1(1)`=rowsof(mcell)' {;
mat mcell[`j',`i'] = (mcell[`j',`i']/`tot')*100;
};
};
};
};
else {;
if ("`col'"=="col") | ("`row'"=="row") {;
// scol options, sum to 100 in columns;
forv i=1(1)`=colsof(mcell)' {;
local tot = 0;
forv j=1(1)`=rowsof(mcell)' {;
local tot = `tot' + mcell[`j',`i'];
};
forv j=1(1)`=rowsof(mcell)' {;
mat mcell[`j',`i'] = (mcell[`j',`i']/`tot')*100;
};
};
};
};
if ~missing(`"`srow'"') mat mcell = mcell \ mrowt;
local pos = 1;
foreach mr of local l_`v1' {;
local x : list posof "`mr'" in strmcoll;
if `x'==0 {;
if `pos'<=colsof(mcell) {;
if (`pos'==1) mat mcell = J(rowsof(mcell),1,0), mcell;
if (`pos'> 1) mat mcell = mcell[.,1..`=`pos'-1'], J(rowsof(mcell),1,0), mcell[.,`pos'..colsof(mcell)];
};
else if `pos'>colsof(mcell) {;
mat mcell = mcell[.,1..`=`pos'-1'], J(rowsof(mcell),1,0);
};
};
local ++ pos;
};
// add missing to the output;
local x : list posof "." in strmcoll;
if `x'==0 & ~missing(`"`missing'"') {;
mat mcell = mcell, J(rowsof(mcell),1,0);
};
local pos = 1;
foreach mr of local l_`v2' {;
local x : list posof "`mr'" in strmrow;
if `x'==0 {;
if `pos' <= `=rowsof(mcell)-`misrow'' {;
if (`pos'==1) mat mcell = J(1,colsof(mcell),0) \ mcell;
if (`pos'> 1) mat mcell = mcell[1..`=`pos'-1',.] \ J(1,colsof(mcell),0) \ mcell[`pos'..rowsof(mcell),.];
};
else if `pos' > `=rowsof(mcell)-`misrow'' {;
if ~missing(`"`srow'"') {;
mat mcell = mcell[1..`=`pos'-1',.] \ J(1,colsof(mcell),0) \ mcell[rowsof(mcell),1..colsof(mcell)];
};
else {;
mat mcell = mcell[1..`=`pos'-1',.] \ J(1,colsof(mcell),0);
};
};
};
local ++ pos;
};
local x : list posof "." in strmrow;
if `x'==0 & ~missing(`"`missing'"') {;
if ~missing(`"`srow'"') {;
mat mcell = mcell[1..`=rowsof(mcell)-1',.] \ J(1,colsof(mcell),0) \ mcell[rowsof(mcell),1..colsof(mcell)];
};
else {;
mat mcell = mcell \ J(1,colsof(mcell),0);
};
};
mat mall1 = (nullmat(mall1), mcell);
}; // end for by;
mat rblanks = J(1,colsof(mall1),.a);
mat mall1 = rblanks \ mall1 ;
mat mall2 = (nullmat(mall2) \ mall1);
}; // end for v2;
};
// Process the numbers in cells in cont groups;
if ("`cont'"!="") {;
cap mat drop mallx;
foreach v1 of local by {;
qui tabstat `cont' `weight1' if `touse', by(`v1') stat(`topt') `missopt' save;
if ~missing(`"`missing'"') {;
local send = `:word count `l_`v1'''+1;
};
else {;
local send : word count `l_`v1'';
};
forv s=1(1)`send' {;
mat x0 = r(Stat`s');
if (`s'==`send') & (x0[1,1]==.) {;
mat mallx = (nullmat(mallx), J(rowsof(mallx),1,.));
};
else {;
mat x1 = vec(r(Stat`s'));
mat mallx = (nullmat(mallx), x1);
};
};
};
mat mall2 = (nullmat(mall2) \ mallx);
};
if ~missing("`savemat'") {;
local rn "`rnames'";
local rn : subinstr local rn "_GX_" "", all;
local rn : subinstr local rn "_GZ_" "", all;
local rn : subinstr local rn "_VX_" "", all;
mat svm = mall2;
mat rownames svm = `rn';
mat colnames svm = `cnames';
local 0 `savemat';
syntax name , [replace exit];
if missing("`replace'") {;
matrix `namelist'= nullmat(`namelist') \ svm;
};
else {;
matrix `namelist'= svm;
};
`exit';
};
// Put to dataset;
local rr = rowsof(mall2);
local rc = colsof(mall2);
local obs = `rr'+2;
drop _all;
qui set obs `obs';
qui gen str d_0 = "";
local i=0;
foreach v of local rnames {;
qui replace d_0 = `"`v'"' in `=`i'+3';
local ++i;
};
local j=1;
foreach v1 of local cnames {;
qui gen str d_`j' = `"`v1'"' in 2;
forv i=1/`rr' {;
qui replace d_`j' = string(mall2[`i',`j']) in `=`i'+2';
};
local ++j;
};
//Puting into xml;
tempfile tmpfile;
tempname ofile outfile;
local lsheet "`sheet'";
if ~missing("`replace'") {;
local xmlheader "
";
file open `outfile' using "`tmpfile'", write;
file write `outfile' `"`xmlheader'"' _n;
local fontstyles "01 Times New Roman 10";
file write `outfile' `"`fontstyles'"' _n;
local styles "";
file write `outfile' `"`styles'"';
local styles "
";
file write `outfile' `"`styles'"' _n;
file write `outfile' "";
file write `outfile' "created using tabmult";
file write `outfile' `"?`lsheet'?"' _n;
};
else {; // append
file open `ofile' using "`save'", read;
file open `outfile' using "`tmpfile'", write;
file read `ofile' line; // header
file write `outfile' `"`line'"' _n;
file read `ofile' line; // font list
file write `outfile' `"`line'"' _n;
file read `ofile' line; // styles
file write `outfile' `"`line'"' _n;
file read `ofile' line; // Description, sheetnames
if strpos(`"`line'"',"tabmult")==0 {;
display as error "can not append, existing file was created or changed by some other program";
exit 603;
};
local sheetID=0;
local sheet "`lsheet'";
local dfdfdf : subinstr local line "?`lsheet'?" "", all count(local c);
while `c'>0 {;
local ++sheetID;
local lsheet "`sheet' (`sheetID')";
local dfdfdf : subinstr local line "?`lsheet'?" "", all count(local c);
}; //while
local line : subinstr local line "" "`lsheet'?";
file write `outfile' `"`line'"' _n;
while r(eof)==0 {;
file read `ofile' line;
if `"`line'"'!="" file write `outfile' `"`line'"' _n;
}; //for
};
file write `outfile' "" _n;
if `color'>=0 | ~missing("`gridlines'") {;
file write `outfile' "";
if `color'>=0 file write `outfile' "`color'";
if ~missing("`gridlines'") file write `outfile' "";
file write `outfile' "";
};
file write `outfile' "" _n;
file write `outfile' " | " ;
tokenize `by';
local x=1;
foreach byn of local bynames {;
local k: word count `l_``x''';
local ltitle "
`byn' | ";
file write `outfile' `"`ltitle'"';
local ++x;
};
file write `outfile' "
";
unab vlist : d_*;
foreach var of local vlist {;
local datum = `var'[2];
local xmlrow "`xmlrow' `datum' | ";
};
file write `outfile' `"`xmlrow'"';
file write `outfile' "
";
local xmlrow "";
forvalues i = 3/`=_N' {;
file write `outfile' "";
foreach var of local vlist {;
local datum = `var'[`i'];
if (`"`datum'"'==".") local datum = "na";
if (`"`datum'"'==".a") local datum = "";
local dtype = cond(~missing(real(`"`datum'"')), "Number", "String");
local stype = cond(~missing(real(`"`datum'"')), "s01_Numbers", "s01_Var");
if (`"`opts'"'=="") & (`"`dtype'"'=="Number") local stype s01_Freq;
if strpos(`"`datum'"',"_GX_") {;
local stype s01_Group;
local datum : subinstr local datum "_GX_" "", all;
};
if strpos(`"`datum'"',"_GZ_") {;
local stype s01_Var;
local datum : subinstr local datum "_GZ_" "", all;
local datum : subinstr local datum "_" " ", all;
};
if strpos(`"`datum'"',"_VX_") {;
local stype s01_Var;
local datum : subinstr local datum "_VX_" " ", all;
};
local xmlrow "`xmlrow' `datum' | ";
};
file write `outfile' `"`xmlrow'"';
file write `outfile' "
" _n;
local xmlrow "";
};
file write `outfile' "
" _n;
file write `outfile' "";
file close _all;
capture copy "`tmpfile'" "`save'", replace;
if _rc {;
display as error "file can not be saved at this location";
exit 603;
};
display as text "note: results saved to `filename'";
if "`c(os)'"=="Windows" & missing("`c(mode)'"){;
if ~missing(`"`excelpath'"') {;
display `"{ stata `"winexec "`excelpath'" "`filename'" "': click here}"' _c;
display as text " to open with Excel";
if ~missing("`savepath'") global `savepath' "`excelpath'";
};
if ~missing(`"`calcpath'"') {;
display `"{ stata `"winexec "`calcpath'" "`filename'" "': click here}"' _c;
display as text " to open with Calc";
};
};
restore;
end;