cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Browse apps to extend the software in the new JMP Marketplace
Choose Language Hide Translation Bar
ih
Super User (Alumni) ih
Super User (Alumni)

Can you construct this without writing expression as a string?

I dislike constructing jsl expressions as strings, it makes them hard to troubleshoot and checking for syntax errors is a multistep process. This example solution for @A_Zaid's Plotting conditionally post has me struggling though.  Is it possible to construct this graph builder function without writing the string and parsing it as text? I'm stuck when trying to construct the 'expression' of Y arguments which is an unknown number of arguments and can't be parsed on its own.

 

Maybe there an equivalent to do.call, invoke, or exec operators in R, or the splat operator in Python that I haven't found yet?

 

Names default to here(1);

dt = Open("$Sample_data/probe.jmp");

//Variables to hold the dynamic parts of the graph builder expresssion
Expr1 = "";
Expr2 = "";

// Pick columns to include
cols = ( dt << get column names() )[random shuffle(8::397)[1::5]];

//Populate expressions
for(c=1, c<= n items(cols), c++,
	Expr1 = Expr1 || "Y( Column( \!"" || cols[c] || "\!" ), Position(1) ),";
	Expr2 = Expr2 || "Y( " || char( c ) || " ),";
);

Eval( Parse( substitute(
	//the graph builder script as a string with 'dummy variables' where the
	//expressions created earlier will go
	"gb = Graph Builder(
		Size( 838, 391 ),
		Variables(
			X( :DELL_RPPBR ),
			Expr1_
		),
		Elements( Line( X, Expr2_ Legend( 10 ) ) ),
		Local Data Filter(
			Add Filter(
				columns( :DELL_RPPBR ),
				Where( :DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896 )
			)
		)
	)",
	//replace the 'dummy variables'
	"Expr1_",
	Expr1,
	"Expr2_",
	Expr2
) ) );

 

4 ACCEPTED SOLUTIONS

Accepted Solutions
jthi
Super User

Re: Can you construct this without writing expression a string?

I usually just fall back to using Eval(Parse()) because it feels way easier. Found this Re: Use variable list of columns as Y Axis in Graph Builder where @Mark_Bailey gives solution to something similar.

 

Here is my attempt with this:

Names Default To Here(1);

dt = Open("$Sample_data/probe.jmp");

cols = (dt << get column names())[Random Shuffle(8 :: 397)[1 :: 5]];

var expr = Expr(
	Variables(X(:DELL_RPPBR))
);

var line = Expr(Line(X));

For(c = 1, c <= N Items(cols), c++,
	y expr = Expr(Y(Position(1)));
	Insert Into(y expr, cols[c], 1);
	Insert Into(var expr, Name Expr(y expr));
	
	yline expr = Parse("Y("||char(c)||")");
	Insert Into(var line, Name Expr(yline expr));

);

yline expr = Expr(Legend(10));
Insert Into(var line, Name Expr(yline expr));

Eval(
	Substitute(
			Expr(
				gb = dt << Graph Builder(
					Size(838, 391),
					Show Control Panel(0),
					vvv,
					Elements(yyy),
					Local Data Filter(Conditional, Add Filter(columns(:DELL_RPPBR), 
						Where(:DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896)))
				)
			),
		Expr(vvv), Name Expr(var expr),
		Expr(yyy), Name Expr(var line)
	)
);

I thought that this wouldn't help at all with debugging but it does because you can just run the Substitute() part of graph builder generation to see what it tries to do (and you also get JSL syntax highlighting).

-Jarmo

View solution in original post

Re: Can you construct this without writing expression as a string?

It is also fine to insert--in line--expressions resulting from substitutions, character conversions, etc. As ever, this is a balancing act between ease of interpretation/maintenance and conciseness. In the example below I've done this to illustrate.

 

Expression handling is not the easiest thing to learn (or teach). It is quite powerful though, and as you mention, allows you to keep the color-coded text that makes maintenance easier.

 

Names Default To Here( 1 );

dt = Open("$Sample_data/probe.jmp");

colList = {R_FUSE_16X0_5, VDP_PEMIT, "30P1_4X4_VBE"n, RM_RPB_100X7, "30N4_LE8_2RX1C_BVCBS"n}; //assume we've built this, somehow

