basketball fixes

basketball fixes

program make_basketball
make_mt

make_careermins

read_mt

make_master

//make_mt

end

////////////////////////
// This makes master baseball dataset
program make_master

insheet using ${basketball}players.csv, clear

replace college = college + ", " + birthdate if v12 != ""
replace birthdate = v12 if v12 != ""
drop v12

merge 1:1 ilkid using ${stash}bk_tmp, keep(match) nogen

gen birthyear = substr(birthdate,1,4)
rename firstseason debutyear
rename lastseason lastyear

rename ilkid playerid

keep playerid firstname lastname debutyear lastyear birthyear minutesrank minutes gp asts pts reb

rename firstname namefirst
rename lastname namelast

gen playername = namefirst + " " + namelast

// generate file for amazon to get me data on
drop if debutyear < 1940
drop if debutyear > 1990

drop if minutesrank > 1000

merge 1:1 playerid using ${basketball}mt_bk_output, nogen

save ${basketball}bk_master, replace

end

////////////////////////
/// MAKE CAREER MINS
program make_careermins

insheet using ${basketball}player_career.csv, clear
keep ilk firstname lastname gp minutes* pts asts reb turnover

// this data is at player-league level.
local vars "gp minutes pts reb asts turnover"
foreach x in `vars'{
    bysort ilk: egen tmp = total(`x')
    replace `x' = tmp
    drop tmp
}

bysort ilk: drop if _n>1

gsort- minutes
gen minutesrank = _n

save ${stash}bk_tmp, replace

end

//// MT section

program read_mt

insheet using ${basketball}mt_bk_output.csv, clear

gen wikihandle = subinstr(answer, "http://en.wikipedia.org/wiki/","",.)
keep inputp inputdisp wikihandle

gen tmp = subinstr(inputd, " ","_",.)
fix_errors

rename inputplayerid playerid
keep wikihandle playerid

save ${basketball}mt_bk_output, replace

end

program fix_errors

replace wiki = tmp if inputd == "Slater Martin"
replace wiki = tmp if inputd == "Tom Meschery"
replace wiki = tmp if wiki == "Micheal_Williams"
replace wiki = tmp if inputd == "Michael Cage"
replace wiki = tmp if inputd == "Alvin Robertson"
replace wiki = tmp if inputd == "Magic Johnson"
replace wiki = tmp if inputd == "Michael Jordan"
replace wiki = tmp if inputd == "Phil Jordon"

replace wiki = "George_L._Johnson" if inputd == "George L. Johnson"

replace wiki = "Wayne_Cooper_(basketball)" if inputd == "Wayne Cooper"

replace wiki = "Micheal_Ray_Richardson" if inputd == "Micheal Ray Richardson"

replace wiki = "Eddie_Johnson_(basketball,_born_1955)" if inputd == "Eddie Johnson"
replace wiki = "Eddie_A._Johnson" if inputd == "Eddie A. Johnson"

replace wiki = "Mike_Brown_(basketball,_born_1963)" if inputd == "Mike Brown"

replace wiki = "Mahmoud_Abdul-Rauf" if wiki == "Mahmud"
replace wiki = "Jan_van_Breda_Kolff" if inputd == "Jan Vanbredakolff"
replace wiki = "Norm_Van_Lier" if inputd == "Norm Vanlier"
replace wiki = "Danny_Vranes" if inputd == "Danny Vranes"
replace wiki = "Dick_Barnett" if inputd == "Dick Barnett"
replace wiki = "Walt_Wesley" if inputd == "Walt Wesley"

end

// This makes baseball data for MT
program make_mt

use ${basketball}bk_master, clear

gen displayname = namefirst + " " + namelast
gen searchname = namefirst + "+" + namelast + "+" + " basketball"

gen link = "http://en.wikipedia.org/w/index.php?title=Special%3ASearch&profile=default&search=" + searchname + "&fulltext=Search"

outsheet playerid displayname link using ${stash}mt_input_bk.csv, replace comma

end

program make_basketball

make_hof

make_allstar

make_match

make_master

end

program read_mt

insheet using ${basketball}mt_bk_output.csv, clear

gen wikihandle = subinstr(answer, "http://en.wikipedia.org/wiki/","",.)
keep inputp inputdisp wikihandle

gen tmp = subinstr(inputd, " ","_",.)
fix_errors

save ${basketball}mt_bk_output, replace

end

program fix_errors

replace wiki = tmp if inputd == "Slater Martin"
replace wiki = tmp if inputd == "Tom Meschery"
replace wiki = tmp if wiki == "Micheal_Williams"
replace wiki = tmp if inputd == "Michael Cage"
replace wiki = tmp if inputd == "Alvin Robertson"
replace wiki = tmp if inputd == "Magic Johnson"
replace wiki = tmp if inputd == "Michael Jordan"
replace wiki = tmp if inputd == "Phil Jordon"

replace wiki = "George_L._Johnson" if inputd == "George L. Johnson"

replace wiki = "Wayne_Cooper_(basketball)" if inputd == "Wayne Cooper"

replace wiki = "" if inputd == "George L. Johnson"

replace wiki = "Micheal_Ray_Richardson" if inputd == "Micheal Ray Richardson"

replace wiki = "Eddie_Johnson_(basketball,_born_1955)" if inputd == "Eddie Johnson"
replace wiki = "Eddie_A._Johnson" if inputd == "Eddie A. Johnson"

replace wiki = "Mike_Brown_(basketball,_born_1963)" if inputd == "Mike Brown"

replace wiki = "Mahmoud_Abdul-Rauf" if wiki == "Mahmud"
replace wiki = "Jan_van_Breda_Kolff" if inputd == "Jan Vanbredakolff"
replace wiki = "Norm_Van_Lier" if inputd == "Norm Vanlier"
replace wiki = "Danny_Vranes" if inputd == "Danny Vranes"
replace wiki = "Dick_Barnett" if inputd == "Dick Barnett"
replace wiki = "Walt_Wesley" if inputd == "Walt Wesley"

end

// This makes baseball data for MT
program make_mt

use ${basketball}bk_master, clear

gen displayname = namefirst + " " + namelast
gen searchname = namefirst + "+" + namelast + "+" + " basketball"

gen link = "http://en.wikipedia.org/w/index.php?title=Special%3ASearch&profile=default&search=" + searchname + "&fulltext=Search"

outsheet playerid displayname link using ${stash}mt_input_bk.csv, replace comma

end

// This makes master baseball dataset
program make_master

insheet using ${basketball}players.csv, clear

replace college = college + ", " + birthdate if v12 != ""
replace birthdate = v12 if v12 != ""
drop v12

merge 1:1 ilkid using ${stash}bk_tmp, keep(match) nogen

gen birthyear = substr(birthdate,1,4)
rename firstseason debutyear
rename lastseason lastyear

rename ilkid playerid

keep playerid firstname lastname debutyear lastyear birthyear minutesrank minutes gp asts pts reb

rename firstname namefirst
rename lastname namelast

gen playername = namefirst + " " + namelast

// generate file for amazon to get me data on
drop if debutyear < 1940
drop if debutyear > 1990

drop if minutesrank > 1000

save ${basketball}bk_master, replace

end

program make_careermins

insheet using ${basketball}player_career.csv, clear
keep ilk firstname lastname gp minutes* pts asts reb turnover

// this data is at player-league level.
local vars "gp minutes pts reb asts turnover"
foreach x in `vars'{
    bysort ilk: egen tmp = total(`x')
    replace `x' = tmp
    drop tmp
}

bysort ilk: drop if _n>1

gsort- minutes
gen minutesrank = _n

save ${stash}bk_tmp, replace

end


+small note about bshell

+small note about bshell

{smcl}
{* *! version 1.20.1 09sep2021}{...}
{vieweralsosee "parallel" "help parallel"}{...}
{cmd:help bshell}
{hline}

{title:Title}

{phang}
{bf:bshell} {hline 2} Stata module to run shell processes in all platforms and modes.

{marker syntax}{...}
{title:Syntax}

{p 8 17 2}
{cmd:bshell} {it:command}

{marker description}{...}
{title:Description}

{pstd}
This is a drop-in replacement for {cmd:shell} that works on all platforms and modes.
Normally it just passes the {it:command} on to {cmd:shell}. But in batch-mode on Windows {cmd:shell} doesn't work
(commands are ignored) and so in these cases we use a compiled plugin to launch the process using win32 API.
If you need to invoke {cmd:cmd}, you can call {cmd:bshell cmd /c ...}.

{marker examples}{...}
{title:Examples}

{pstd}
Example using {cmd:bshell}:

	{cmd:bshell notepad}

{smcl}
{* *! version 1.20.1 09sep2021}{...}
{vieweralsosee "parallel" "help parallel"}{...}
{cmd:help bshell}
{hline}

{title:Title}

{phang}
{bf:bshell} {hline 2} Stata module to run shell processes in all platforms and modes.

{marker syntax}{...}
{title:Syntax}

{p 8 17 2}
{cmd:bshell} {it:command}

{marker description}{...}
{title:Description}

{pstd}
This is a drop-in replacement for {cmd:shell} that works on all platforms and modes.
Normally it just passes the {it:command} on to {cmd:shell}. But in batch-mode on Windows {cmd:shell} doesn't work
(commands are ignored) and so in these cases we use a compiled plugin to launch the process using win32 API.

{marker examples}{...}
{title:Examples}

{pstd}
Example using {cmd:bshell}:

	{cmd:bshell notepad}


added quality charts

added quality charts

clear all
set more off
set matsize 11000
program drop _all

qui adopath + "/mnt/nfs6/wikipedia.proj/wikibaseball/scripts/stata/ado"
declare_global

cd ${path}

// 1a. Produce Baseball / Basketball datasets
make_baseball

make_basketball

make_traf

make_rev

use ${lahman}bb_master, clear
append using ${basketball}bk_master
gen isbaseball = (minutesrank==.)
save ${stash}bbk_master, replace

// 1b. Merge data

make_merge

/////////// ANALYSIS //////////////////

// 1. Summary Stats
summary

t_test 1
t_test 0

// 2. Digitization
program drop _all
reg_all digit

// 3. Copy DD
reg_all copy

// 4. Copy DDD
reg_ddd

// 5. DD regressions -- Traf
reg_traf

// 5b. IV == Traf
ivest

// FIGURES

// 1. figure

// 2. Mean charts
meanline img Images(Baseball) 1
meanline img Images(Basketball) 0

program drop _all
meanline text Text(Baseball) 1
meanline text Text(Basketball) 0

meanline bd Citations(Baseball) 1
meanline bd Citations(Basketball) 0

// 6. Killer Pictures
killer_pic2 1
killer_pic2 0

// 7. Time dummies picture
ddpic bd 1
ddpic bd 0

ddpic img 1
ddpic img 0

ddpic text 1
ddpic text 0

// 8. Scatterplot
traf_scatter

// 9. Quality-wise impacts
chart_qual img 1
chart_qual traf 1
chart_qual text 1
chart_qual bd 1

// APPENDIX
reg_all digit ln

reg_all copy ln

reg_ddd ln

reg_traf ln

program drop _all
reg_robust copy

clear all
set more off
set matsize 11000
program drop _all

qui adopath + "/mnt/nfs6/wikipedia.proj/wikibaseball/scripts/stata/ado"
declare_global

cd ${path}

// 1a. Produce Baseball / Basketball datasets
make_baseball

make_basketball

make_traf

make_rev

use ${lahman}bb_master, clear
append using ${basketball}bk_master
gen isbaseball = (minutesrank==.)
save ${stash}bbk_master, replace

// 1b. Merge data
make_merge

/////////// ANALYSIS //////////////////

// 1. Summary Stats
summary

t_test 1
t_test 0

// 2. Digitization
program drop _all
reg_all digit

// 3. Copy DD
reg_all copy

