cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
Choose Language Hide Translation Bar
thickey
Level III

How to programmatically traverse a Graph builder tree structure and associate frameboxes with their titles.

I have a piece of analysis (see below) where I want to annotate specific charts using a script I have written. For example, I want to add annotation to XXX104 and XXX107. On both of these charts I will add a custom graphic and text to identify a cluster of points I want to draw attention to. 

 

chart.jpg

 

Using the command '<< show tree structure' I can identify the frameboxs which have each chart. However, I do not see the chart 'name' associated with them. For example, I want to be able to traverse this tree using the chart names (e.g. XXX107) as a lookup and identify the associated framebox (e.g. FrameBox(7)) so I can reference it and add my annotation in an automated fashion.

 

chart2.jpg

 

In fact in the entire tree I do not see any 'tree nodes' which have the 'chart names' such as XXX107

 

Ultimately the chart below is what I want to achieve

chart.jpg

 

Here is the code which I used to do this manually. The file is also uploaded. 

My issue is I cannot figure out how to reference a framebox by identifying it's corresponding 'title' in the display tree.

 

Cheers, Troy

 

clearLog();
try(close(DT, noSave));
DT = open("PRE_CLEAN_DATA.jmp", invisible);
DT << newColumn("ENT_CLUSTER", character, nominal, formula(:ENTITY || "_" || :CLUSTER));

