Most Common JSL Mistakes and How to Avoid Them (2020-US-30MP-571)
Justin Chilton, JMP Senior Associate Software Development Engineer in Test, SAS
Wendy Murphrey, Senior Principal Technical Support Analyst, SAS
Have you been working on a scripting project and become frustrated by errors or unexpected results? In this session, we will identify some of the pitfalls that scripters of various experience levels encounter and help you to understand the cause so that you can move forward with accomplishing your scripting goals. We will help you through the simple mistakes, like not reviewing the log, to the foundational topics, such as scripting data tables and platforms, to the more complex topics, including name resolution and expressions. We will finish the session by providing resources available to you for learning more about the JMP Scripting Language.
Speaker | Transcript |
wendymurphrey | Hello and thank you for joining our presentation today. We're excited to talk with you about common scenarios that script authors at various experience levels encounter. |
Our intent is to explain what's happening so that you have the tools and knowledge to avoid these mistakes. So let's get started. | |
Perhaps because it's not open by default, it's easy to forget to check the log for messages. But the log contains useful information about recent script executions. So let's take a closer look at this example in JMP. | |
Here we can see that the script simply opens a data table and attempts to make a row selection. When we run this script, | |
we get an interactive error message for the fatal error, that is, those situations where your script stops due to the error. The pop up window is new behavior for JMP 15, and this can be turned off in preferences. | |
When we dismiss that message, we can check the log and we see the same message from the window, but we also see the line number | |
nd the name of the script file to help guide us to the trouble area. Now, don't solely rely on these pop up windows because other important messages will appear in the log, as well, | |
and they don't necessarily cause a pop up window. So let's take a look at another example. | |
In this script, the author is attempting to open a table, create an analysis, and print a message to the log. | |
And when we run this script, we notice right away that our analysis was not generated. | |
So again, we check the log | |
and we see that there was a column that was not found in the data table, but the script continued and printed our message to the log. | |
So the important takeaway here is always check the log, especially when what happens does not meet your expectation. It might be as simple as a misspelled column name. | |
The concept of current data table is often described as the active table. But what makes the table the active or current data table and why does it matter to your script? | |
When a table is opened, created, or otherwise acted upon, it becomes the current data table. But there are other ways that a table can become current at runtime that you as the script author might not have anticipated. | |
Their tasks that JMP processes in the background. For example, JMP may need to evaluate a formula or make an update to a window. | |
It's these background processes that can cause a different table than the one you intended to become active at runtime. And this can cause your script to fail, or worse, you don't even realize that JMP used the wrong table. | |
So here we've provided some | |
best practices to craft your scripts using data table-specific references. This is the way you can ensure that the correct table is used, regardless of what table is active at runtime. | |
In this example, we're demonstrating how you can send a bivariate call to a specific data table to ensure that the data in that table is used for your analysis. | |
You can also use the new column as a message to a specific data table, and this will ensure that your new column is created in the desired table and it does not go to whatever is current. | |
You can also use optional data table references. Here, we're just demonstrating how you can use that for referencing columns and we're going to talk in more detail about referencing columns in a little bit. | |
You can also use optional data table references in functions; for each row and summarize are two where this is available. | |
We'd also like to suggest that you avoid using a data table...assigning a data table reference using the current data table function in your production scripts. In this example we're joining two data tables and we would like to assign a reference to that table | |
that's created from the join. Now, join returns a reference to the newly created table. So instead of assigning to the current data table, we want to assign the reference when the join is created. | |
So here, we simply make our assignment when we call the join, and when we run this | |
we can see that indeed our new DT variable references the desire table and it is not affected by what table is current. | |
Being able to identify a column using a JSL variable is a common task and which method you use depends upon your intended use. | |
The column function returns a reference to the column as a whole unit. It accepts a variety of arguments, such as the column name is a string or a string variable, | |
an index or a numeric variable representing an index. It can be subscripted to reference the column's value on a specific row. | |
Some places that you can use the column function are in platform calls, sending messages to a column, or assigning a variable reference to a column. | |
The as column function returns a reference to the value on the current row | |
of the specified column. It accepts all the same arguments as the column function, but it's important to understand that in JMP, the current row is always zero unless otherwise managed. | |
So some great places to use the as column function are in formulas, for each row, and select where, because they control the row as they evaluate on on each row by their design. So let's take a look at an example of what we mean. | |
Here we're opening a sample data table. We've assigned a JSL variable to the name of a column, and we're attempting to use the column function in our select where. | |
Now, when we run this script, we find that no rows were selected. Why is that? Because select where evaluates on each row, | |
using the column function without any subscript will not select any rows. And we remember that the column function returns a reference to the column as a whole unit, so a whole column of data will never equal 14. | |
So the solutions are to use the column function with a row subscript (and this row subscript simply means the current row, whatever it is while the select where is evaluating), or you can use the as column function to select your rows. | |
The use of a JSL variable in formulas can be problematic for a few reasons. The lifespan of a JSL variable is limited to the current JMP session, and as the value of the variable changes, so can the result of the formula. | |
And unless you look at the formula in the formula editor, you may not realize you have a problem. | |
So let's take a look at our example. Here, I'm simply opening a data table and establishing a list of columns that I would like to sum. | |
I'm creating my new formula column and I'm using my JSL variable in my sum function. So let's run this. And we can see that our new column was created and populated with data. But let's take a look at the formula editor. We never said our JSL variable was used in the sum function. | |
And when we dismiss that, and we make a change to one of the dependent columns, we also noticed that our, our column formula was not automatically updated. | |
So let's save and close JMP, save this table and close JMP. | |
And we'll start again. | |
Now remember that the lifespan of that JSL variable was limited to the prior JMP session. So let's open the table and take a look at what happens. | |
We noticed that our variable...column formulas still did not evaluate again. When we take a look at that, we still see our JSL variable in there, but now we have a red line under it. And we hover over it, we see the name unresolved error. | |
If we try to re evaluate the formula we do get the error message and ignoring the errors will cause all of our data to go to be changed to missing. So how do we fix this? | |
When you use a variable in a formula, you'll need to do something to instruct JMP to replace the variable with its value before evaluating the formula. You can use substitute for this, but today we're going to demonstrate expression functions. So let's close this table. | |
We'll open a fresh copy of the table and reestablish our list of columns. | |
And now let's take a look at how we work around this. | |
So the eval expression function tells JMP to evaluate and replace any variables in there...in its argument that are wrapped in an EXPR function. | |
And then it will return the unevaluated expression. So in this code JMP is going to replace the expression SAT scores and replace it with our list of columns. | |
So let's run just this section and see what happens. And we do see that the sum function now has our list of columns that we want to sum instead of the variable. | |
But it didn't actually create the column for us. It's simply returned this expression. So to evaluate that, we add an eval function around our eval expression function. | |
And this will cause JMP to evaluate the entire new column expression after the replacement has occurred. So essentially, the email says, run... run what's returned by the eval expression, which is this. | |
So let's run it and we can see that our column was now created and we have the data as we expect. | |
And our formula does not have our variable any longer, it has our list of columns. | |
And when we make a change to the data in a dependent column, our formula now updates automatically. | |
So now I'm going to pass this over to Justin to talk about a few more mistakes to avoid. | |
Justin Chilton | Alright, so another mistake that we see people make is when they're going to set column properties and the syntax can be a little bit tricky. So before we get into the details, |
let's talk about what a column is in terms of column properties. So essentially a column is just a | |
repository for this property information. It doesn't do any syntax checking to make sure that | |
what you give it is correct and this enables it to store anything that you'd like. And this means that this is because the consumer of the property expects something to be in a specific format, whether that be a JMP platform, using a column property or even a JSL application. | |
So let's take a look at a quick example. | |
And here, we are going to open up a table that we have called column properties, just for testing. We have one column called NPN1 and one column called test. And this column has spec limits and an access property, but we want to | |
add the spec limit properties to test. So we have here, what we think | |
is the correct syntax to set the spec limits column properties. We run this and doesn't give us an error but when we | |
look at the column, there is spec limits on that column. | |
And you can, but you can tell that it's not correct. When we asked for process capability analysis with our spec limits, | |
when we run that, we do get one for NPN1 and but we don't get one for the test column. That's because what's stored in the spec limits property for the test column is not correct. | |
So one way to do this if you already know how to use the set properties spec limits, | |
you can just send the property message to the column NPN1, which is the column that we know to be correct. So if we run just that one line, we get the correct syntax. | |
And this actually shows us that this should really be in a list form instead of separate arguments. So another way, if you're not familiar with the get property, you can go to the column that you know is correct and copy column properties. | |
And then you can paste that into your script and you'll see that you have add column properties as well as a couple of set property. So add column properties is just a way to batch up multiple set properties. We can remove that and we can remove the extra access | |
property. So once we're done removing that, we can use this set property to send it to our test column with the send operator. So now when we run this, | |
we...it's...it has overwritten our spec limits, but we can confirm that by running our same distribution using our spec limits. | |
So, and you can see here that we have our process capability analysis for both of our columns. So that's just one example of how to get spec limits, but the same can apply to any column property that you need to apply and get the syntax for in JSL. | |
So the next mistake that we're going to talk about is manipulating dispatch commands. So what is a dispatch command? | |
It's the thing that appears in your saved script when you do the red triangle menu's save to script window or save to clipboard. | |
And it appears when you add something like a reference line or you change something else like closing an outline box. And this command was developed as an internal way for JMP to be able to recreate graphs | |
with the customization that you made. And it wasn't planned for users to | |
customize this with dynamic variables. So that means that some of the arguments in the dispatch command do not accept JSL variables. So let's take a look at an example of where that might happen. So here's a script. We have a | |
table and a string variable which will use a little later. And then this is essentially what would be returned by the save to script window. So when we run this, | |
we can see that our dispatch command that sets the min max and increment for measurement axis | |
here is set from zero to 1.4. Now we can see that the dispatch command is relying on the | |
outline box title here, but that outline box title is actually based on our y input. So say that we wanted this to be dynamic input from a user and all we have this this variable. We could use the variable for the Y. | |
But then we would also need to use that that the name of that column in here. So when we run this, you'll actually see that we don't get the correct axis settings 0.4 to 1.2, which is not...so that means that what we sent was not processed. | |
So there are a couple ways to address this. And the first is going to be using eval expression solution that Wendy talked about a few minutes ago. So here you see we have the same eval expression. We actually want to wrap the concatenation that we tried in the previous example | |
here. | |
And so eval expression will say evaluate this whole thing. So when we run eval EXPR, | |
it just replaces that whole thing with our concatenated string and then, remember when we do the eval, | |
it runs that expression. So here we have 0 to 1.4. So that's one way. | |
But sometimes it can still get a little bit tricky. And what how you should really manipulate those dispatch commands because you're still using the dispatch commands and you have to know how they work. So the most common way of doing this is to remove this this dispatch command altogether. | |
So, and use what we call display box subscripting. So here I'm going to run this with the removed dispatch command and we have our original axis settings. | |
And then what we're going to do is, before we don't really know how to write this, just from looking at it. So we're going to go to this icon, this outline box, right click | |
edit and show tree structure. So, this shows us the display box tree structure within this outline box and within this outline box, you can see this axix box is the one that we want to change. So within this outline box is axis box one. | |
So then we can go and write our script. We use this report function to get the report layer of the platform, OBJ1. | |
Then we subscript to get the outline box title that we know it should be, based on our column name. | |
And then within that we want...and we want to be as specific as possible, so that if things change in a future version of JMP we're less likely to have issues. | |
So then within that, we want axis box 1 and that's how we get that axis box, and then we can take the same pieces of information from the dispatch command and just convert them the messages that we send to the axis box. | |
And then you can see over here that now our axis settings are from zero to 1.4. | |
So another more powerful way to do this is using XPath. It does the same thing, but can be a little bit more complicated, but also allows for more flexibility for things in future versions of JMP because you can use underlying data about a box. | |
So the last mistake that we're going to discuss is only using the global namespace. So | |
you may not know it, but when you're when you create a symbol without specifying where it goes, it goes into the global namespace. So this is great for quick scripts when you're just mocking something up for your personal use, | |
but it may not be great idea for scripts that you send to colleagues or scripts that you publish or add ins that you publish on the Community. | |
So little bit more details about how scoping and and name lookup works in JMP. You start at the bottom here in this diagram. The local scope can can be any number of scopes, with which are functions and local box. | |
So above that, if it doesn't find the symbol or you didn't specify it to go in local depending on if you're writing or reading, | |
then there's the here scope, and that exists essentially one per file. And there's global scope, which essentially is one scope for your session of JMP. And then over to the right, we have namespace scope, which is something that you really have to explicitly seek out in order to | |
access. Let's take a look at an example. This is just going to delete all of my symbols and since I didn't specify where they should go, it's going to go into globals. So x is 5. But what if someone else has a script out there that's like, well, I also | |
have a | |
variable x that I want to use. And I'm not going to specify where to put it. So I'm going to set it to 10. | |
But then when you come back to your script and maybe this is some variable that you've set and then you have some callback on a button...button | |
action that is relying on that variable, then you go to use it and the variable has changed on you and that's probably not desired. | |
So what are some ways that we can address this? Well, the easiest way to do this is to use names default to here(1). This means that nothing will end up in the global scope, unless you explicitly tell it to go there. So let's take a look | |
at this | |
example. So all we've done is add this name default to here(1). It can go with...generally it goes at the top of your script, but it can really go anywhere. | |
I'm going, I'm going to clean up my symbols and set this variable x equal to 5. So then we have some other scripture over here that's | |
says that says I want to access your, your x. But really this x can can never access this x in this here namespace. So | |
let's copy part of the script and do X equals 50. | |
And then so the X in the right hand script is 50, but that had no effect on the X in our left hand script. So that's what, that's a great way to easily separate your symbols from other scripts and applications. | |
The other option is to use a custom namespace. This is a little bit more advanced, but it can be very powerful. So here I'm again...clear my | |
symbols and then creating a new namespace, setting up a variable x within the namespace and showing it. So it has a value of five. | |
And you have another script out here. Maybe this is one of your scripts and you know that | |
myNS exists. So you can try and access that, and you can do that just fine, because you know the name of this. But it's much less likely that someone nefarious is going to change your symbol without your knowledge. So | |
I can change this symbol and it's still going to be 10 but if I just do X, it's, it's not going to really have any have any effect on the result of the the X from the namespace. | |
I'm going to throw it back over to Wendy, who's going to tell us about some overlooked resources. | |
wendymurphrey | Thank you, Justin. There are many resources available to you to learn more about JSL, and many of them are free. Here we've linked for you some of the more common places to look and learn more about JSL. |
We also offer technical support that you can reach out to, and there are a couple of books on the topic as well. Of course, we always encourage you to take a look at our training resources and the plans that they have there for you. | |
Be sure to take a look at the paper associated with this presentation on the JMP community, as there are a few more mistakes that we covered there for you, which we didn't have time to discuss today. Thank you for watching. |