// 4. Copy DDD
reg_ddd

// 5. DD regressions -- Traf
reg_traf

// 5b. IV == Traf
ivest

// FIGURES

// 1. figure

// 2. Mean charts
meanline img Images(Baseball) 1
meanline img Images(Basketball) 0

program drop _all
meanline text Text(Baseball) 1
meanline text Text(Basketball) 0

meanline bd Citations(Baseball) 1
meanline bd Citations(Basketball) 0

// 6. Killer Pictures
killer_pic2 1
killer_pic2 0

// 7. Time dummies picture
ddpic bd 1
ddpic bd 0

ddpic img 1
ddpic img 0

ddpic text 1
ddpic text 0

// 8. Scatterplot
traf_scatter

// APPENDIX
reg_all digit ln

reg_all copy ln

reg_ddd ln

reg_traf ln

program drop _all
reg_robust copy


Small helpfil tweaks after using it in the GUI viewer.

{smcl}
{* *! version 0.0.1  28may2015}{...}
{vieweralsosee "[R] regress" "mansection R regress"}{...}
{viewerjumpto "Syntax" "svm##syntax"}{...}
{viewerjumpto "Description" "svm##description"}{...}
{viewerjumpto "Options" "svm##options"}{...}
{viewerjumpto "Examples" "svm##examples"}{...}
{viewerjumpto "Stored results" "svm##results"}{...}
{viewerjumpto "Gotchas" "svm##gotchas"}{...}
{viewerjumpto "Copyright" "svm##copyright"}{...}
{viewerjumpto "Exeunt" "svm##exeunt"}{...}
{viewerjumpto "References" "svm##references"}{...}
{...}{* NB: these hide the newlines }
{...}
{...}
{title:Title}

{p2colset 5 18 20 2}{...}
{p2col :{cmd:svm} {hline 2}}Support Vector Machines{p_end}
{p2colreset}{...}

{marker syntax}{...}
{title:Syntax}

