cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
The Discovery Summit 2025 Call for Content is open! Submit an abstract today to present at our premier analytics conference.
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