Plat = newWindow("CHART",
	myGraphBox = DT << Graph Builder(
		Size( 1446, 747 ),
		Show Control Panel( 0 ),
		Variables(
			X( :WW ),
			Y( :ENTITY_MEAN ),
			Y( :GRAND_MEAN, Position( 1 ) ),
			Wrap( :ENTITY ),
			Color( :CLUSTER )
		),
		Elements(
			Points( X, Y( 1 ), Y( 2 ), Legend( 23 ) ),
			Smoother( X, Y( 1 ), Y( 2 ), Legend( 24 ) )
		),
		SendToReport(
			Dispatch(
				{},
				"WW",
				ScaleBox,
				{Min( 202301.28 ), Max( 202320.72 ), Inc( 1 ), Minor Ticks( 1 ),
				Label Row(
					{Automatic Tick Marks( 0 ), Label Orientation( "Vertical" ),
					Show Minor Ticks( 0 )}
				)}
			)
		)
	);
	
	summarize(DT, clusters = by(:ENT_CLUSTER));
	foreach({item}, clusters,
		if(contains(item, "NON_CLUSTER"), continue());
		
		ent = word(1, item, "_");
		clust = word(2, item, "_");
		write("Entity = " || ent || "\!n");
		write("Cluster = " || clust || "\!n");
		
		left = min(DT:WW[DT << getRowsWhere(:ENT_CLUSTER == item)]);
		right = max(DT:WW[DT << getRowsWhere(:ENT_CLUSTER == item)]);
		group_mean_min = min(DT:ENTITY_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
		group_mean_max = max(DT:ENTITY_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
		grand_mean_min = min(DT:GRAND_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
		grand_mean_max = max(DT:GRAND_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
		if(group_mean_min < grand_mean_min, bot = group_mean_min, bot = grand_mean_min);
		if(group_mean_max > grand_mean_max, top = group_mean_max, top = grand_mean_max);
		write("Left, Right, Top, Bot = " || char(left) || ", " || char(right) || ", " || char(top) || ", " || char(bot) || "\!n");
		
		// ##########################################################
		// ## This is what I want to do:
		// ##########################################################
		//  1. Traverse the graphbox tree
		//  2.  Identify Frame Box for each of the entities found
		//  3.  Get the Framebox Reference
		//  4.  Add my annotation.
		

		// This is a manual Example I made using the show properties and tree structure commands
		//myGraphBox << Show Properties();
		//myGraphBox << Show tree structure();
		rep = myGraphBox << report;
		framebox = rep[frame box( 7 )];
		framebox << Add Graphics Script(
			penColor( "Black" );
			fillcolor("Blue");
			Transparency(0.1);
			rect( left, top, right, bot, 0 );
			rect( left, top, right, bot, 1 );
		);
		framebox << Add Graphics Script(
			textSize(9);
			textColor("Blue");
			text( {left, top}, "Cluster 1" );
		);
		
		write("\!n");
	);
	
);

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
jthi
Super User

Re: How to programmatically traverse a Graph builder tree structure and associate frameboxes with their titles.

Quick and dirty full example:

Names Default To Here(1);

dt = Open("$DOWNLOADS/PRE_CLEAN_DATA.jmp", invisible);
dt << New Column("ENT_CLUSTER", character, nominal, formula(:ENTITY || "_" || :CLUSTER));

gb = dt << Graph Builder(
	Size(1446, 747),
	Show Control Panel(0),
	Variables(X(:WW), Y(:ENTITY_MEAN), Y(:GRAND_MEAN, Position(1)), Wrap(:ENTITY), Color(:CLUSTER)),
	Elements(Points(X, Y(1), Y(2), Legend(23)), Smoother(X, Y(1), Y(2), Legend(24))),
	SendToReport(
		Dispatch(
			{},
			"WW",
			ScaleBox,
			{Min(202301.28), Max(202320.72), Inc(1), Minor Ticks(1), Label Row(
				{Automatic Tick Marks(0), Label Orientation("Vertical"), Show Minor Ticks(0)}
			)}
		)
	)
);
rep = Report(gb);
fbtitles = (rep << XPath("//GraphBuilderGroupBox/text()"));
Remove From(fbtitles, Contains(fbtitles, "ENTITY")); // we "know" we have wrap variable 
// or we could perform filtering using Filter Each

Summarize(dt, clusters = by(:ENT_CLUSTER));

For Each({item}, clusters,
	If(Contains(item, "NON_CLUSTER"),
		Continue()
	);
	
	ent = Word(1, item, "_");
	clust = Word(2, item, "_");
	Write("Entity = " || ent || "\!n");
	Write("Cluster = " || clust || "\!n");
	
	left = Min(DT:WW[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	right = Max(DT:WW[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	group_mean_min = Min(DT:ENTITY_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	group_mean_max = Max(DT:ENTITY_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	grand_mean_min = Min(DT:GRAND_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	grand_mean_max = Max(DT:GRAND_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	If(group_mean_min < grand_mean_min,
		bot = group_mean_min,
		bot = grand_mean_min
	);
	If(group_mean_max > grand_mean_max,
		top = group_mean_max,
		top = grand_mean_max
	);
	Write("Left, Right, Top, Bot = " || Char(left) || ", " || Char(right) || ", " || Char(top) || ", " || Char(bot) || "\!n");
	
	framebox = rep[frame box(Contains(fbtitles, ent);)];
	Eval(EvalExpr(
		framebox << Add Graphics Script(
			Pen Color("Black");
			Fill Color("Blue");
			Transparency(0.1);
			Rect(Expr(left), Expr(top), Expr(right), Expr(bot), 0);
			Rect(Expr(left), Expr(top), Expr(right), Expr(bot), 1);
		);
		
	));
	Eval(EvalExpr(
		framebox << Add Graphics Script(
			Text Size(9);
			Text Color("Blue");
			Text({Expr(left), Expr(top)}, Expr(clust));
		);
	));
	Write("\!n");
);

jthi_0-1699879213845.png

 

(I have done something a bit like this earlier for wafermaps)

 

-Jarmo

View solution in original post

4 REPLIES 4
jthi
Super User

Re: How to programmatically traverse a Graph builder tree structure and associate frameboxes with their titles.

GFrameBoxes are sorted in specific order (might be a good idea to force it using Value Order column property). You can get the framebox index based on that order

 

cluster_rows = Loc(dt[0, "ENT_CLUSTER"], "XXX104_CLUSTER1");
fb_idx = Contains(clusters, "XXX104_CLUSTER1"); // framebox idx // might not work in all cases!
fbs = Report(myGraphBox) << XPath("//FrameBox");
Eval(EvalExpr(
	fbs[fb_idx] << Add Graphics Script(
		Pen Color("Black");
		Fill Color("Red");
		Transparency(0.1);
		Rect(Expr(left), Expr(top), Expr(right), Expr(bot), 0);
		Rect(Expr(left), Expr(top), Expr(right), Expr(bot), 1);
	);	
));

Also remember to evaluate the values into graphic script, so you won't end up with this.

 

jthi_0-1699875428523.png

 

In general GraphBuilderGroupBox are very difficult to access/manipulate and I would try to avoid it as much as possible. You can get the titles with XPath, but there can be extra titles

(Report(myGraphBox) << XPath("//GraphBuilderGroupBox/text()"))
{"ENTITY", "XXX101", "XXX102", "XXX103", "XXX104", "XXX105", "XXX106", "XXX107","XXX112", "XXX118", "XXX119"}
-Jarmo
jthi
Super User

Re: How to programmatically traverse a Graph builder tree structure and associate frameboxes with their titles.

Quick and dirty full example:

Names Default To Here(1);

dt = Open("$DOWNLOADS/PRE_CLEAN_DATA.jmp", invisible);
dt << New Column("ENT_CLUSTER", character, nominal, formula(:ENTITY || "_" || :CLUSTER));

gb = dt << Graph Builder(
	Size(1446, 747),
	Show Control Panel(0),
	Variables(X(:WW), Y(:ENTITY_MEAN), Y(:GRAND_MEAN, Position(1)), Wrap(:ENTITY), Color(:CLUSTER)),
	Elements(Points(X, Y(1), Y(2), Legend(23)), Smoother(X, Y(1), Y(2), Legend(24))),
	SendToReport(
		Dispatch(
			{},
			"WW",
			ScaleBox,
			{Min(202301.28), Max(202320.72), Inc(1), Minor Ticks(1), Label Row(
				{Automatic Tick Marks(0), Label Orientation("Vertical"), Show Minor Ticks(0)}
			)}
		)
	)
);
rep = Report(gb);
fbtitles = (rep << XPath("//GraphBuilderGroupBox/text()"));
Remove From(fbtitles, Contains(fbtitles, "ENTITY")); // we "know" we have wrap variable 
// or we could perform filtering using Filter Each

Summarize(dt, clusters = by(:ENT_CLUSTER));

For Each({item}, clusters,
	If(Contains(item, "NON_CLUSTER"),
		Continue()
	);
	
	ent = Word(1, item, "_");
	clust = Word(2, item, "_");
	Write("Entity = " || ent || "\!n");
	Write("Cluster = " || clust || "\!n");
	
	left = Min(DT:WW[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	right = Max(DT:WW[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	group_mean_min = Min(DT:ENTITY_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	group_mean_max = Max(DT:ENTITY_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	grand_mean_min = Min(DT:GRAND_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	grand_mean_max = Max(DT:GRAND_MEAN[DT << getRowsWhere(:ENT_CLUSTER == item)]);
	If(group_mean_min < grand_mean_min,
		bot = group_mean_min,
		bot = grand_mean_min
	);
	If(group_mean_max > grand_mean_max,
		top = group_mean_max,
		top = grand_mean_max
	);
	Write("Left, Right, Top, Bot = " || Char(left) || ", " || Char(right) || ", " || Char(top) || ", " || Char(bot) || "\!n");
	
	framebox = rep[frame box(Contains(fbtitles, ent);)];
	Eval(EvalExpr(
		framebox << Add Graphics Script(
			Pen Color("Black");
			Fill Color("Blue");
			Transparency(0.1);
			Rect(Expr(left), Expr(top), Expr(right), Expr(bot), 0);
			Rect(Expr(left), Expr(top), Expr(right), Expr(bot), 1);
		);
		
	));
	Eval(EvalExpr(
		framebox << Add Graphics Script(
			Text Size(9);
			Text Color("Blue");
			Text({Expr(left), Expr(top)}, Expr(clust));
		);
	));
	Write("\!n");
);

jthi_0-1699879213845.png

 

(I have done something a bit like this earlier for wafermaps)

 

-Jarmo
thickey
Level III

Re: How to programmatically traverse a Graph builder tree structure and associate frameboxes with their titles.

Thanks for the help Jarmo.

I've not done a whole pile of work on traversing these trees but the way you explain it is great.....perhaps this one could be a topic for next years scripters club

 

Cheers, Troy

 

 

jthi
Super User

Re: How to programmatically traverse a Graph builder tree structure and associate frameboxes with their titles.

Going deeper into the report layer could definitely be one good topic.

-Jarmo