{p 8 16 2}
svm {depvar} [{indepvars}] {ifin} [{it:{help svm##weight:weight}}] [{cmd:,} {it:options}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{syntab:Model}
{synopt :{cmdab:t:ype}}Type of model to fit: C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, or NU_SVR. Default: C_SVC{p_end}
{synopt :{cmdab:k:ernel}}SVM kernel function to use: LINEAR, POLY, RBF, SIGMOID, or PRECOMPUTED. Default: RBF{p_end}

{synopt :{cmdab:gamma:}}For POLY, RBF and SIGMOID kernels, a scaling factor for the linear part. Default: 1/[# variables TODO: what's Stata's hidden macro for this?]{p_end}
{synopt :{cmdab:coef0:}}For POLY and SIGMOID kernels, an intercept term for the linear part. Default: 0{p_end}
{synopt :{cmdab:deg:ree}}For POLY kernels, the degree of the polynomial to use. Default: cubic (3){p_end}

{* XXX the division between 'tuning' and 'model' parameters is hazy; e.g. you could in theory cross-validate to choose degree (and people do this with neural networks), or even to choose the kernel . hmmmmm}{...}

{syntab:Tuning}
{synopt :{cmdab:C:}}For C_SVC, EPSILON_SVR and NU_SVR SVMs, this is a regularization parameter which weights the slack variables [citation needed]. Default: 1{p_end}
{synopt :{cmdab:p:}}For EPSILON_SVR SVMs, this provides the error tolerance boundary [citation needed]. Default: 0.1{p_end}
{synopt :{cmdab:nu:}}For NU_SVC, ONE_CLASS, and NU_SVR SVMs, .... Default: 0.5{p_end}

{synopt :{cmdab:eps:ilon}}The stopping tolerance used to decide when convergence has happened. Default: 0.001{p_end}
{synopt :{cmdab:shrink:ing}}Whether or not to use shrinkage heuristics (regularization??) to improve the fit. Default: disabled{p_end}
{synopt :{cmdab:autonorm:alize}}Whether to center and scale the data. NOT IMPLEMENTED. Default: disabled{p_end}

{syntab:Performance}
{synopt :{cmdab:prob:ability}}Whether or not to compute extra data needed for "predict, prob". Disable with "noprobability". Default: enabled{p_end}
{synopt :{cmdab:cache:_size}}The size of the RAM cache used during fitting, in megabytes. Default: 100MB (100){p_end}
{synopt :{cmdab:SVs:}}Whether or not to return the SVs matrix. NOT IMPLEMENTED. Disable with "noSVs". Default: enabled{p_end}
{synopt :{cmdab:nSVs:}}Whether or not to return the nSVs matrix. NOT IMPLEMENTED. Disable with "nonSVs". Default: enabled{p_end}
{synopt :{cmdab:labels:}}Whether or not to return the labels matrix. NOT IMPLEMENTED. Disable with "nolabels". Default: enabled{p_end}
{synopt :{cmdab:sv_coef:}}Whether or not to return the sv_coef matrix. NOT IMPLEMENTED. Disable with "nosvcoef". Default: enabled{p_end}
{synopt :{cmdab:rho:}}Whether or not to return the rho matrix. NOT IMPLEMENTED. Disable with "norho". Default: enabled{p_end}
{* NB: there's no probA or probB options because those are just cruft in support of "predict, prob"... I think. }{...}

{synoptline}
{pstd}All variables must be numeric (Stata stores categoricals as numerics); use {help encode} to map string variables to classes.{p_end}
INCLUDE help fvvarlist

{p 8 16 2}
predict {targetvar} {ifin}, [{cmdab:prob:ability}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{cmdab:prob:ability}}If specified, estimate class probabilities for each observation. This only makes sense for classification problems.{p_end}

{p 8 16 2}
svm_export using {filename}

{p 8 16 2}
svm_import using {filename}

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{cmd: using}}The filename to export or import a fitted libsvm model from, conventionally ending in '.model'.{p_end}

{marker description}{...}
{title:Description}

{pstd}
{cmd:svm} fits a support vector machine (SVM) model of {depvar} on {indepvars}.
SVM is not one, but several variant models, all based upon splitting hyperplanes and culling unimportant observations.
The name comes part from how each observation in a dataset can be thought of as a vector in d-dimensional space, where d is the number of {indepvars}
and part from the culling: the "support vectors" are those observations which the algorithm detects are critical to the fit.

{pstd}
Not all combinations of options are valid, and you will get an error if you specify an invalid combination.
And amongst valid combinations, not all options and datasets will give good results.
The most important choice you need to make when fitting is what type and what kernel to use. Our goal is to have sane defaults, so that the other options are very very optional, only brought out for difficult datasets.
See {help svm##svmtutorial} for a gentle introduction and coverage of the specification issues involved in using SVM.

{pstd}
This package is a thin wrapper for the widely deployed {help svm##libsvm:libsvm},
so in addition to the Stata package, libsvm must be installed.
On Windows, libsvm.dll should have been bundled with the package,
and you can find it in your {help adopath} (try {cmd:findfile libsvm.dll} to verify this).
On OS X, libsvm is available in both {browse "https://brew.sh":brew} and {browse "https://www.macports.org":macports}.
On Linux, search for libsvm in your distribution's package manager.
You can also install libsvm from source, which you would need to do if your package manager has an older or no version of libsvm.
If you are having trouble getting the plugin loaded, please contact the authors,
as we do not have access to all combinations of operating systems and we want to make the experience as smooth as possible for our users.
The thinness is a feature, to keep things simple and portable between statistical systems;
if you have feature requests send them to us but know that we will probably not implement them until we get them built into libsvm so that everyone can benefit.

{pstd}
If you are confused by the options, refer to the authoratative source:
the libsvm {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html":FAQ},
{browse "https://github.com/cjlin1/libsvm/blob/master/README":README},
and {help svm##libsvm:implementation paper}.
Please write us with suggestions for clarification.

{marker options}{...}
{title:Options}

{title:svm}

{pstd}
{cmd:svm} does model training, otherwise known as fitting or estimation (depending on your statistical background).

{phang}
{opt type} specifies what subtype of SVM to run.
C_SVC and NU_SVC perform classification, and your {depvar} should be a discrete variable, probably stored as byte or int.
You can tell if you have made this mistake if there is a unique class for each observation.
libsvm automatically handles multiclass classification using the one-against-all [citation needed].
ONE_CLASS ignores {depvar} (????) and estimates the support---where in feature space---the data lies [......].
EPSILON_SVR and NU_SVR perform regression. You can use this with discrete variables, but more commonly you would use it with continuous ones stored as float or double.
To learn about the NU distinction, see {help svm##nusvm:Chen and Lin's ν-SVM tutorial}.

{phang}
{opt kernel} gives a kernel function to use.
The basic SVM algorithm finds a linear boundary (a hyperplane), between two classes [XXX how does this apply to SVR?].
The {browse "https://en.wikipedia.org/wiki/Kernel_Method":kernel trick} attempts to make data linearly-separable by mapping it into a higher dimensional space.
Additionally, it can handle curved boundaries by effectively bending the lower dimensional space. See {browse "https://www.youtube.com/watch?v=3liCbRZPrZA"} for a visualization of this process.
The trick is that the fit can be done efficiently because the fit does not care about the points under the mapping,
but rather cares about their kernel value <u,x>, a value used in scoring the coefficients u,
and for certain kernels this value can be computed straight from the original data without doing the mapping at all.{p_end}

{pmore}Choices of kernels are:{p_end}
{pmore2}LINEAR means the linear kernel you are probably familiar with from {help regress:OLS}: u'*v{p_end}
{pmore2}POLY is (gamma*u'*v + coef0)^degree{p_end}
{pmore2}RBF stands for Radial Basis Functions, and treats the coefficients as a mean to smoothly approach in a ball, with the form exp(-gamma*|u-v|^2); this kernel tends to be good at [...].{p_end}
{pmore2}SIGMOID is a kernel which bends the linear kernel to fit in -1 to 1, similar to {help logistic} regression: tanh(gamma*u'*v + coef0){p_end}
{pmore2}PRECOMPUTED assumes that {depvar} is actually a list of precomputed kernel values.{p_end}

{phang}
{opt gamma} is used in the non-linear kernels as a scaling factor for the linear part, as seen above.
[TODO: tips about choosing this]

{phang}
Similarly, {opt coef0} is used in the non-linear kernels as a pseudo-intercept term.
Except it is not used in the RBF kernel as RBF is essentially a distance function, and biasing would be pointless.[???]
[TODO: tips about choosing this]

{phang}
{opt degree} selects the degree of the polynomial used by the POLY kernel.
Setting this too high will result in overfitting. Setting it too low may result in non-convergence.
[TODO: tips about choosing this]

{phang}
{opt C} weights (regularizes) the slack variables used in C_SVC, EPSILON_SVR and NU_SVR
[TODO: tips about choosing this]

{phang}
{opt p} is the epsilon used by EPSILON_SVR. Larger makes your fit more flexible, and can lead to underfitting[???]. Smaller can lead to overfitting. (the name epsilon was already taken for an option, so the libsvm authors chose "p").

{phang}
{opt nu} is used in the NU variants. See {help svm##nusvm:the ν-SVM tutorial} for details.
[TODO: ...]

{phang}
{opt epsilon} is the stopping tolerance used by the numerical optimizer. You could widen this if you are finding convergence is slow or not occurring, but be aware that this usually non-convergence is a deeper problem in the data versus the kernel.

{phang}
{opt shrinking} invokes libsvm's built-in shrinking heuristics [TODO: what does this mean?]

{phang}
{opt autonormalize} instructs the fitter to first center and scale the data
so that every column has identical mean and variance
 as suggested in the {help svm##libsvmguide:libsvm guide}.
 In theory, this should help the results---as measured by cross-validation---
 by weighting each column's effect the same and reducing the chance for numerical error
 but this is not always the case, so it is disabled by default.

{pmore}
If enabled, {cmd:predict} will also autonormalize.

{phang}
{opt probability} enables the use of "predict, prob" as described below. This takes additional CPU and space, so if you can disable it if you don't need it by writing "noprobability" instead.

{phang}
{opt cache_size} tweaks an internal libsvm parameter of how much RAM to use during training. Value is given in megabytes (MB).
[TODO: what effect does this have?]

{phang}
{opt SVs},
{opt nSVs},
{opt labels},
{opt sv_coef}, and
{opt rho}
control whether or not these matrices are exported from libsvm to Stata.
Though libsvm is perfectly capable of allocating space for them, and always does,
your {help matsize} may not be large enough for them, so you can disable each individually by prefixing "no", e.g. "nosv_coef". See {help svm##gotchas:gotchas}.
{...}
{...}

{title:predict}

{pstd}
After training you can ask svm to {cmd:predict} what it thinks the classes (classification) or values (regression) of given observations into {targetvar}.
{targetvar} must not exist, so if you want to repredict your choices are {cmd:drop {targetvar}} or to pick a new name, e.g. {cmd:predict {targetvar}2}.{p_end}
{pmore}For classification problems, {opt probability} requests, for each observation, the probability of it being each class.{p_end}
{pmore}For regression problems, {opt probability} requests [....].{p_end}
{pmore}In addition to predictions in {targetvar}, {targetvar} is used as a stem for names of new columns for the results.
This option will fail if you specified {opt noprobability} during estimation.{p_end}

{pstd}
Prediction automatically uses the same {indepvars} as in training, so if you rename or drop columns between commands you will have problems.
{...}
{...}

{title:import/export}

{pstd}
libsvm has an ad-hoc format it uses to save trained models. The command line programs {cmd:svm-train} and {cmd:svm-predict} that come with libsvm communicate via it. We support it for completeness. Those programs use the '.model' file extension by default, and we suggest you follow this convention.

{pstd}
{cmd:svm_export} will write a fitted model to disk.

{pstd}
{cmd:svm_import} will load a model from disk, replacing any previous in-memory fit. When you import, [some properties] will be missing because the import was done without reference to any dataset.

{pstd}
Do not confuse these commands with {cmd:import_svmlight} and {cmd:export_svmlight}.

{marker examples}{...}
{title:Examples:  classification}

        {hline}
{phang2}{cmd:. webuse highschool, clear}{p_end}
{phang2}{cmd:. svm gender}{p_end}
{phang2}{cmd:. ereturn list}{p_end}
{phang2}{cmd:. predict P, prob}{p_end}
        {hline}

{title:Examples:  regression, suppressing sv_coef}

        {hline}
{phang2}{cmd:. sysuse auto, clear}{p_end}
        {hline}

{title:Examples:  classification with tuning}

        {hline}
{phang2}{cmd:. webuse regsmpl, clear}{p_end}
        {hline}

{title:Examples:  forecasting}

        {hline}
--- train on one set of data, generate more observations, predict
        {hline}

{title:Example:  weighted regression}

{pstd}
Not currently implemented.

{marker results}{...}
{title:Stored results}

{pstd}
{cmd:svm} stores the following in {cmd:e()}:

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Scalars}{p_end}
{synopt:{cmd:e(nr_class)}}number of classes, in a classification problem. [??? if SVR??]{p_end}
{synopt:{cmd:e(l)}}number of support vectors{p_end}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Macros}{p_end}
{synopt:{cmd:e(cmd)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(cmdline)}}command as typed{p_end}
{synopt:{cmd:e(depvar)}}name of dependent variable{p_end}
{synopt:{cmd:e(title)}}title in estimation output{p_end}
{synopt:{cmd:e(model)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(svm_type)}}SVM type string, as above{p_end}
{synopt:{cmd:e(svm_kernel)}}kernel string, as above{p_end}
{synopt:{cmd:e(predict)}}program used to implement {cmd:predict}{p_end}
{* {synopt:{cmd:e(estat_cmd)}}program used to implement {cmd:estat}{p_end} }{...}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Matrices}(may be missing, depending on options){p_end}
{synopt:{cmd:e(SVs)}}A list of the support vectors chosen. Numbers are indices into observations of the dataset; this will not exist in models loaded from {cmd:svm_import}.{p_end}
{synopt:{cmd:e(labels)}}List of the class "labels" (which are integers, as far as libsvm is concerned). This should be the same set as in the original dataset, but libsvm may permute it. [XXX hide this from Stata; just use it to label the]{p_end}
{synopt:{cmd:e(nSVs)}}The number of support vectors belonging to each class, in a classification problem.{p_end}
{synopt:{cmd:e(sv_coef)}}???{p_end}
{synopt:{cmd:e(rho)}}??? something involving multiclass decision boundaries ???{p_end}

{* XXX this section name should be more formal}{marker gotchas}{...}
{title:Gotchas}

{pstd}
{bf:Memory Limits}: The cheaper versions of stata support allow less memory to be used. As machine learning problems typically are on very large datasets,
it is easy to inadvertently instruct this package to construct more columns or larger matrices than you can afford.
In this case, you will receive an error and find yourself with a partially allocated set of probability columns or a partial set of {cmd:e()} matrices^.

{pmore}
You can proceed by retrying the command with various combinations of "no{matrix}" options applied to reduce the output size. If done in the same session you will also need to manually drop the mistake columns with {cmd:drop {targetvar}*}.

{pmore}
If you are really stuck, you can also proceed by giving up on Stata and switching to libsvm's companion {cmd:svm-train} program,
will have been installed with the libsvm package if you used a package manager, or
which you can get {browse "http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+zip":from its authors};
you can use {help export svmlight} to extract your dataset for {cmd:svm-train}.

{pmore}^{browse "https://github.com/kousu/statasvm/pulls":patches} to instead trap errors and restore the previous state are very welcome.

{marker copyright}{...}
{title:Copyright}

{pstd}
The wrapper is licensed:

{pmore}
The MIT License (MIT)

{pmore}
Except where otherwise noted in the code, Copyright (c) 2015
Nick Guenther <nguenthe@uwaterloo.ca>, Matthias Schonlau <schonlau@uwaterloo.ca>

{pmore}
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

{pmore}
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

{pmore}
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

{pstd}
libsvm is licensed:

{pmore}
Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin
All rights reserved.

{pmore}
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

{pmore}
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

{pmore}
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

{pmore}
3. Neither name of copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

{pmore}
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

{marker exeunt}{...}
{title:Exeunt}

{pstd}
Though the license does not obligate you in any way to do so, if you find
this software useful we would be curious and appreciative to hear about your adventures in machine learning through Stata.{p_end}
{pmore}Thank you.

{pmore}* Nick Guenther <nguenthe@uwaterloo.ca>{p_end}
{pmore}* Matthias Schonlau <schonlau@uwaterloo.ca>{p_end}

{marker references}{...}
{title:References}

{marker sourcecode}{...}
{phang}
Guenther, Nick and Schonlau, Matthias. 2015. Stata-SVM.
{browse "https://github.com/kousu/statasvm/"}
{p_end}

{marker svmtutorial}{...}
{phang}
[svmtutorial] TODO
{p_end}

{marker libsvm}{...}
{phang}
Chang, Chih-Chung and Lin, Chih-Jen. 2011.
{it:LIBSVM : a library for support vector machines.}
ACM Transactions on Intelligent Systems and Technology, 2:27:1--27:27.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/libsvm.pdf"}.
Software available at {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm"}
{p_end}

{marker libsvmguide}{...}
{phang}
Chih-Wei Hsu, Chih-Chung Chang, and Chih-Jen Lin.
{it:A Practical Guide to Support Vector Classification.}
April 15, 2010.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf"}
{p_end}

{marker nusvm}{...}
{phang}
Pai-Hsuen Chen, Chih-Jen Lin, and Bernhard Schölkopf.
{it:A Tutorial on ν-Support Vector Machines}.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/nusvmtutorial.pdf"}
{p_end}

{smcl}
{* *! version 0.0.1  28may2015}{...}
{vieweralsosee "[R] regress" "mansection R regress"}{...}
{viewerjumpto "Syntax" "svm##syntax"}{...}
{viewerjumpto "Description" "svm##description"}{...}
{viewerjumpto "Options" "svm##options"}{...}
{viewerjumpto "Examples" "svm##examples"}{...}
{viewerjumpto "Stored results" "svm##results"}{...}
{viewerjumpto "Gotchas" "svm##gotchas"}{...}
{viewerjumpto "Copyright" "svm##copyright"}{...}
{viewerjumpto "Exeunt" "svm##exeunt"}{...}
{viewerjumpto "References" "svm##references"}{...}
{...}{* NB: these hide the newlines }
{...}
{...}
{title:Title}

{p2colset 5 18 20 2}{...}
{p2col :{cmd:svm} {hline 2}}Support Vector Machines{p_end}
{p2colreset}{...}

{marker syntax}{...}
{title:Syntax}

{p 8 16 2}
svm {depvar} [{indepvars}] {ifin} [{it:{help svm##weight:weight}}] [{cmd:,} {it:options}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{syntab:Model}
{synopt :{cmdab:t:ype}}Type of model to fit: C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, or NU_SVR. Default: C_SVC{p_end}
{synopt :{cmdab:k:ernel}}SVM kernel function to use: LINEAR, POLY, RBF, SIGMOID, or PRECOMPUTED. Default: RBF{p_end}

{synopt :{cmdab:gamma:}}For POLY, RBF and SIGMOID kernels, a scaling factor for the linear part. Default: 1/[# variables TODO: what's Stata's hidden macro for this?]{p_end}
{synopt :{cmdab:coef0:}}For POLY and SIGMOID kernels, an intercept term for the linear part. Default: 0{p_end}
{synopt :{cmdab:deg:ree}}For POLY kernels, the degree of the polynomial to use. Default: cubic (3){p_end}

{* XXX the division between 'tuning' and 'model' parameters is hazy; e.g. you could in theory cross-validate to choose degree (and people do this with neural networks), or even to choose the kernel . hmmmmm}{...}

{syntab:Tuning}
{synopt :{cmdab:C:}}For C_SVC, EPSILON_SVR and NU_SVR SVMs, this is a regularization parameter which weights the slack variables [citation needed]. Default: 1{p_end}
{synopt :{cmdab:p:}}For EPSILON_SVR SVMs, this provides the error tolerance boundary [citation needed]. Default: 0.1{p_end}
{synopt :{cmdab:nu:}}For NU_SVC, ONE_CLASS, and NU_SVR SVMs, .... Default: 0.5{p_end}

{synopt :{cmdab:eps:ilon}}The stopping tolerance used to decide when convergence has happened. Default: 0.001{p_end}
{synopt :{cmdab:shrink:ing}}Whether or not to use shrinkage heuristics (regularization??) to improve the fit. Default: disabled{p_end}
{synopt :{cmdab:autonorm:alize}}Whether to center and scale the data. NOT IMPLEMENTED. Default: disabled{p_end}

{syntab:Performance}
{synopt :{cmdab:prob:ability}}Whether or not to compute extra data needed for "predict, prob". Disable with "noprobability". Default: enabled{p_end}
{synopt :{cmdab:cache:_size}}The size of the RAM cache used during fitting, in megabytes. Default: 100MB (100){p_end}
{synopt :{cmdab:SVs:}}Whether or not to return the SVs matrix. NOT IMPLEMENTED. Disable with "noSVs". Default: enabled{p_end}
{synopt :{cmdab:nSVs:}}Whether or not to return the nSVs matrix. NOT IMPLEMENTED. Disable with "nonSVs". Default: enabled{p_end}
{synopt :{cmdab:labels:}}Whether or not to return the labels matrix. NOT IMPLEMENTED. Disable with "nolabels". Default: enabled{p_end}
{synopt :{cmdab:sv_coef:}}Whether or not to return the sv_coef matrix. NOT IMPLEMENTED. Disable with "nosvcoef". Default: enabled{p_end}
{synopt :{cmdab:rho:}}Whether or not to return the rho matrix. NOT IMPLEMENTED. Disable with "norho". Default: enabled{p_end}
{* NB: there's no probA or probB options because those are just cruft in support of "predict, prob"... I think. }{...}

{synoptline}
{pstd}All variables must be numeric (Stata stores categoricals as numerics); use {help encode} to map string variables to classes.{p_end}
INCLUDE help fvvarlist

{p 8 16 2}
predict {targetvar} {ifin}, [{cmdab:prob:ability}]

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{cmdab:prob:ability}}If specified, estimate class probabilities for each observation. This only makes sense for classification problems.{p_end}

{p 8 16 2}
svm_export using {filename}

{p 8 16 2}
svm_import using {filename}

{synoptset 20 tabbed}{...}
{synopthdr}
{synoptline}
{synopt :{cmd: using}}The filename to export or import a fitted libsvm model from, conventionally ending in '.model'.{p_end}

{marker description}{...}
{title:Description}

{pstd}
{cmd:svm} fits a support vector machine (SVM) model of {depvar} on {indepvars}.
SVM is not one, but several variant models, all based upon splitting hyperplanes and culling unimportant observations.
The name comes part from how each observation in a dataset can be thought of as a vector in d-dimensional space, where d is the number of {indepvars}
and part from the culling: the "support vectors" are those observations which the algorithm detects are critical to the fit.

{pstd}
Not all combinations of options are valid, and you will get an error if you specify an invalid combination.
And amongst valid combinations, not all options and datasets will give good results.
The most important choice you need to make when fitting is what type and what kernel to use. Our goal is to have sane defaults, so that the other options are very very optional, only brought out for difficult datasets.
See {help svm##svmtutorial} for a gentle introduction and coverage of the specification issues involved in using SVM.

{pstd}
This package is a thin wrapper for the widely deployed {help svm##libsvm:libsvm},
so in addition to the Stata package, libsvm must be installed.
On Windows, libsvm.dll should have been bundled with the package,
and you can find it in your {help adopath} (try {cmd:findfile libsvm.dll} to verify this).
On OS X, libsvm is available in both {browse "https://brew.sh":brew} and {browse "https://www.macports.org":macports}.
On Linux, search for libsvm in your distribution's package manager.
You can also install libsvm from source, which you would need to do if your package manager has an older or no version of libsvm.
If you are having trouble getting the plugin loaded, please contact the authors,
as we do not have access to all combinations of operating systems and we want to make the experience as smooth as possible for our users.
The thinness is a feature, to keep things simple and portable between statistical systems;
if you have feature requests send them to us but know that we will probably not implement them until we get them built into libsvm so that everyone can benefit.

{pstd}
If you are confused by the options, refer to the authoratative source:
the libsvm {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html":FAQ},
{browse "https://github.com/cjlin1/libsvm/blob/master/README":README},
and {help svm##libsvm:implementation paper}.
Please write us with suggestions for clarification.

{marker options}{...}
{...}
{title:svm}

{pstd}
{cmd:svm} does model training, otherwise known as fitting or estimation (depending on your statistical background).

{phang}
{opt type} specifies what subtype of SVM to run.
C_SVC and NU_SVC perform classification, and your {depvar} should be a discrete variable, probably stored as byte or int.
You can tell if you have made this mistake if there is a unique class for each observation.
libsvm automatically handles multiclass classification using the one-against-all [citation needed].
ONE_CLASS ignores {depvar} (????) and estimates the support---where in feature space---the data lies [......].
EPSILON_SVR and NU_SVR perform regression. You can use this with discrete variables, but more commonly you would use it with continuous ones stored as float or double.
To learn about the NU distinction, see {help svm##nusvm:Chen and Lin's ν-SVM tutorial}.

{phang}
{opt kernel} gives a kernel function to use.
The basic SVM algorithm finds a linear boundary (a hyperplane), between two classes [XXX how does this apply to SVR?].
The {browse "https://en.wikipedia.org/wiki/Kernel_Method":kernel trick} attempts to make data linearly-separable by mapping it into a higher dimensional space.
Additionally, it can handle curved boundaries by effectively bending the lower dimensional space. See {browse "https://www.youtube.com/watch?v=3liCbRZPrZA"} for a visualization of this process.
The trick is that the fit can be done efficiently because the fit does not care about the points under the mapping,
but rather cares about their kernel value <u,x>, a value used in scoring the coefficients u,
and for certain kernels this value can be computed straight from the original data without doing the mapping at all.{p_end}

{pmore}Choices of kernels are:{p_end}
{pmore2}LINEAR means the linear kernel you are probably familiar with from {help regress:OLS}: u'*v{p_end}
{pmore2}POLY is (gamma*u'*v + coef0)^degree{p_end}
{pmore2}RBF stands for Radial Basis Functions, and treats the coefficients as a mean to smoothly approach in a ball, with the form exp(-gamma*|u-v|^2); this kernel tends to be good at [...].{p_end}
{pmore2}SIGMOID is a kernel which bends the linear kernel to fit in -1 to 1, similar to {help logistic} regression: tanh(gamma*u'*v + coef0){p_end}
{pmore2}PRECOMPUTED assumes that {depvar} is actually a list of precomputed kernel values.{p_end}

{phang}
{opt gamma} is used in the non-linear kernels as a scaling factor for the linear part, as seen above.
[TODO: tips about choosing this]

{phang}
Similarly, {opt coef0} is used in the non-linear kernels as a pseudo-intercept term.
Except it is not used in the RBF kernel as RBF is essentially a distance function, and biasing would be pointless.[???]
[TODO: tips about choosing this]

{phang}
{opt degree} selects the degree of the polynomial used by the POLY kernel.
Setting this too high will result in overfitting. Setting it too low may result in non-convergence.
[TODO: tips about choosing this]

{phang}
{opt C} weights (regularizes) the slack variables used in C_SVC, EPSILON_SVR and NU_SVR
[TODO: tips about choosing this]

{phang}
{opt p} is the epsilon used by EPSILON_SVR. Larger makes your fit more flexible, and can lead to underfitting[???]. Smaller can lead to overfitting. (the name epsilon was already taken for an option, so the libsvm authors chose "p").

{phang}
{opt nu} is used in the NU variants. See {help svm##nusvm:the ν-SVM tutorial} for details.
[TODO: ...]

{phang}
{opt epsilon} is the stopping tolerance used by the numerical optimizer. You could widen this if you are finding convergence is slow or not occurring, but be aware that this usually non-convergence is a deeper problem in the data versus the kernel.

{phang}
{opt shrinking} invokes libsvm's built-in shrinking heuristics [TODO: what does this mean?]

{phang}
{opt autonormalize} instructs the fitter to first center and scale the data
so that every column has identical mean and variance
 as suggested in the {help svm##libsvmguide:libsvm guide}.
 In theory, this should help the results---as measured by cross-validation---
 by weighting each column's effect the same and reducing the chance for numerical error
 but this is not always the case, so it is disabled by default.

{pmore}
If enabled, {cmd:predict} will also autonormalize.

{phang}
{opt probability} enables the use of "predict, prob" as described below. This takes additional CPU and space, so if you can disable it if you don't need it by writing "noprobability" instead.

{phang}
{opt cache_size} tweaks an internal libsvm parameter of how much RAM to use during training. Value is given in megabytes (MB).
[TODO: what effect does this have?]

{phang}
{opt SVs},
{opt nSVs},
{opt labels},
{opt sv_coef}, and
{opt rho}
control whether or not these matrices are exported from libsvm to Stata.
Though libsvm is perfectly capable of allocating space for them, and always does,
your {help matsize} may not be large enough for them, so you can disable each individually by prefixing "no", e.g. "nosv_coef". See {help svm##gotchas:gotchas}.
{...}
{...}

{title:predict}

{pstd}
After training you can ask svm to {cmd:predict} what it thinks the classes (classification) or values (regression) of given observations into {targetvar}.
{targetvar} must not exist, so if you want to repredict your choices are {cmd:drop {targetvar}} or to pick a new name, e.g. {cmd:predict {targetvar}2}.{p_end}
{pmore}For classification problems, {opt probability} requests, for each observation, the probability of it being each class.{p_end}
{pmore}For regression problems, {opt probability} requests [....].{p_end}
{pmore}In addition to predictions in {targetvar}, {targetvar} is used as a stem for names of new columns for the results.
This option will fail if you specified {opt noprobability} during estimation.{p_end}

{pstd}
Prediction automatically uses the same {indepvars} as in training, so if you rename or drop columns between commands you will have problems.
{...}
{...}

{title:import/export}

{pstd}
libsvm has an ad-hoc format it uses to save trained models. The command line programs {cmd:svm-train} and {cmd:svm-predict} that come with libsvm communicate via it. We support it for completeness. Those programs use the '.model' file extension by default, and we suggest you follow this convention.

{pstd}
{cmd:svm_export} will write a fitted model to disk.

{pstd}
{cmd:svm_import} will load a model from disk, replacing any previous in-memory fit. When you import, [some properties] will be missing because the import was done without reference to any dataset.

{pstd}
Do not confuse these commands with {cmd:import_svmlight} and {cmd:export_svmlight}.

{marker examples}{...}
{title:Examples:  classification}

        {hline}
{phang2}{cmd:. webuse highschool, clear}{p_end}
{phang2}{cmd:. svm gender}{p_end}
{phang2}{cmd:. ereturn list}{p_end}
{phang2}{cmd:. predict P, prob}{p_end}
        {hline}

{title:Examples:  regression, suppressing sv_coef}

        {hline}
{phang2}{cmd:. sysuse auto, clear}{p_end}
        {hline}

{title:Examples:  classification with tuning}

        {hline}
{phang2}{cmd:. webuse regsmpl, clear}{p_end}
        {hline}

{title:Examples:  forecasting}

        {hline}
--- train on one set of data, generate more observations, predict
        {hline}

{title:Example:  weighted regression}

{pstd}
Not currently implemented.

{marker results}{...}
{title:Stored results}

{pstd}
{cmd:svm} stores the following in {cmd:e()}:

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Scalars}{p_end}
{synopt:{cmd:e(nr_class)}}number of classes, in a classification problem. [??? if SVR??]{p_end}
{synopt:{cmd:e(l)}}number of support vectors{p_end}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Macros}{p_end}
{synopt:{cmd:e(cmd)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(cmdline)}}command as typed{p_end}
{synopt:{cmd:e(depvar)}}name of dependent variable{p_end}
{synopt:{cmd:e(title)}}title in estimation output{p_end}
{synopt:{cmd:e(model)}}"{cmd:svm}"{p_end}
{synopt:{cmd:e(svm_type)}}SVM type string, as above{p_end}
{synopt:{cmd:e(svm_kernel)}}kernel string, as above{p_end}
{synopt:{cmd:e(predict)}}program used to implement {cmd:predict}{p_end}
{* {synopt:{cmd:e(estat_cmd)}}program used to implement {cmd:estat}{p_end} }{...}

{synoptset 20 tabbed}{...}
{p2col 5 20 24 2: Matrices}(may be missing, depending on options){p_end}
{synopt:{cmd:e(SVs)}}A list of the support vectors chosen. Numbers are indices into observations of the dataset; this will not exist in models loaded from {cmd:svm_import}.{p_end}
{synopt:{cmd:e(labels)}}List of the class "labels" (which are integers, as far as libsvm is concerned). This should be the same set as in the original dataset, but libsvm may permute it. [XXX hide this from Stata; just use it to label the]{p_end}
{synopt:{cmd:e(nSVs)}}The number of support vectors belonging to each class, in a classification problem.{p_end}
{synopt:{cmd:e(sv_coef)}}???{p_end}
{synopt:{cmd:e(rho)}}??? something involving multiclass decision boundaries ???{p_end}

{* XXX this section name should be more formal}{marker gotchas}{...}
{title:Gotchas}

{pstd}
{bf:Memory Limits}: The cheaper versions of stata support allow less memory to be used. As machine learning problems typically are on very large datasets,
it is easy to inadvertently instruct this package to construct more columns or larger matrices than you can afford.
In this case, you will receive an error and find yourself with a partially allocated set of probability columns or a partial set of {cmd:e()} matrices^.

{pmore}
You can proceed by retrying the command with various combinations of "no{matrix}" options applied to reduce the output size. If done in the same session you will also need to manually drop the mistake columns with {cmd:drop {targetvar}*}.

{pmore}
If you are really stuck, you can also proceed by giving up on Stata and switching to libsvm's companion {cmd:svm-train} program,
will have been installed with the libsvm package if you used a package manager, or
which you can get {browse "http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+zip":from its authors};
you can use {help export svmlight} to extract your dataset for {cmd:svm-train}.

{pmore}^{browse "https://github.com/kousu/statasvm/pulls":patches} to instead trap errors and restore the previous state are very welcome.

{marker copyright}{...}
{title:Copyright}

{pstd}
The wrapper is licensed:

{pmore}
The MIT License (MIT)

{pmore}
Except where otherwise noted in the code, Copyright (c) 2015
Nick Guenther <nguenthe@uwaterloo.ca>, Matthias Schonlau <schonlau@uwaterloo.ca>

{pmore}
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

{pmore}
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

{pmore}
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

{pstd}
libsvm is licensed:

{pmore}
Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin
All rights reserved.

{pmore}
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

{pmore}
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

{pmore}
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

{pmore}
3. Neither name of copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

{pmore}
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

{marker exeunt}{...}
{title:Exeunt}

{pstd}
Though the license does not obligate you in any way to do so, if you find
this software useful we would be curious and appreciative to hear about your adventures in machine learning through Stata.{p_end}
{pmore}Thank you.

{pmore}* Nick Guenther <nguenthe@uwaterloo.ca>{p_end}
{pmore}* Matthias Schonlau <schonlau@uwaterloo.ca>{p_end}

{marker references}{...}
{title:References}

{marker sourcecode}{...}
{phang}
Guenther, Nick and Schonlau, Matthias. 2015. Stata-SVM.
{browse "https://github.com/kousu/statasvm/"}
{p_end}

{marker svmtutorial}{...}
{phang}
[svmtutorial]
{p_end}

{marker libsvm}{...}
{phang}
Chang, Chih-Chung and Lin, Chih-Jen. 2011.
{it:LIBSVM : a library for support vector machines.}
ACM Transactions on Intelligent Systems and Technology, 2:27:1--27:27.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/libsvm.pdf"}
Software available at {browse "http://www.csie.ntu.edu.tw/~cjlin/libsvm"}
{p_end}

{marker libsvmguide}{...}
{phang}
Chih-Wei Hsu, Chih-Chung Chang, and Chih-Jen Lin.
{it:A Practical Guide to Support Vector Classification.}
April 15, 2010.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf"}
{p_end}

{marker nusvm}{...}
{phang}
Pai-Hsuen Chen, Chih-Jen Lin, and Bernhard Schölkopf.
{it:A Tutorial on ν-Support Vector Machines}.
{browse "http://www.csie.ntu.edu.tw/~cjlin/papers/nusvmtutorial.pdf"}
{p_end}


remove clutter (was empty)

remove clutter (was empty)


Fix small error with cleanup

Fix small error with cleanup

*! version 1.17.0 15sep2016
*! PARALLEL: Stata module for parallel computing
*! by George G. Vega [cre,aut], Brian Quistorff [ctb]
*!
*! Project website:
*!   https://github.com/gvegayon/parallel
*! Bug reports:
*!   https://github.com/gvegayon/parallel/issues
/*
Copyright (c) 2014  <George G. Vega>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// Syntax parser
program def parallel
    version 11.0

	// Checks wether if is parallel prefix or not
	if  (regexm(`"`0'"', "^(do|clean|setclusters|break|version|append|printlog|viewlog)")) {
	/* If not prefix */
		parallel_`0'
	}
	else if (regexm(`"`0'"', "^(bs|sim)[,]?[\s ]?")) {
	/* Prefix bootstrap or simulate */
		local cmd = regexs(1)
		local 0   = regexr(`"`0'"', "^(bs|sim)", "")
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_`cmd' `0', argopt(`argopt') `options'
	}
	else if (regexm(`"`0'"',"^([,]?.*[:])")) {
	/* if prefix */
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		// Gets the options (if these exists) of parallel
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_do `0', `options' prefix argopt(`argopt')
	}
	else {
		local val = regexm(`"`0'"', "^([a-zA-Z0-9_]*).*")
		local subcommand = regexs(1)
		di as result `"-`subcommand'-"' as error " invalid parallel subcommand" as text " ({stata h parallel:{it:help parallel}})"
		exit 198
	}
end

/* Returns the version of parallel */
program def parallel_version, rclass
	version 11.0
	di as result "parallel" as text " Stata module for parallel computing"
	di as result "vers" as text " 1.17.0 15sep2016"
	di as result "auth" as text " George G. Vega [cre,aut], Brian Quistorff [ctb]"

	return local pll_vers = "1.17.0"
end

/* Take a look to logfiles */
program def parallel_printlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(print)
end

program def parallel_viewlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(view)
end

program def parallel_checklog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)] action(string)

	if ("`event'"=="") local event = "$LAST_PLL_ID"

	if ("`event'"!=".") {
		/* By default uses 1 */
		if ("`pll_instance'" == "") local pll_instance 1

		local pll_instance : di %04.0f `pll_instance'

		/* Does de file exists */
		local logname = "__pll`event'_do`pll_instance'.log"

		if (c(os) == "Windows") local logname = "`c(tmpdir)'`logname'"
		else local logname = "`c(tmpdir)'/`logname'"

		/* If the logfile does not exists, do nothing*/
		cap confirm file "`logname'"
		if (_rc) {
			di as result "No logfile for instance -`pll_instance'- of parallel process -`event'- found"
			exit
		}

		/* Showing the log in screen */
		if ("`action'"=="print") {
			di as result "{hline 80}"
			di as result %~80s "beginning of file -`logname'-"
			di as result "{hline 80}"
			type `"`logname'"'
			di as result "{hline 80}"
			di as result %~80s "end of file -`logname'-"
			di as result "{hline 80}"
		}
		else view `"`logname'"'
	}
	else {
		di as error "It seems that you haven't use -parallel- yet."
		exit 601
	}

end

////////////////////////////////////////////////////////////////////////////////
// Splits the dataset into clusters
program def parallel_spliter
	version 11.0
	syntax [namelist(name=xtstructure)] [,parallelid(string) sorting(integer 0) force(integer 0) keepusing(varlist) orig_cl_local(string)]
	//args xtstructure parallelid Sorting Force

	if length("$PLL_CLUSTERS") == 0 {
		di as err "Number of clusters not fixed." as text " Set the number of clusters with " _newline as err "-{cmd:parallel setclusters #}-"
		exit 198
	}

	//quietly {
		if (length("`xtstructure'")) {
			_assert `sorting' | `force', msg("Data not sorted") rc(5)

			/* Checking the data types */
			foreach var of varlist `xtstructure' {
				capture confirm string var `var'
				if (`=_rc') local numvars `numvars' `var'
				else local strvars `strvars' `var'
			}

			/* calculate max possible clusters */
			egen _`parallelid'grp = group(`xtstructure'), missing
			summ _`parallelid'grp, meanonly
			local max_n_cl = `r(max)'
		}
		else {
			local max_n_cl = _N
		}

		/* Do we have too many clusters? */
		if (`max_n_cl'<$PLL_CLUSTERS){
			c_local `orig_cl_local' ${PLL_CLUSTERS}
			global PLL_CLUSTERS = `max_n_cl'
			di "Small workload/num groups. Temporarily setting number of clusters to ${PLL_CLUSTERS}"
		}

		if (length("`xtstructure'")) {
			preserve
			keep _`parallelid'grp
			contract _`parallelid'grp
			sort _freq
			//Figuring out the mapping from groupID to cut that equalizes sizes is the "Partition Problem"
			//which is hard to solve exactly (it's NP-complete). A rough solution suffices here though.
			gen _`parallelid'cut = mod(_n, ${PLL_CLUSTERS}) + 1
			tempfile grp_to_cut_map
			qui save `grp_to_cut_map'
			restore

			qui merge m:1 _`parallelid'grp using `grp_to_cut_map', keepusing(_`parallelid'cut) nogenerate
			drop _`parallelid'grp
			sort `xtstructure'
		}
		else {
			gen _`parallelid'cut = ceil(_n*${PLL_CLUSTERS}/_N) //each of size _N/$PLL_CLUSTERS
		}

		if (length("`keepusing'")) {
			keep _`parallelid'cut `keepusing'
			gen __pllnobs`parallelid' = _n
		}

		qui save __pll`parallelid'_dataset, replace
		drop _all
	//}

end

////////////////////////////////////////////////////////////////////////////////
// MAIN PROGRAM
// There are things we need to possibly restore on exit
// - Data/S_FN (this is taken care of by -preserve-)
// - PWD (non-indented capture block)
// - Temporaliy changed number of clusters (non-indented capture block)
// - Turn off the timer (do within catch for capture blocks)
program def parallel_do, rclass
	version 11.0

	#delimit ;
	syntax anything(name=dofile equalok everything)
		[, by(string)
		Keep
		KEEPLast
		prefix
		Force
		PROGrams(string)
		Mata
		NOGlobals
		KEEPTiming
		Seeds(string)
		NOData
		Randtype(string)
		Timeout(integer 60)
		PROCessors(integer 0)
		argopt(string)
		KEEPUsing(string)
		SETparallelid(string)
		];
	#delimit cr

	if length("$PLL_CLUSTERS") == 0 {
		di "{error:You haven't set the number of clusters}" _n "{error:Please set it with: {cmd:parallel setclusters} {it:#}}"
		exit 198
	}

	if `c(noisily)'==0 & "`programs'"!="" {
		di "{error:If you want to pass programs in memory (using the option programs)}" _n "{error:then the program must be run noisily.}"
		exit 198
	}

	// Initial checks
	foreach opt in macrolist keep keeplast prefix force mata noglobals keeptiming nodata {
		local `opt' = length("``opt''") > 0
	}

	/* Randtype */
	if ("`randtype'" == "") local randtype = "datetime"

	/* If no data parsing has to be done (because of no data!) */
	if (!(c(N)*c(k))) local nodata 1

	if (!`keeptiming') {
		timer clear 97
		timer clear 98
		timer clear 99
	}

	timer on 98

	// Deletes last parallel instance ran
	local last_id = cond("`r(pll_id)'"!="","`r(pll_id)'", "$LAST_PLL_ID")
	if (`keeplast' & length("`last_id'")) cap parallel_clean, e(`last_id') nologs

	if length("`by'") != 0 {
		local sortlist: sortedby
		local sorting = regexm("`sortlist'","^`by'")
	}
	else local sorting = 0

	// Gets the directory where to work at
	if (!`prefix') {

		/* First checks if the file exists */
		mata: parallel_normalizepath(`"`dofile'"',1)
		local pll_dir = "`filedir'"
		local dofile = "`filename'"
	}
	else local pll_dir = c(pwd)+"/"

	local initialdir = c(pwd)
	qui cd "`pll_dir'"
	capture noisily { //Start capture block for PWD & $PLL_CLUSTERS

	/* Creates a unique ID for the process and secures it */
	if ("`setparallelid'"=="") mata: parallel_sandbox(5)
	else local parallelid = "`setparallelid'"

	/* Generates database clusters */
	if (!`nodata'){
		local sfn = "$S_FN" // be able to "fake" restore this
		preserve

		parallel_spliter `by' , parallelid(`parallelid') sorting(`sorting') force(`force') keepusing(`keepusing') orig_cl_local(orig_PLL_CLUSTERS)
	}

	global LAST_PLL_ID = "`parallelid'"
	global LAST_PLL_DIR = "`pll_dir'"
	global LAST_PLL_N = $PLL_CLUSTERS

	/* Starts building the files */
	local work_around_no_cwd = 0
	quietly {

		/* Saves mata objects */
		if (`mata') {
			mata: mata mlib create l__pll`parallelid'_mlib, replace
			cap mata: mata mlib add l__pll`parallelid'_mlib *()
			cap which l__pll`parallelid'_mlib.mlib
			if _rc!=0{
				local work_around_no_cwd = 1
				noi di "Note: In order to pass mata objects to the clusters, they are saved to a temporary mlib file in the current directly."
				noi di "      Your ado-path doesn't contain the current directory."
				noi di "      We have added the current directory to the end of the ado-path for the clusters.."
			}
			cap mata: mata matsave __pll`parallelid'_mata.mmat *, replace
			if (`=_rc') local matasave = 0
			else local matasave = 1
		}
		else local matasave = 0
	}

	/* Writing the dofile */
	mata: st_local("errornum", strofreal(parallel_write_do(strtrim(`"`dofile' `argopt'"'), "`parallelid'", $PLL_CLUSTERS, `prefix', `matasave', !`noglobals', "`seeds'", "`randtype'", `nodata', "`pll_dir'", "`programs'", `processors',`work_around_no_cwd')))
	if (`errornum') {
		/* Removes the sandbox file (unprotect the files) */
		mata: parallel_sandbox(2, "`parallelid'")

		error `errornum'
	}

	timer off 98
	cap timer list
	if (r(t98) == .) local pll_t_setu = 0
	else local pll_t_setu = r(t98)

	/* Running the dofiles */
	timer on 99
	mata: st_local("nerrors", strofreal(parallel_run("`parallelid'",$PLL_CLUSTERS,`"$PLL_STATA_PATH"',`=`timeout'*1000')))
	timer off 99

	cap timer list
	if (r(t99) == .) local pll_t_calc = 0
	else local pll_t_calc = r(t99)
	local pll_t_reps = r(nt99)

	timer on 97
	// Paste the databases
	if (!`nodata' & !`nerrors') {
		parallel_fusion `parallelid', clusters($PLL_CLUSTERS) keepusing(`keepusing')
		cap drop _`parallelid'cut

		global S_FN = "`sfn'" // Restores original S_FN (file name) value
		restore, not
	}

	return scalar pll_n = $PLL_CLUSTERS

	/* Removes the sandbox file (unprotect the files) */
	if ("`setparallelid'" == "") {
		mata: parallel_sandbox(2, "`parallelid'")
		if (!`keep' & !`keeplast') parallel_clean, e("`parallelid'") nologs
	}

	} //End capture block
	if _rc {
		local orig_rc = _rc
		qui cd "`initialdir'"
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		cap timer off 97
		cap timer off 98
		cap timer off 99
		exit `orig_rc'
	}
	qui cd "`initialdir'"
	if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'

	timer off 97
	cap timer list
	if (r(t97) == .) local pll_t_fini = 0
	else local pll_t_fini = r(t97)

	qui timer list
	return local  pll_seeds="`pllseeds'"
	return scalar pll_errs = `nerrors'
	return local  pll_dir "`pll_dir'"
	return scalar pll_t_reps = `pll_t_reps'
	return scalar pll_t_setu = `pll_t_setu'
	return scalar pll_t_calc = `pll_t_calc'
	return scalar pll_t_fini = `pll_t_fini'
	return local pll_id = "`parallelid'"

	if `nerrors' {
		di as err "`nerrors' child processes encountered errors. Throwing last error."
		exit `pll_last_error'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Cleans all files generated by parallel
// If you want to keep the logs, then specify -, nologs-
program def parallel_clean
	version 11.0
	syntax [, Event(string) All Force nologs]

	if (length("`event'") != 0 & length("`all'") != 0) {
		di as error `"invalid syntax: Using -pll_id- and -all- jointly is not allowed."'
		exit 198
	}
	local do_logs = ("`logs'"!="nologs")
	mata: parallel_clean("`event'", `=length("`all'") > 0', `=length("`force'") > 0', `do_logs')
end

////////////////////////////////////////////////////////////////////////////////
// Sets the number of clusters as a global macro
program parallel_setclusters
	version 11.0
	syntax anything(name=nclusters)  [, Force Statapath(string asis) Gateway(string) Includefile(string) procexec(int 2)]

	local nclusters = int(real(`"`nclusters'"'))
	_assert `nclusters'!=., msg(`"Not allowed: "#" Should be a number"') rc(109)
	_assert `nclusters'>0,  msg(`"Not allowed: "#" Should be positive"') rc(109)
	_assert inlist(`procexec',0,1,2), msg("procexec() must be 0, 1, or 2") rc(198)

	global USE_PROCEXEC = `procexec'

	local force = (length("`force'")>0)
	mata: parallel_setclusters(`nclusters', `force')
	mata: st_local("error", strofreal(parallel_setstatapath(`"`statapath'"', `force')))
	_assert (!`error'), msg("Can not set Stata directory, try using -statapath()- option") rc(`error')

	if "`c(mode)'"=="batch" & "`c(os)'"=="Windows" & "$USE_PROCEXEC"=="0" {
		if `"`gateway'"'=="" local gateway "pll_gateway.sh"

		cap confirm file `"`gateway'"'
		_assert `=!_rc', msg(`"On Windows batch-mode with command-line process execution, requires a gateway file is required but not found (path: `c(pwd)')"') rc(`=_rc')
		global PLL_GATEWAY_FNAME `"`gateway'"'
	}

	if `"`includefile'"'!=""{
		cap confirm file `"`includefile'"'
		_assert `=!_rc', msg("Can not find the include file (`includefile').") msg(`=_rc')
		global PLL_INCLUDE_FILE `"`includefile'"'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Exports

// Exports a copy of programs
program def program_export
	version 11.0
	syntax using/ [,Programlist(string) Inname(string)]

	mata: program_export("`using'", "`programlist'", "`inname'")
end

////////////////////////////////////////////////////////////////////////////////
// Appends the clusterized dataset
program def parallel_fusion
	version 11.0
	syntax anything(name=parallelid) , clusters(integer) [keepusing(string)]

	cap use "__pll`parallelid'_dta0001.dta", clear
	if (_rc){
		di "{error:No dataset for instance 0001.}"
		exit _rc
	}
	local sortlist: sortedby

	forval i = 2/`clusters' {
		cap append using `"__pll`parallelid'_dta`=string(`i',"%04.0f")'.dta"'
		if (_rc){
			di "{error:No dataset for instance `=string(`i',"%04.0f")'.}"
			exit _rc
		}
	}

	/* If it just used a set of variables */
	if (length("`keepusing'")) {
		qui merge 1:1 __pllnobs`parallelid' using __pll`parallelid'_dataset, keep(3) nogen
		drop __pllnobs`parallelid'
	}

	//restore the sort
	if "`sortlist'"!="" sort `sortlist'

end

////////////////////////////////////////////////////////////////////////////////
// Checks whether the user pressed break inside a loop
program def parallel_break
	version 11.0
	mata: parallel_break()
end

*! version 1.17.0 15sep2016
*! PARALLEL: Stata module for parallel computing
*! by George G. Vega [cre,aut], Brian Quistorff [ctb]
*!
*! Project website:
*!   https://github.com/gvegayon/parallel
*! Bug reports:
*!   https://github.com/gvegayon/parallel/issues
/*
Copyright (c) 2014  <George G. Vega>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// Syntax parser
program def parallel
    version 11.0

	// Checks wether if is parallel prefix or not
	if  (regexm(`"`0'"', "^(do|clean|setclusters|break|version|append|printlog|viewlog)")) {
	/* If not prefix */
		parallel_`0'
	}
	else if (regexm(`"`0'"', "^(bs|sim)[,]?[\s ]?")) {
	/* Prefix bootstrap or simulate */
		local cmd = regexs(1)
		local 0   = regexr(`"`0'"', "^(bs|sim)", "")
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_`cmd' `0', argopt(`argopt') `options'
	}
	else if (regexm(`"`0'"',"^([,]?.*[:])")) {
	/* if prefix */
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		// Gets the options (if these exists) of parallel
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_do `0', `options' prefix argopt(`argopt')
	}
	else {
		local val = regexm(`"`0'"', "^([a-zA-Z0-9_]*).*")
		local subcommand = regexs(1)
		di as result `"-`subcommand'-"' as error " invalid parallel subcommand" as text " ({stata h parallel:{it:help parallel}})"
		exit 198
	}
end

/* Returns the version of parallel */
program def parallel_version, rclass
	version 11.0
	di as result "parallel" as text " Stata module for parallel computing"
	di as result "vers" as text " 1.17.0 15sep2016"
	di as result "auth" as text " George G. Vega [cre,aut], Brian Quistorff [ctb]"

	return local pll_vers = "1.17.0"
end

/* Take a look to logfiles */
program def parallel_printlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(print)
end

program def parallel_viewlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(view)
end

program def parallel_checklog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)] action(string)

	if ("`event'"=="") local event = "$LAST_PLL_ID"

	if ("`event'"!=".") {
		/* By default uses 1 */
		if ("`pll_instance'" == "") local pll_instance 1

		local pll_instance : di %04.0f `pll_instance'

		/* Does de file exists */
		local logname = "__pll`event'_do`pll_instance'.log"

		if (c(os) == "Windows") local logname = "`c(tmpdir)'`logname'"
		else local logname = "`c(tmpdir)'/`logname'"

		/* If the logfile does not exists, do nothing*/
		cap confirm file "`logname'"
		if (_rc) {
			di as result "No logfile for instance -`pll_instance'- of parallel process -`event'- found"
			exit
		}

		/* Showing the log in screen */
		if ("`action'"=="print") {
			di as result "{hline 80}"
			di as result %~80s "beginning of file -`logname'-"
			di as result "{hline 80}"
			type `"`logname'"'
			di as result "{hline 80}"
			di as result %~80s "end of file -`logname'-"
			di as result "{hline 80}"
		}
		else view `"`logname'"'
	}
	else {
		di as error "It seems that you haven't use -parallel- yet."
		exit 601
	}

end

////////////////////////////////////////////////////////////////////////////////
// Splits the dataset into clusters
program def parallel_spliter
	version 11.0
	syntax [namelist(name=xtstructure)] [,parallelid(string) sorting(integer 0) force(integer 0) keepusing(varlist) orig_cl_local(string)]
	//args xtstructure parallelid Sorting Force

	if length("$PLL_CLUSTERS") == 0 {
		di as err "Number of clusters not fixed." as text " Set the number of clusters with " _newline as err "-{cmd:parallel setclusters #}-"
		exit 198
	}

	//quietly {
		if (length("`xtstructure'")) {
			_assert `sorting' | `force', msg("Data not sorted") rc(5)

			/* Checking the data types */
			foreach var of varlist `xtstructure' {
				capture confirm string var `var'
				if (`=_rc') local numvars `numvars' `var'
				else local strvars `strvars' `var'
			}

			/* calculate max possible clusters */
			egen _`parallelid'grp = group(`xtstructure'), missing
			summ _`parallelid'grp, meanonly
			local max_n_cl = `r(max)'
		}
		else {
			local max_n_cl = _N
		}

		/* Do we have too many clusters? */
		if (`max_n_cl'<$PLL_CLUSTERS){
			c_local `orig_cl_local' ${PLL_CLUSTERS}
			global PLL_CLUSTERS = `max_n_cl'
			di "Small workload/num groups. Temporarily setting number of clusters to ${PLL_CLUSTERS}"
		}

		if (length("`xtstructure'")) {
			preserve
			keep _`parallelid'grp
			contract _`parallelid'grp
			sort _freq
			//Figuring out the mapping from groupID to cut that equalizes sizes is the "Partition Problem"
			//which is hard to solve exactly (it's NP-complete). A rough solution suffices here though.
			gen _`parallelid'cut = mod(_n, ${PLL_CLUSTERS}) + 1
			tempfile grp_to_cut_map
			qui save `grp_to_cut_map'
			restore

			qui merge m:1 _`parallelid'grp using `grp_to_cut_map', keepusing(_`parallelid'cut) nogenerate
			drop _`parallelid'grp
			sort `xtstructure'
		}
		else {
			gen _`parallelid'cut = ceil(_n*${PLL_CLUSTERS}/_N) //each of size _N/$PLL_CLUSTERS
		}

		if (length("`keepusing'")) {
			keep _`parallelid'cut `keepusing'
			gen __pllnobs`parallelid' = _n
		}

		qui save __pll`parallelid'_dataset, replace
		drop _all
	//}

end

////////////////////////////////////////////////////////////////////////////////
// MAIN PROGRAM
// There are things we need to possibly restore on exit
// - Data/S_FN (this is taken care of by -preserve-)
// - PWD (non-indented capture block)
// - Temporaliy changed number of clusters (non-indented capture block)
// - Turn off the timer (do within catch for capture blocks)
program def parallel_do, rclass
	version 11.0

	#delimit ;
	syntax anything(name=dofile equalok everything)
		[, by(string)
		Keep
		KEEPLast
		prefix
		Force
		PROGrams(string)
		Mata
		NOGlobals
		KEEPTiming
		Seeds(string)
		NOData
		Randtype(string)
		Timeout(integer 60)
		PROCessors(integer 0)
		argopt(string)
		KEEPUsing(string)
		SETparallelid(string)
		];
	#delimit cr

	if length("$PLL_CLUSTERS") == 0 {
		di "{error:You haven't set the number of clusters}" _n "{error:Please set it with: {cmd:parallel setclusters} {it:#}}"
		exit 198
	}

	if `c(noisily)'==0 & "`programs'"!="" {
		di "{error:If you want to pass programs in memory (using the option programs)}" _n "{error:then the program must be run noisily.}"
		exit 198
	}

	// Initial checks
	foreach opt in macrolist keep keeplast prefix force mata noglobals keeptiming nodata {
		local `opt' = length("``opt''") > 0
	}

	/* Randtype */
	if ("`randtype'" == "") local randtype = "datetime"

	/* If no data parsing has to be done (because of no data!) */
	if (!(c(N)*c(k))) local nodata 1

	if (!`keeptiming') {
		timer clear 97
		timer clear 98
		timer clear 99
	}

	timer on 98

	// Deletes last parallel instance ran
	if (`keeplast' & length("`r(pll_id)'")) cap parallel_clean, e(`r(pll_id)') nologs

	if length("`by'") != 0 {
		local sortlist: sortedby
		local sorting = regexm("`sortlist'","^`by'")
	}
	else local sorting = 0

	// Gets the directory where to work at
	if (!`prefix') {

		/* First checks if the file exists */
		mata: parallel_normalizepath(`"`dofile'"',1)
		local pll_dir = "`filedir'"
		local dofile = "`filename'"
	}
	else local pll_dir = c(pwd)+"/"

	local initialdir = c(pwd)
	qui cd "`pll_dir'"
	capture noisily { //Start capture block for PWD & $PLL_CLUSTERS

	/* Creates a unique ID for the process and secures it */
	if ("`setparallelid'"=="") mata: parallel_sandbox(5)
	else local parallelid = "`setparallelid'"

	/* Generates database clusters */
	if (!`nodata'){
		local sfn = "$S_FN" // be able to "fake" restore this
		preserve

		parallel_spliter `by' , parallelid(`parallelid') sorting(`sorting') force(`force') keepusing(`keepusing') orig_cl_local(orig_PLL_CLUSTERS)
	}

	global LAST_PLL_ID = "`parallelid'"
	global LAST_PLL_DIR = "`pll_dir'"
	global LAST_PLL_N = $PLL_CLUSTERS

	/* Starts building the files */
	local work_around_no_cwd = 0
	quietly {

		/* Saves mata objects */
		if (`mata') {
			mata: mata mlib create l__pll`parallelid'_mlib, replace
			cap mata: mata mlib add l__pll`parallelid'_mlib *()
			cap which l__pll`parallelid'_mlib.mlib
			if _rc!=0{
				local work_around_no_cwd = 1
				noi di "Note: In order to pass mata objects to the clusters, they are saved to a temporary mlib file in the current directly."
				noi di "      Your ado-path doesn't contain the current directory."
				noi di "      We have added the current directory to the end of the ado-path for the clusters.."
			}
			cap mata: mata matsave __pll`parallelid'_mata.mmat *, replace
			if (`=_rc') local matasave = 0
			else local matasave = 1
		}
		else local matasave = 0
	}

	/* Writing the dofile */
	mata: st_local("errornum", strofreal(parallel_write_do(strtrim(`"`dofile' `argopt'"'), "`parallelid'", $PLL_CLUSTERS, `prefix', `matasave', !`noglobals', "`seeds'", "`randtype'", `nodata', "`pll_dir'", "`programs'", `processors',`work_around_no_cwd')))
	if (`errornum') {
		/* Removes the sandbox file (unprotect the files) */
		mata: parallel_sandbox(2, "`parallelid'")

		error `errornum'
	}

	timer off 98
	cap timer list
	if (r(t98) == .) local pll_t_setu = 0
	else local pll_t_setu = r(t98)

	/* Running the dofiles */
	timer on 99
	mata: st_local("nerrors", strofreal(parallel_run("`parallelid'",$PLL_CLUSTERS,`"$PLL_STATA_PATH"',`=`timeout'*1000')))
	timer off 99

	cap timer list
	if (r(t99) == .) local pll_t_calc = 0
	else local pll_t_calc = r(t99)
	local pll_t_reps = r(nt99)

	timer on 97
	// Paste the databases
	if (!`nodata' & !`nerrors') {
		parallel_fusion `parallelid', clusters($PLL_CLUSTERS) keepusing(`keepusing')
		cap drop _`parallelid'cut

		global S_FN = "`sfn'" // Restores original S_FN (file name) value
		restore, not
	}

	return scalar pll_n = $PLL_CLUSTERS

	/* Removes the sandbox file (unprotect the files) */
	if ("`setparallelid'" == "") {
		mata: parallel_sandbox(2, "`parallelid'")
		if (!`keep' & !`keeplast') parallel_clean, e("`parallelid'") nologs
	}

	} //End capture block
	if _rc {
		local orig_rc = _rc
		qui cd "`initialdir'"
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		cap timer off 97
		cap timer off 98
		cap timer off 99
		exit `orig_rc'
	}
	qui cd "`initialdir'"
	if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'

	timer off 97
	cap timer list
	if (r(t97) == .) local pll_t_fini = 0
	else local pll_t_fini = r(t97)

	qui timer list
	return local  pll_seeds="`pllseeds'"
	return scalar pll_errs = `nerrors'
	return local  pll_dir "`pll_dir'"
	return scalar pll_t_reps = `pll_t_reps'
	return scalar pll_t_setu = `pll_t_setu'
	return scalar pll_t_calc = `pll_t_calc'
	return scalar pll_t_fini = `pll_t_fini'
	return local pll_id = "`parallelid'"

	if `nerrors' {
		di as err "`nerrors' child processes encountered errors. Throwing last error."
		exit `pll_last_error'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Cleans all files generated by parallel
// If you want to keep the logs, then specify -, nologs-
program def parallel_clean
	version 11.0
	syntax [, Event(string) All Force nologs]

	if (length("`event'") != 0 & length("`all'") != 0) {
		di as error `"invalid syntax: Using -pll_id- and -all- jointly is not allowed."'
		exit 198
	}
	local do_logs = ("`logs'"!="nologs")
	mata: parallel_clean("`event'", `=length("`all'") > 0', `=length("`force'") > 0', `do_logs')
end

////////////////////////////////////////////////////////////////////////////////
// Sets the number of clusters as a global macro
program parallel_setclusters
	version 11.0
	syntax anything(name=nclusters)  [, Force Statapath(string asis) Gateway(string) Includefile(string) procexec(int 2)]

	local nclusters = int(real(`"`nclusters'"'))
	_assert `nclusters'!=., msg(`"Not allowed: "#" Should be a number"') rc(109)
	_assert `nclusters'>0,  msg(`"Not allowed: "#" Should be positive"') rc(109)
	_assert inlist(`procexec',0,1,2), msg("procexec() must be 0, 1, or 2") rc(198)

	global USE_PROCEXEC = `procexec'

	local force = (length("`force'")>0)
	mata: parallel_setclusters(`nclusters', `force')
	mata: st_local("error", strofreal(parallel_setstatapath(`"`statapath'"', `force')))
	_assert (!`error'), msg("Can not set Stata directory, try using -statapath()- option") rc(`error')

	if "`c(mode)'"=="batch" & "`c(os)'"=="Windows" & "$USE_PROCEXEC"=="0" {
		if `"`gateway'"'=="" local gateway "pll_gateway.sh"

		cap confirm file `"`gateway'"'
		_assert `=!_rc', msg(`"On Windows batch-mode with command-line process execution, requires a gateway file is required but not found (path: `c(pwd)')"') rc(`=_rc')
		global PLL_GATEWAY_FNAME `"`gateway'"'
	}

	if `"`includefile'"'!=""{
		cap confirm file `"`includefile'"'
		_assert `=!_rc', msg("Can not find the include file (`includefile').") msg(`=_rc')
		global PLL_INCLUDE_FILE `"`includefile'"'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Exports

// Exports a copy of programs
program def program_export
	version 11.0
	syntax using/ [,Programlist(string) Inname(string)]

	mata: program_export("`using'", "`programlist'", "`inname'")
end

////////////////////////////////////////////////////////////////////////////////
// Appends the clusterized dataset
program def parallel_fusion
	version 11.0
	syntax anything(name=parallelid) , clusters(integer) [keepusing(string)]

	cap use "__pll`parallelid'_dta0001.dta", clear
	if (_rc){
		di "{error:No dataset for instance 0001.}"
		exit _rc
	}
	local sortlist: sortedby

	forval i = 2/`clusters' {
		cap append using `"__pll`parallelid'_dta`=string(`i',"%04.0f")'.dta"'
		if (_rc){
			di "{error:No dataset for instance `=string(`i',"%04.0f")'.}"
			exit _rc
		}
	}

	/* If it just used a set of variables */
	if (length("`keepusing'")) {
		qui merge 1:1 __pllnobs`parallelid' using __pll`parallelid'_dataset, keep(3) nogen
		drop __pllnobs`parallelid'
	}

	//restore the sort
	if "`sortlist'"!="" sort `sortlist'

end

////////////////////////////////////////////////////////////////////////////////
// Checks whether the user pressed break inside a loop
program def parallel_break
	version 11.0
	mata: parallel_break()
end


ieddtable, make it a command

ieddtable, make it a command

cap program drop 	ieddtable
	program define	ieddtable

	syntax varlist, t(varname numeric) tmt(varname numeric) [COVARiates(varlist numeric)]

	/* the rest will be inside the command */

	local colnames 2ndDiff 2ndDiff_err 2ndDiff_N B1stDiff B1stDiff_err B1stDiff_N E1stDiff E1stDiff_err E1stDiff_N BC_Mean BC_err BC_N BT_Mean BT_err BT_N EC_Mean EC_err EC_N ET_Mean ET_err ET_N

	*Define default row here. The results for each var will be one row that starts with all missing vlaues
	mat startRow = (.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.)
	mat colnames startRow = `colnames'

	noi di "Start row to see headers, remove for production"
	matlist startRow // See the default row with its column names

	*Initiate the result matrix with a place holder row that will not be used for anything. Matrices cannot be initiated empty
	mat resultMat = startRow
	mat rownames resultMat = placeholder

	*Loop over all varlist
	foreach var of local varlist {

		*Each variable is a row in the result matrix
		mat `var' = startRow
		mat rownames `var' = `var'
		mat colnames `var' = `colnames'

		*Local that keeps track of which column to fill
		local colindex 0

		tempvar regsample

		/*************

			Calculating 2nd difference and restict the sample
			for this output var

		*************/

		*Run the regression to get the double difference
		qui reg `var' `tmt'#`t' `covariates'
		mat resTable = r(table)

		**This is why this is done first. All other calculations
		* for this outcome var should be restricted to this sample.
		gen `regsample' = e(sample)
		local N `e(N)'

		**Test that the dummy vars creates for valid groups for each
		* combination of time and treat in the sample used for this outcome var
		testDDdums `t' `tmt' `regsample' `var'  //comment in when this is made into a command

		//Get the second differnce
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,1,4)

		*Get the standard error of second difference
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,2,4)

		*Get the N of second difference regression
		local ++colindex
		mat `var'[1,`colindex'] = `N'

		/*************

			Calculating 1st differences

		*************/

		forvalues tmt01 = 0/1 {

			qui reg `var' `t' `covariates' if `tmt' == `tmt01' & `regsample' == 1
			mat resTable = r(table)

			//Get the 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,1,1)

			*Get the standard error of 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,2,1)

			*Get the N of first difference regression
			local ++colindex
			mat `var'[1,`colindex'] =  `e(N)'

		}

		/*************

			Calculating standard means for all groups

		*************/
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				qui mean `var' if `tmt' == `t01' & `t' == `tmt01' & `regsample' == 1

				mat resTable = r(table)

				//Get the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,1,1)

				*Get the standard error of the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,2,1)

				*Get the N of the ordinary means regressions
				local ++colindex
				mat `var'[1,`colindex'] =  `e(N)'
			}
		}

		*Append this row to the result table
		mat 	resultMat = (resultMat \ `var')

	}

	*Remove placeholder row
	matrix resultMat = resultMat[2..., 1...]

	*Show the final matrix will all data needed to start building the output
	noi di "Matlist with results"
	matlist resultMat

end

/***************************************
****************************************

	Write sub-commands for outputs

****************************************
***************************************/

	******
	*Then the result matrix can be passed into subcommands that output in either Excel, LaTeX or in the main window.

	*The results can be accessed like this, which makes the sub-commands less sensitive to changing column order in the section above.
	//mat A = resultMat[3, "BC_Mean"] // returns a 1x1 matrix
	//local a = el(A,1,1)				// returns the value

	//matlist A
	//di `a'

/***************************************
****************************************

	Write sub-commands for input testing

****************************************
***************************************/

cap program drop 	testDDdums
	program define	testDDdums

		args time treat samplevar outputvar

		**Test that for only two of three dummies there are observations
		* that has only that dummy. I.e. the two that is not the
		* interaction. If the interaction is 1, all three shluld be 1.

		*Test that there is some observations in each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				count if `treat' == `t01' & `time' == `tmt01' & `samplevar' == 1

				if `r(N)' < 2 {
					noi di as error "{phang}In the difference in differnce regression for outputvar `outputvar' there were not enough observations in each combination of treatment and time. The tab below is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
					noi tab `treat' `time' if `samplevar' == 1, missing
					error 480

				}
			}
		}

	end

	/* Create some dummy datat */

	clear

	*set seed 12345
	set obs 10000

	gen tmt = (runiform()<.5)
	gen t	= (runiform()<.5)

	gen var_a = runiform()
	gen var_b = runiform()
	gen var_c = runiform()
	gen var_d = runiform()

	gen cov_a = runiform()
	gen cov_b = runiform()

	local varlist 		var_a var_b var_c var_d
	local covariates 	cov_a cov_b

	/* the rest will be inside the command */

	local colnames 2ndDiff 2ndDiff_err 2ndDiff_N B1stDiff B1stDiff_err B1stDiff_N E1stDiff E1stDiff_err E1stDiff_N BC_Mean BC_err BC_N BT_Mean BT_err BT_N EC_Mean EC_err EC_N ET_Mean ET_err ET_N

	*Define default row here. The results for each var will be one row that starts with all missing vlaues
	mat startRow = (.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.)
	mat colnames startRow = `colnames'

	matlist startRow // See the default row with its column names

	*Initiate the result matrix with a place holder row that will not be used for anything. Matrices cannot be initiated empty
	mat resultMat = startRow
	mat rownames resultMat = placeholder

	*Loop over all varlist
	foreach var of local varlist {

		*Each variable is a row in the result matrix
		mat `var' = startRow
		mat rownames `var' = `var'
		mat colnames `var' = `colnames'

		*Local that keeps track of which column to fill
		local colindex 0

		tempvar regsample

		/*************

			Calculating 2nd difference and restict the sample
			for this output var

		*************/

		*Run the regression to get the double difference
		qui reg `var' tmt#t `covariates'
		mat resTable = r(table)

		**This is why this is done first. All other calculations
		* for this outcome var should be restricted to this sample.
		gen `regsample' = e(sample)
		local N `e(N)'

		**Test that the dummy vars creates for valid groups for each
		* combination of time and treat in the sample used for this outcome var
		//testDDdums t tmt `regsample' `var'  //comment in when this is made into a command

		//Get the second differnce
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,1,4)

		*Get the standard error of second difference
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,2,4)

		*Get the N of second difference regression
		local ++colindex
		mat `var'[1,`colindex'] = `N'

		/*************

			Calculating 1st differences

		*************/

		forvalues tmt01 = 0/1 {

			qui reg `var' t `covariates' if tmt == `tmt01' & `regsample' == 1
			mat resTable = r(table)

			//Get the 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,1,1)

			*Get the standard error of 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,2,1)

			*Get the N of first difference regression
			local ++colindex
			mat `var'[1,`colindex'] =  `e(N)'

		}

		*Get the means for each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				qui mean `var' if tmt == `t01' & t == `tmt01' & `regsample' == 1

				mat resTable = r(table)

				//Get the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,1,1)

				*Get the standard error of the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,2,1)

				*Get the N of the ordinary means regressions
				local ++colindex
				mat `var'[1,`colindex'] =  `e(N)'
			}
		}

		*Append this row to the result table
		mat 	resultMat = (resultMat \ `var')

	}

	*Remove placeholder row
	matrix resultMat = resultMat[2..., 1...]

	*Show the final matrix will all data needed to start building the output
	matlist resultMat

/***************************************
****************************************

	Write sub-commands for outputs

****************************************
***************************************/

	******
	*Then the result matrix can be passed into subcommands that output in either Excel, LaTeX or in the main window.

	*The results can be accessed like this, which makes the sub-commands less sensitive to changing column order in the section above.
	mat A = resultMat[3, "BC_Mean"] // returns a 1x1 matrix
	local a = el(A,1,1)				// returns the value

	matlist A
	di `a'

/***************************************
****************************************

	Write sub-commands for input testing

****************************************
***************************************/

cap program drop 	testDDdums
	program define	testDDdums

		args time treat samplevar outputvar

		**Test that for only two of three dummies there are observations
		* that has only that dummy. I.e. the two that is not the
		* interaction. If the interaction is 1, all three shluld be 1.

		*Test that there is some observations in each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				count if `treat' == `t01' & `time' == `tmt01' & `samplevar' == 1

				if `r(N)' < 2 {
					noi di as error "{phang}In the difference in differnce regression for outputvar `outputvar' there were not enough observations in each combination of treatment and time. The tab below is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
					noi tab `treat' `time' if `samplevar' == 1, missing
					error 480

				}
			}
		}

	end


pull in ../bin as a subtree

pull in ../bin as a subtree

git subtree add --prefix bin ../bin master

= to.do: =

o Some dotfiles, e.g. .gitconfig, really need to be modified by a script rather than
  simply symlinked.  That's because things like username and email address change from one
  system to another.  So use a file full of, e.g., git config --global ...  An alternative
  would be to include them.

o regular dotfiles should start with "_" rather than ".", to make them visible.

o bloggish directories should have a "make wc" target that subtracts the word count of the
  template file.  Or, better, use sed to filter out tags.

========================================================================================>
Work Log:
========

1111We
  @ OpenSSH/Cookbook/Multiplexing - Wikibooks
    Makes ssh much faster, and circumvents dreamhost's connection limits.
    > mkdir ~/.ssh/controlmasters; chmod 700 ~/.ssh/controlmasters
  * Add the following to .ssh/config, in the global section at the top:
      ControlPath ~/.ssh/controlmasters/%r@%h:%p
      ControlPersist 10m
      ControlMaster auto
  @ https://github.com/blipvert/ljupdate/commits/master marginally more up to date
    * update emacs/depends.make

2016

0214Su
  * much hacking in gnome-setup and font setup over the last week or so.

0215Mo
  * added adobe-source-code-pro font, tell emacs to use system font.

... Really ought to copy the config-related stuff out of starport/Admin/admin.to.do

0626
  ~ alltray lookeed useful - lets you put an icon for any program into the tray.
    It starts the program if it's not already running, but fails in xmonad because it's
    waiting for the program to be iconified.
  * gsimplecal, fbpanel, and probably others have subdirectories in .config
    probably best is to make another directory called dotconfig, parallel to dotfiles

0627Mo
  * made the Makefiles independendent of Tools/Makefile.

0704Mo
  * git subtree add --prefix bin ../bin master
  * The idea is that if there's a pre-existing ~/bin, we can symlink into it.

==============================================================================
Local Variables:
    fill-column:90
End:

= to.do: =

o Some dotfiles, e.g. .gitconfig, really need to be modified by a script rather than
  simply symlinked.  That's because things like username and email address change from one
  system to another.  So use a file full of, e.g., git config --global ...  An alternative
  would be to include them.

o regular dotfiles should start with "_" rather than ".", to make them visible.

o bloggish directories should have a "make wc" target that subtracts the word count of the
  template file.  Or, better, use sed to filter out tags.

========================================================================================>
Work Log:
========

1111We
  @ OpenSSH/Cookbook/Multiplexing - Wikibooks
    Makes ssh much faster, and circumvents dreamhost's connection limits.
    > mkdir ~/.ssh/controlmasters; chmod 700 ~/.ssh/controlmasters
  * Add the following to .ssh/config, in the global section at the top:
      ControlPath ~/.ssh/controlmasters/%r@%h:%p
      ControlPersist 10m
      ControlMaster auto
  @ https://github.com/blipvert/ljupdate/commits/master marginally more up to date
    * update emacs/depends.make

2016

0214Su
  * much hacking in gnome-setup and font setup over the last week or so.

0215Mo
  * added adobe-source-code-pro font, tell emacs to use system font.

... Really ought to copy the config-related stuff out of starport/Admin/admin.to.do

0626
  ~ alltray lookeed useful - lets you put an icon for any program into the tray.
    It starts the program if it's not already running, but fails in xmonad because it's
    waiting for the program to be iconified.
  * gsimplecal, fbpanel, and probably others have subdirectories in .config
    probably best is to make another directory called dotconfig, parallel to dotfiles

0627Mo
  * made the Makefiles independendent of Tools/Makefile.

==============================================================================
Local Variables:
    fill-column:90
End: