I use 2 methods to create formula columns depending on whether I know how many variables the formula needs
- Expr and substitute are easy to read. Section 1 in the code creates several formula columns for each variable in a list for mean(var, byvar)
- Parse and eval and possibly eval insert create a single formula column that requires a number of variables, the number and name being set by the variable list.
- Section 2a and 2b work and are functionally equivalent
- Section 2c and 2d do not run, but seem a path to an elegant solution
I would like ideas to create better practices and would appreciate recommendations
Names Default To Here( 1 );
n=ntable();
dtnms={};
If(
n==0, dtc=1,
n==1, If(current data table() << get name == "Big Class", dtc=0, dtc=1) , // else
for(ii=1, ii <= n, ii++, insert into(dtnms, data table(ii) << getname));
if(contains(dtnms, "Big Class") > 0, dtc=1, dtc=0);
);
if (dtc,dt = Open( "$SAMPLE_DATA/Big Class.jmp" ), dt = data table("Big Class"));
// Section 1: Add several columns of formula entering variables dynamically.
Form1=expr(
form1col=dt << new column(Name, formula(col mean(As Column(YVar), As Column(ByVar))))
);
VarList={"sex", "height", "weight"};
For(ii=2, ii <= N Items(VarList), ii++,
Eval(Substitute(Name expr(form1),
expr(Name), VarList[ii]||" Averages",
expr(YVar), VarList[ii],
expr(ByVar), VarList[1],
));
);
// Section 2: Average the columns in the list of variables
// Method 2a Method works but is hard to debug
VarList={"height", "weight", "age"};
Formtxt1 = "formcol = dt << new column(\!"Method a: Mean\!", formula(mean( ";
Formtxt2 = "As Column(\!""||VarList[1]||"\!")";
If(
N Items(VarList)==2, Formtxt2 ||= ", As Column(\!""||VarList[2]||"\!")",
N Items(VarList)>2, For(ii=2, ii <= N Items(VarList), ii++,
Formtxt2 ||= ", As Column(\!""||VarList[ii]||"\!")"
)
);
Formtxt=Formtxt1||Formtxt2||");";
eval(parse(Formtxt));
// Method 2b: Somewhat easier to see
col4=expr(
NewCol1=dt << New Column("Method b: Mean of "||_names_);
);
form4txt="NewCol1 <<formula(Mean( ^_Vars_^ )))";
VarList={"height", "weight", "age"};
VarRef = eval list(dt << get Column Reference(VarList));
_Vars_ = "As Column( \!"" || ConCat Items(VarList, "\!"), As Column( \!"") || "\!")";
form4txta = Eval Insert(form4txt);
Eval(Substitute(Name expr(col4),
expr(_names_), concat Items(VarList,", ",)
));
Eval(Parse(form4txta)); //Parse makes the text an expr which is evaluated
// *************************************** //
// ************* Not working ************* //
//Method 2c: I think this would be the best, but I cannot make it correct - Still have a list
form3=expr(
NewCol=dt << New Column("Mean of "||_names_, formula(Mean(_Vars_ )));
);
VarList={"height", "weight", "age"};
VarRef = eval list(dt << get Column Reference(VarList));
Eval(Substitute(Name expr(form3),
expr(_names_), concat Items(VarList,", ",),
expr(_Vars_), VarRef
));
// Method 2d: Using a loop, but still incorrect
form4=expr(
NewCol=dt << New Column("Mean of "||_names_, formula(Mean(_Vars_ )));
);
VarList={"height", "weight", "age"};
VarRef = eval list(dt << get Column Reference(VarList));
Form4a =Substitute(Name expr(form4),
expr(_names_), concat Items(VarList,", ",)
);
For(ii=1, ii <= N Items(VarList), ii++,
form4a=Substitute(Name expr(form4a),
expr(_Vars_), expr(VarRef[ii], _Vars_)
);
);
Eval(Substitute(Name expr(form4a),
expr(_Vars_), ""
));
// *************************************** //
// ************* Not working ************* //