gbExpr = Expr(
	gb = Graph Builder(
		Size( 838, 391 ),
		_VAR_EXP_,						// placeholder for Variables ( ) code.
		Elements( _LINE_EXP_ ),			// placeholder for Line ( ) code.
		Local Data Filter( Add Filter( columns( :DELL_RPPBR ), Where( :DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896 ) ) )
	)
);

varExpr = Expr( Variables( X( :DELL_RPPBR ) ) );  	//Loop will insert into the last (default) position

lineExpr = Expr( Line( X, Legend( 1 ) ) );			//Loop will insert into the i+1th position

For( i = 1, i <= N Items( colList ), i++,			//note we're inserting the result of a substitute ( ) in each case
	Insert Into( varExpr, Substitute( Expr( Y( Column( _COL_ ), Position( 1 ) ) ), Expr( _COL_ ), Char( colList[i] ) ) );
	Insert Into( lineExpr, Substitute( Expr( Y( _i_ ) ), Expr( _i_ ), i ), i + 1 );
);

Substitute Into( gbExpr, Expr( _VAR_EXP_ ), Name Expr( varExpr ), Expr( _LINE_EXP_ ), Name Expr( lineExpr ) );

gbExpr; //evaluate the (now completely built) expression to show graph

View solution in original post

Re: Can you construct this without writing expression as a string?

I got this far but I can't work on it any more today. There is an error in the expression using the Name() directive. There is a way around it but I do not have time now.

 

Names Default To Here( 1 );

dt = Open( "$Sample_data/probe.jmp" );

//Variables to hold the dynamic parts of the graph builder expresssion
response expr = Expr(
	Variables( X( :DELL_RPPBR ) )
);

line expr = Expr(
	Line( X, Legend( 10 ) )
);

// Pick columns to include
col = (dt << Get Column Names)[Random Shuffle( 10 :: 397 )[1 :: 5]];

//Populate expressions
For( c = 1, c <= N Items( col ), c++,
	Insert Into( response expr, Substitute( Expr( Y( Name( ccc ), Position( 1 ) ) ), Expr( ccc ), Char( col[c], 1 ) ) );
	Insert Into( line expr, Substitute( Expr( Y( ccc ) ), Expr( ccc ), c ), c+1 );
);

Eval(
	Substitute( 
		Expr(
			gb = dt << Graph Builder(
				Size( 838, 391 ),
				Variables(
					vvv
				),
				Elements( Line( X, lll, Legend( 10 ) ) ),
				Local Data Filter(
					Add Filter(
						Columns( :DELL_RPPBR ),
						Where( -1 < :DELL_RPPBR <= 0.483837991952896 )
					)
				)
			)
		),
		Expr( vvv ), Name Expr( response expr ),
		Expr( lll ), Name Expr( line expr )
	)
);

View solution in original post

Craige_Hales
Super User

Re: Can you construct this without writing expression as a string?

The insert into approach looks really good; I'm going to remember that. I'd been using a { list of expressions } and replacing the {} with the new head, like this:

dt = Open( "$Sample_data/probe.jmp" );
// Pick columns to include
cols = (dt << get column names())[Random Shuffle( 8 :: 397 )[1 :: 5]];

//Variables to hold the dynamic parts of the graph builder expresssion
List1 = {};
List2 = {};

//Populate expressions
For( c = 1, c <= N Items( cols ), c++,
	List1[c] = Eval Expr( Y( Expr( cols[c] ), Position( 1 ) ) );
	List2[c] = Eval Expr( Y( Expr( c ) ) );
);
// list1: {Y( Name( "M1-TRENCH_ISO_IL" ), Position( 1 ) ), Y(...
// list2: {Y( 1 ), Y( 2 ), Y( 3 ), Y( 4 ), Y( 5 )}
Insert Into( List1, Expr( X( :DELL_RPPBR ) ), 1 ); // front of list1
Insert Into( List2, Expr( X ), 1 ); // front of list2
Insert Into( List2, Expr( Legend( 10 ) ) ); // back of list2

// swap the {} for variables() and line()
variablesExpr = Substitute( List1, Expr( {} ), Expr( variables() ) );
lineExpr = Substitute( List2, Expr( {} ), Expr( Line() ) );

exprGB = Expr(
	//the graph builder script as a string with 'dummy variables' where the
	//expressions created earlier will go
	(Graph Builder(
		Size( 838, 391 ),
		Variables_,
		Elements( Line_ ),
		Local Data Filter( Add Filter( columns( :DELL_RPPBR ), Where( :DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896 ) ) )
	))
);

Substitute Into( exprGB, Expr( Variables_ ), Name Expr( variablesExpr ), Expr( Line_ ), Name Expr( lineExpr ) );

Show( Name Expr( exprGB ) );

Eval( exprGB );

 edit...

And InsertInto and RemoveFrom both work with positions in an expression. Cool, I really need to remember that! 

 

Craige

View solution in original post

7 REPLIES 7
txnelson
Super User

Re: Can you construct this without writing expression a string?

Eval(Parse()) has saved me on too many occasions. I do not know any other method for your example script.  Maybe another community member has a solution

Jim
jthi
Super User

Re: Can you construct this without writing expression a string?

I usually just fall back to using Eval(Parse()) because it feels way easier. Found this Re: Use variable list of columns as Y Axis in Graph Builder where @Mark_Bailey gives solution to something similar.

 

Here is my attempt with this:

Names Default To Here(1);

dt = Open("$Sample_data/probe.jmp");

cols = (dt << get column names())[Random Shuffle(8 :: 397)[1 :: 5]];

var expr = Expr(
	Variables(X(:DELL_RPPBR))
);

var line = Expr(Line(X));

For(c = 1, c <= N Items(cols), c++,
	y expr = Expr(Y(Position(1)));
	Insert Into(y expr, cols[c], 1);
	Insert Into(var expr, Name Expr(y expr));
	
	yline expr = Parse("Y("||char(c)||")");
	Insert Into(var line, Name Expr(yline expr));

);

yline expr = Expr(Legend(10));
Insert Into(var line, Name Expr(yline expr));

Eval(
	Substitute(
			Expr(
				gb = dt << Graph Builder(
					Size(838, 391),
					Show Control Panel(0),
					vvv,
					Elements(yyy),
					Local Data Filter(Conditional, Add Filter(columns(:DELL_RPPBR), 
						Where(:DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896)))
				)
			),
		Expr(vvv), Name Expr(var expr),
		Expr(yyy), Name Expr(var line)
	)
);

I thought that this wouldn't help at all with debugging but it does because you can just run the Substitute() part of graph builder generation to see what it tries to do (and you also get JSL syntax highlighting).

-Jarmo
ih
Super User (Alumni) ih
Super User (Alumni)

Re: Can you construct this without writing expression a string?

Aah ha, using Insert Into() to build a function expression instead of a list is a great solution.  I will give this a try this evening, I see the light, thanks all!

Re: Can you construct this without writing expression as a string?

It is also fine to insert--in line--expressions resulting from substitutions, character conversions, etc. As ever, this is a balancing act between ease of interpretation/maintenance and conciseness. In the example below I've done this to illustrate.

 

Expression handling is not the easiest thing to learn (or teach). It is quite powerful though, and as you mention, allows you to keep the color-coded text that makes maintenance easier.

 

Names Default To Here( 1 );

dt = Open("$Sample_data/probe.jmp");

colList = {R_FUSE_16X0_5, VDP_PEMIT, "30P1_4X4_VBE"n, RM_RPB_100X7, "30N4_LE8_2RX1C_BVCBS"n}; //assume we've built this, somehow

gbExpr = Expr(
	gb = Graph Builder(
		Size( 838, 391 ),
		_VAR_EXP_,						// placeholder for Variables ( ) code.
		Elements( _LINE_EXP_ ),			// placeholder for Line ( ) code.
		Local Data Filter( Add Filter( columns( :DELL_RPPBR ), Where( :DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896 ) ) )
	)
);

varExpr = Expr( Variables( X( :DELL_RPPBR ) ) );  	//Loop will insert into the last (default) position

lineExpr = Expr( Line( X, Legend( 1 ) ) );			//Loop will insert into the i+1th position

For( i = 1, i <= N Items( colList ), i++,			//note we're inserting the result of a substitute ( ) in each case
	Insert Into( varExpr, Substitute( Expr( Y( Column( _COL_ ), Position( 1 ) ) ), Expr( _COL_ ), Char( colList[i] ) ) );
	Insert Into( lineExpr, Substitute( Expr( Y( _i_ ) ), Expr( _i_ ), i ), i + 1 );
);

Substitute Into( gbExpr, Expr( _VAR_EXP_ ), Name Expr( varExpr ), Expr( _LINE_EXP_ ), Name Expr( lineExpr ) );

gbExpr; //evaluate the (now completely built) expression to show graph

