/*""" JSL in Graph Builder
Author: jthi
Creation Date: 2024-09-18
Creation JMP Version: JMP Pro 18.0.1
This is just a small peak to what can be scripted in graph builder.
This will also partially overlap with scripting graphs in general
"""*/
Names Default To Here(1);
dt = Open("$SAMPLE_DATA/Big Class.jmp");
stop(); // to prevent script from running to end while still adding here scope
/*""" Improving JMP created script """*/
// JMP created simple graph builder script
// Report snapshot: Big Class - Graph Builder
Data Table("Big Class") << Graph Builder(
Size(525, 454),
Show Control Panel(0),
Variables(X(:weight), Y(:height)),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
);
// Remove unnecessary comment, add reference to your table and store reference to a variable
// Common abbreviation is gb for Graph Builder
gb = dt << Graph Builder(
Size(525, 454),
Show Control Panel(0),
Variables(X(:weight), Y(:height)),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
);
/*""" Dynamically replace single column """*/
// NOTE: I always store my column "referneces" as strings of the column names
// This is most flexible (and robust) option in my opinion, but you will have to know
// how to modify those into other formats if necessary
group_col = "age";
gb = dt << Graph Builder(
Show Control Panel(0),
Variables(X(:weight), Y(:height), Page(group_col)),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
);
// Eval() is enough
gb = dt << Graph Builder(
Show Control Panel(0),
Variables(X(:weight), Y(:height), Page(Eval(group_col))),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
);
// You can go more complicated route AND sometimes this is helpful (building expressions)
// Usually my preferred method EvalExpr
gb_expr1 = EvalExpr(
dt << Graph Builder(
Show Control Panel(0),
Variables(X(:weight), Y(:height), Page(Expr(NameExpr(AsColumn(dt, group_col))))),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
)
);
//but you could use Substitute
gb_expr2 = Substitute(
Expr(dt << Graph Builder(
Show Control Panel(0),
Variables(X(:weight), Y(:height), Page(_groupcol_)),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
)),
Expr(_groupcol_), NameExpr(AsColumn(dt, group_col))
);
// Running (you can add Eval before EvalExpr() if you just want to create the graph
gb = Eval(gb_expr1);
// Note: you have already evaluated the expression -> groupcol won't be updated
group_col = "sex";
gb = Eval(gb_expr1);
// Many options how you can handle this, create a function, use expression, ...
gb_expr3 = Expr(EvalExpr(
dt << Graph Builder(
Show Control Panel(0),
Variables(X(:weight), Y(:height), Page(Expr(NameExpr(AsColumn(dt, group_col))))),
Elements(Points(X, Y, Legend(3)), Smoother(X, Y, Legend(4)))
)
));
group_col = "age";
gb = Eval(gb_expr3);
group_col = "sex";
gb = Eval(gb_expr3);
/*""" Note: Graph Builder is complicated platform """*/
gb = dt << Graph Builder(
Size(525, 448),
Show Control Panel(0),
Variables(X(:sex)),
Elements(Bar(X, Legend(3)))
);
Report(gb) << show properties();
Report(gb) << Show Tree Structure;
nw = New Window("gbscript", << type("Script"), << Language("XML"),
Report(gb) << Get XML
);
// You might not be able to access everything directly from Graph Builder report
// and you might have to utilize the data table (for example groups)
// If you access the values from table, remember that the order might be different
gb = dt << Graph Builder(
Size(599, 566),
Show Control Panel(0),
Variables(X(:sex), Group X(:age)),
Elements(Bar(X, Legend(3)))
);
// Report(gb)[GraphBuilderGroupBox(1)] << Select(1);
Report(gb)[GraphBuilderGroupBox(1)] << get xml; // You can try to parse groups from 121314151617age
// Remember data types and ordering
Column(dt, "age") << Set Property(
"Value Order",
{Custom Order({17, 16, 15, 14, 13, 12}), Common Order(0)}
);
Summarize(dt, groups1 = By(:age)); /// order considered (at least in this case)
groups2 = Associative Array(Column(dt, "age")) << get keys; // not considerd
dt_summary = dt << Summary(
Group(:age),
Freq("None"),
Weight("None"),
Link to original data table(0),
output table name("Groups"),
Private
);
groups3 = dt_summary[0, 1];
Close(dt_summary, no save);
show(groups1, groups2, groups3);
/*""" Finding graph builder object from window """*/
// Graph Builder Outline Box text cannot be relied on as it can be changed
// -> Utilize helpKey xml attribute
// References to outline boxes in current report (could be multiple)
obs = Current Report() << XPath("//OutlineBox[@helpKey = 'Graph Builder']");
gbs = obs << Get Scriptable Object; // references to graph builders
// Full example of looking for single graph builder from current report
obs = Current Report() << XPath("//OutlineBox[@helpKey = 'Graph Builder']");
gbs = obs << Get Scriptable Object; // references to graph builders
If(N Items(obs) < 1,
Throw("No graph builder found");
);
gb = obs[1] << get scriptable object;
gb << Get Data Table; // ... and so on
/*""" Changing different parts of graph builder """*/
// Changing some things is very simple
dt = Open("$SAMPLE_DATA/Big Class.jmp");
gb = dt << Graph Builder(
Size(599, 566),
Show Control Panel(0),
Variables(X(:sex), Group X(:age)),
Elements(Bar(X, Legend(3)))
);
gb << Title("MY NEW TITLE"); // Changes the outline box
// How about the text below outline box ("graph title")
gb << Subtitle("Test"); // nope
gb << Show Subtitle(1);
// XPath
tebs = Report(gb) << XPath("//GraphBuilderTitleBox/TextEditBox");
tebs[1] << Set Text("Before subtitle");
// Or report subscripting (I prefer XPath)
Report(gb)[GraphBuilderTitleBox(1), TextEditBox(1)] << Set Text("Before Subtitle2");
// Can also be modified with Dispatch but I don't personally like using these
// The syntax is difficult to understand in my opinion (what is "graph title" for example)
Report(gb) << Dispatch({}, "graph title", TextEditBox, {Set Text("BEFORE SUBTITLE")});
/*
nw = New Window("gbscript", << type("Script"), << Language("XML"),
Current Report() << Get XML
);
*/
// AxisBox
dt = Open("$SAMPLE_DATA/Big Class.jmp");
gb = dt << Graph Builder(
Size(599, 566),
Show Control Panel(0),
Variables(X(:sex), Group X(:age)),
Elements(Bar(X, Legend(3)))
);
// ScaleBox might do same thing (or not) I prefer using AxisBox
rep = Report(gb);
// Axis titles
// Dispatch is easiest option
Report(gb) << Dispatch({}, "Y title", TextEditBox, {Set Text("My Y Axis")});
Report(gb) << Dispatch({}, "X title", TextEditBox, {Set Text("My X Axis")});
// Note: if you have grouping and so on, you will can potentially have multipe frameboxes
// and axisboxes
rep[AxisBox(1)] << Select(1); // First X-axis
rep[AxisBox(2)] << Select(1); // Y-axis
rep[AxisBox(3)] << Select(1); // Second X-axis
// Learn to navigate these
abs = rep << XPath("//AxisBox");
yaxis = Remove From(abs, 2)[1];
yaxis << select(1);
// abs left with just xaxis references
yaxis << Min(-1) << Max(10);
abs << Min(1); // you can check from axis settings how the scale goes for categorical data
// In graph builder "index" starts from 0 but this isn't the same for all platforms
abs << Min(-0.5) << Max(1.5);
// FrameBoxes
dt = Open("$SAMPLE_DATA/Big Class.jmp");
gb = dt << Graph Builder(
Size(599, 566),
Show Control Panel(0),
Variables(X(:sex), Group X(:age)),
Elements(Bar(X, Legend(3)))
);
// ScaleBox might do same thing (or not) I prefer using AxisBox
rep = Report(gb);
fbs = rep << XPath("//FrameBox");
firstfb = fbs[1];
// If you want to have graphic scripts, you set them to frameboxes
firstfb << Add Graphics Script(
H Line(3);
);
// Remember that if you use variables, you might want to evalaute them in one way or another
ref = 2;
firstfb << Add Graphics Script(
H Line(ref);
);
ref = 3;
gb << Reshow;
refy = 1;
Eval(EvalExpr(
firstfb << Add Graphics Script(
V Line(Expr(refy));
);
));
refy = 0;
gb << Reshow;
vals = 1::7;
fbs[2] << Add Graphics Script(
For(i = 1, i <= N Items(vals), i++,
H Line(vals[i])
);
);
gb << reshow;
vals2 = 1::7;
fbs[3] << Add Graphics Script(
For Each({val}, vals2,
H Line(val)
);
);
vals2 = .;
gb << reshow;
vals3 = 1::7;
Eval(EvalExpr(
fbs[4] << Add Graphics Script(
myvals = Expr(vals3);
For Each({val}, myvals,
H Line(val);
);
);
));
gb << reshow;
// Segments might have nice information
// Or you can manipulate graphs through them
gb = dt << Graph Builder(
Size(525, 454),
Show Control Panel(0),
Variables(X(:weight), Y(:height)),
Elements(
Points(X, Y, Legend(3)),
Bar(X, Y, Legend(5))
)
);
segs = Report(gb)[FrameBox(1)] << Find Segs();
segs << class name;
ms = Report(gb)[FrameBox(1)] << Find Seg(MarkerSeg(1));
xs = ms << Get X Values;
ys = ms << Get Y Values;
// ms << Get Point(1);
// ms << get markers;
// ms << Get Row Numbers; // too bad this doesn't work in graph builder
// Test what happen if you don't utilize rank by using default order or remove [order]
//order = 1::N Items(xs);
order = Rank(xs);
Eval(EvalExpr(
Report(gb)[FrameBox(1)] << Add Graphics Script(
Pen Color("Blue");
Line(Expr(xs[order]), Expr(ys[order]));
);
));
// Dynamically building graph builder
// Might work as an example
// https://community.jmp.com/t5/JMP-Add-Ins/Column-Quick-Swapper-easily-change-multiple-Y-and-X-axis-columns/ta-p/724616
gb = dt << Graph Builder(
Size(525, 454),
Show Control Panel(0),
Variables(X(:weight), Y(:height)),
Elements(
Points(X, Y, Legend(3)),
Bar(X, Y, Legend(5))
)
);
gb << Add Variable({:age, Role("Wrap")});
gb << Remove Variable(1);
gb << Remove Variable(1);
gb << Remove Variable(1);
gb << Add Variable({:height, Role("Y")});
gb << Add Variable({:weight, Role("X")});
gb << Add Variable({:weight, Role("X")});
gb << Add Variable({:weight, Role("X"), Position(1)});
// Indices/positions can be very confusing with these
gb << Add Element(1, 1, {Type("Line Of Fit"), X, Y, Degree("Quadratic")});
gb << Remove Element(1, 1, 2);
// Example, we wish to build graph based on user inputs
// We do not know how many Y-axis user provides us with
// but we have static x-axis
dt = Open("$SAMPLE_DATA/Semiconductor Capability.jmp");
// Check what JMP created script would look with 1 and multiple columns
// if you want nested / separeate axis, test that also
// One column
gb = dt << Graph Builder(
Size(528, 448),
Show Control Panel(0),
Variables(X(:SITE), Y(:NPN1)),
Elements(Points(X, Y, Legend(3)))
);
// Multiple columns in same axis
gb = dt << Graph Builder(
Show Control Panel(0),
Variables(X(:SITE), Y(:NPN1), Y(:PNP1, Position(1)), Y(:PNP2, Position(1))),
Elements(Points(X, Y(1), Y(2), Y(3), Legend(4)))
);
// Separate axis
gb = dt << Graph Builder(
Show Control Panel(0),
Variables(X(:SITE), Y(:NPN1), Y(:PNP1), Y(:PNP2)),
Elements(Position(1, 1), Points(X, Y, Legend(19))),
Elements(Position(1, 2), Points(X, Y, Legend(20))),
Elements(Position(1, 3), Points(X, Y, Legend(21)))
);
// Now you have to figure out how to build this
// There are at least two approaches
// Build the expression Or utilize << add variable + << Add element
// Using add variable/element
// With these you can sometimes rely on JMP adding "most" of the script
// Separate Y Axis
// Create initial graph
gb = dt << Graph Builder(
Size(528, 448),
Show Control Panel(0),
Variables(X(:SITE)),
Elements(Points(X, Y, Legend(3))),
Invisible
);
ycols = {"NPN1", "NPN2", "NPN3"};
For Each({ycol, idx}, ycols,
Eval(EvalExpr(
gb << Add Variable({Expr(NameExpr(AsColumn(dt, ycol))), Role("Y")});
));
);
gb << reshow;
gb << Show Window(1); // show when ready
wait(0);
// Same Y-axis
gb = dt << Graph Builder(
Size(528, 448),
Show Control Panel(0),
Variables(X(:SITE)),
Elements(Points(X, Y, Legend(3)))
);
ycols = {"NPN1", "NPN2", "NPN3"};
For Each({ycol, idx}, ycols,
Eval(EvalExpr(
gb << Add Variable({Expr(NameExpr(AsColumn(dt, ycol))), Role("Y"), Position(1)});
));
);
// Building expression
variables_expr = Expr(Variables(X(:SITE)));
For Each({ycol, idx}, ycols,
cur_expr = Eval Expr(Y(Expr(Name Expr(AsColumn(dt, ycol)))));
Insert Into(variables_expr, Name Expr(cur_expr));
);
show(variables_expr);
Eval(Substitute(
Expr(gb = dt << Graph Builder(
Size(528, 448),
Show Control Panel(0),
_variables_
)),
Expr(_variables_), Name Expr(variables_expr)
));
// This works as points is the default in graph builder
// but usually you want to build it further. Example with different same axis
/*
gb = dt << Graph Builder(
Show Control Panel(0),
Variables(X(:SITE), Y(:NPN1), Y(:NPN2, Position(1)), Y(:NPN3, Position(1))),
Elements(Line(X, Y(1), Y(2), Y(3), Legend(4)))
);
*/
ycols = {"NPN1", "NPN2", "NPN3", "NPN4", "NPN5"};
variables_expr = Expr(Variables(X(:SITE)));
lines_expr = Expr(Line(X));
For Each({ycol, idx}, ycols,
cur_expr = Eval Expr(Y(Expr(Name Expr(AsColumn(dt, ycol))), Position(1)));
Insert Into(variables_expr, Name Expr(cur_expr));
cur_expr2 = Eval Expr(Y(Expr(idx)));
Insert Into(lines_expr, Name Expr(cur_expr2));
);
Eval(Substitute(
Expr(gb = dt << Graph Builder(
Size(528, 448),
Show Control Panel(0),
_variables_,
Elements(_lines_)
)),
Expr(_variables_), Name Expr(variables_expr),
Expr(_lines_), Name Expr(lines_expr)
));
// One example from community
Web("https://community.jmp.com/t5/The-Plot-Thickens/Reply-All-How-to-script-dynamic-content-for-Graph-Builder/ba-p/21092", JMP Window);
// There are a lot of tricks related to these but there is not time to start covering these in this session
// The documentation is also very lacking for these...
// Check GraphBuilderBox / Graph Builder function from scripting index for some possibilities
Open Help(
"Scripting Index",
Search(Term("Graph Builder"), Match({"Contains Terms", "Match All Terms", "Ignore Case"})),
IndexContext(
Category("All Categories"),
Object("Search results"),
Method("Add Element")
)
);
// manipulating graph builder
dt = Open("$SAMPLE_DATA/Big Class.jmp");
gb = dt << Graph Builder(
Variables(X(:height), Y(:weight)),
Elements(Points(X, Y), Smoother(X, Y))
);
// Changing summary statistic
gb << Update Element(1, 1, 1, {Summary Statistic("Mean"), Error Bars("Range")});
// Sometimes you might want to manipulate directly from UI
cbs = Report(gb) << XPath("//ComboBox");
cbs[1] << get items;
cbs[1] << Set(2); // Set summary statistic to "N" (second option)
cbs[1] << Set(Contains(cbs[1] << get items, "Mean"));
// One of the most asked question in JMP Community
// Adding lines to your plots
// Some of these methods apply to more than graph builder but not all
dt = Open("$SAMPLE_DATA/Big Class.jmp");
// Very simple example
gb = dt << Graph Builder(
Size(525, 490),
Show Control Panel(0),
Variables(X(:age), Y(:height), Group X(:sex)),
Elements(Points(X, Y, Legend(1)))
);
// different height "limits" for F and M
aa_limits = ["F" => {50, 65}, "M" => {55, 70}];
// You don't always have to rely on scripting, easiest option (depending on your data)
// is to utilize the data table. For Wide/Split data you might be able to utilize spec limits
// For tall data add line to your chart
newcol1 = dt << New Column("Low", Numeric, Continuous, Formula(
aa_limits[:sex][1];
));
newcol2 = dt << New Column("High", Numeric, Continuous, Formula(
aa_limits[:sex][2];
));
dt << run formulas;
newcol1 << Delete Formula;
newcol2 << Delete Formula;
// Note how Variables have been set in Points and Line
gb = dt << Graph Builder(
Size(570, 762),
Variables(
X(:age),
Y(:height),
Y(:Low, Position(1)),
Y(:High, Position(1)),
Group X(:sex)
),
Elements(Points(X, Y(1), Legend(1)), Line(Y(2), Y(3), Legend(4))),
SendToReport(
Dispatch({"Points"}, "", OutlineBox, {Close(0)}),
Dispatch({"Line"}, "", OutlineBox, {Close(0)})
)
);
// Great thing with this is that it supports easily multiple types of graphs
// and works with local data filter easily
// You can also use Caption to create lines but it might not work that well with X-group
// Scripting - use ref lines
dt = Open("$SAMPLE_DATA/Big Class.jmp");
gb = dt << Graph Builder(
Size(525, 744),
Show Control Panel(0),
Variables(
X(:age),
Y(:height),
Page(:sex)
),
Elements(Points(X, Y(1), Legend(1)))
);
// different height "limits" for F and M
aa_limits = ["F" => {50, 65}, "M" => {55, 70}];
// We have multiple graphs (we used Page)
Summarize(dt, groups = By(:sex));
fbs = gb << XPath("//AxisBox");
yaxes = fbs[2::N Items(fbs)::2];
For Each({yaxis, idx}, yaxes,
vals = aa_limits[groups[idx]];
yaxis << Add Ref Line(vals[1], "Dashed", "Red", "low", 1);
yaxis << Add Ref Line(vals[2], "Dashed", "Red", "high", 1);
);
// Scripting - use graphic script
// With multiple frame boxes and groups, this can quickly get quite complicated
gb = dt << Graph Builder(
Size(525, 490),
Show Control Panel(0),
Variables(X(:age), Y(:height), Group X(:sex)),
Elements(Points(X, Y, Legend(1)))
);
aa_limits = ["F" => {50, 65}, "M" => {55, 70}];
dt_groups = dt<< Summary(
Group(:sex),
N Categories(:age),
Freq("None"),
Weight("None"),
Link to original data table(0),
output table name("groups"),
invisible
);
groups = dt_groups[0, [1 3]];
Close(dt_groups, no save);
fbs = gb << XPath("//FrameBox");
total_groups = 0;
For Each({fb, idx}, fbs,
Eval(EvalExpr(
fb << Add Graphics Script(
limits = Expr(aa_limits[groups[idx][1]]);
start_group = 0;
total_groups = start_group + Expr(groups[idx][2]);
Pen Color("Red");
Line({start_group - 1, limits[1]}, {total_groups, limits[1]});
Pen Color("Blue");
Line({start_group - 1, limits[2]}, {total_groups, limits[2]});
);
));
);
// Variability charts and lines for more examples
Web("https://community.jmp.com/t5/Discussions/Add-Reference-Lines-to-multiple-Variability-Charts-in-a-Column/td-p/722908", JMP Window);
Web("https://community.jmp.com/t5/Discussions/different-spec-limits-on-a-variability-chart/td-p/565177", JMP Window);
Web("https://community.jmp.com/t5/Discussions/Varying-spec-limits-by-data-category-in-variability-chart/td-p/723027", JMP Window);
/*""" Not covered due to time limitations """*/
// Local Data Filter
// Can cause some issues as frameboxes are rebuilt,
Web("https://community.jmp.com/t5/Discussions/How-to-set-a-Reference-Line-on-each-page-with-Page-option-in/m-p/798517", "JMP Window");
// Column Switcher
// Hover Label
Web("https://www.jmp.com/support/help/en/18.0/index.shtml#page/jmp/work-with-the-hover-label-execution-context.shtml#ww599987");
// Legend Server
// Different state handlers
// Setting images to graph
Write();