Re: Can you construct this without writing expression as a string?

I got this far but I can't work on it any more today. There is an error in the expression using the Name() directive. There is a way around it but I do not have time now.

 

Names Default To Here( 1 );

dt = Open( "$Sample_data/probe.jmp" );

//Variables to hold the dynamic parts of the graph builder expresssion
response expr = Expr(
	Variables( X( :DELL_RPPBR ) )
);

line expr = Expr(
	Line( X, Legend( 10 ) )
);

// Pick columns to include
col = (dt << Get Column Names)[Random Shuffle( 10 :: 397 )[1 :: 5]];

//Populate expressions
For( c = 1, c <= N Items( col ), c++,
	Insert Into( response expr, Substitute( Expr( Y( Name( ccc ), Position( 1 ) ) ), Expr( ccc ), Char( col[c], 1 ) ) );
	Insert Into( line expr, Substitute( Expr( Y( ccc ) ), Expr( ccc ), c ), c+1 );
);

Eval(
	Substitute( 
		Expr(
			gb = dt << Graph Builder(
				Size( 838, 391 ),
				Variables(
					vvv
				),
				Elements( Line( X, lll, Legend( 10 ) ) ),
				Local Data Filter(
					Add Filter(
						Columns( :DELL_RPPBR ),
						Where( -1 < :DELL_RPPBR <= 0.483837991952896 )
					)
				)
			)
		),
		Expr( vvv ), Name Expr( response expr ),
		Expr( lll ), Name Expr( line expr )
	)
);
Craige_Hales
Super User

Re: Can you construct this without writing expression as a string?

The insert into approach looks really good; I'm going to remember that. I'd been using a { list of expressions } and replacing the {} with the new head, like this:

dt = Open( "$Sample_data/probe.jmp" );
// Pick columns to include
cols = (dt << get column names())[Random Shuffle( 8 :: 397 )[1 :: 5]];

//Variables to hold the dynamic parts of the graph builder expresssion
List1 = {};
List2 = {};

//Populate expressions
For( c = 1, c <= N Items( cols ), c++,
	List1[c] = Eval Expr( Y( Expr( cols[c] ), Position( 1 ) ) );
	List2[c] = Eval Expr( Y( Expr( c ) ) );
);
// list1: {Y( Name( "M1-TRENCH_ISO_IL" ), Position( 1 ) ), Y(...
// list2: {Y( 1 ), Y( 2 ), Y( 3 ), Y( 4 ), Y( 5 )}
Insert Into( List1, Expr( X( :DELL_RPPBR ) ), 1 ); // front of list1
Insert Into( List2, Expr( X ), 1 ); // front of list2
Insert Into( List2, Expr( Legend( 10 ) ) ); // back of list2

// swap the {} for variables() and line()
variablesExpr = Substitute( List1, Expr( {} ), Expr( variables() ) );
lineExpr = Substitute( List2, Expr( {} ), Expr( Line() ) );

exprGB = Expr(
	//the graph builder script as a string with 'dummy variables' where the
	//expressions created earlier will go
	(Graph Builder(
		Size( 838, 391 ),
		Variables_,
		Elements( Line_ ),
		Local Data Filter( Add Filter( columns( :DELL_RPPBR ), Where( :DELL_RPPBR >= -1 & :DELL_RPPBR <= 0.483837991952896 ) ) )
	))
);

Substitute Into( exprGB, Expr( Variables_ ), Name Expr( variablesExpr ), Expr( Line_ ), Name Expr( lineExpr ) );

Show( Name Expr( exprGB ) );

Eval( exprGB );

 edit...

And InsertInto and RemoveFrom both work with positions in an expression. Cool, I really need to remember that! 

 

Craige
ih
Super User (Alumni) ih
Super User (Alumni)

Re: Can you construct this without writing expression as a string?

@Craige_Hales I only now realized the power of what you suggested here, I didn't know you can use substitute to modify non-continuous parts of an expression. This method essentially is what the R function do.call does (recreated below), and is similar to invoke or exec operators in R, or the splat operator in python:

 

//call another function (f) with the arguments provided in a list (l)
do.call = function({l,f}, 
	Eval(Substitute(
		Name Expr(l),
		Expr({}),
		Name Expr(f)
	) );
);

//as an example, this calls concat("a", "b", "c")
do.call({"a","b","c"}, Expr(concat()))

Nice!!