I have results from an experiment.
It is an investigation of a material that can reasonably be expected to obey Ohm’s law.
I have a challenge for you …
What is the best estimate for the resistance of this material?
It seems like a fairly trivial problem to solve, but whenever I ask this question in workshops I get a variety of answers.
Please also think about the degree of precision to which you can quote the answer, and ideally qualify the answer with the standard error of the estimate.
Here is the data, together with a download link:
Use the link below to view the data (rightclick to save):
]]>
JSL Decoded: How to Write a JSL Script
If you wish to download the final version of the code, you can do so with this link:
Download final version of the code (lesson 6)
This is a .TXT file: rename it to .JSL to use it inside JMP Software.
]]>When you create a new journal, you are presented with an empty window – which can feel a bit intimidating.
What content should you add, and why?
I take a look at 4 usecases for JMP Journals …
]]>
So if you have ever gone to a conference and the presenter has used a dendrogram assuming you know how to read it, but you didn’t, then this is for you.
]]>
I’m going to share with you 3 tips to increase their effectiveness.
Convert a continuous response into a binary response with values representing good and bad outcomes.
If this condition is relative to a specification limit then the binary response can be generated using a column formula. But more generally, the column can be created by the selecting data rows of interest and then choosing Name Selection In Column from the Row Selection submenu of the Rows menu.
For a decision based on a binary outcome each node of the tree will show a barchart representing the relative proportions of the outcomes. To quantify these proportions, and to have clearly labelled outcomes, select Show Split Prob from the Display Options red triangle submenu.
If the default colours assigned by JMP are confusing (e.g. red for good, blue for bad) this can be a source of cognitive dissonance. Use the Value Colors property of the binary response column to make more logical colour assignments.
The video below explores these ideas in more detail.
]]>
For 2024, I will give more focus to my YouTube channel.
This means more regular blog posts, supplemented by video content …
Historically, my videos have been traditional style based on a screencast with voiceover, but I’m been busy trying to embrace a more authentic YouTube style of presentation. I hope you will enjoy the new content that I am developing. Here is a taster …
]]>I am building a library of functions that I would like to distribute as a single JSL file. But some of my functions reference image files. In this blog post I will explain the process I use for serialising the image file so that it can be embedded directly into my JSL script.
Here is one of my functions:
Assert = function({condition,title,msg,doThrow=1}, {default local}, if (!condition, iconDir = FileLocationClass:getIconsDir; img = open( resources  "asserterror.png", png ); img << setSize({194,248}); NewWindow(title, <<Modal, BorderBox(top(0),bottom(30),left(20),right(20), VListBox( HListBox( SpacerBox(size(40,0)), PictureBox( img ) ), TextBox(msg) ) ) ); if (doThrow,throw()) ); );
Central to this discussion is the PNG image file called ‘asserterror’ that sits in my resources folder.
If I distribute this code as part of a JMP addin, then I can include the resources folder as part of the addin build. But I don’t want to do that. I just want a simple JSL file that other programmers can include into their projects.
My first step is to look at the img object that I am producing. So I create a utility script:
img = open( resources  "asserterror.png", png ); show(img);
JMP’s log window shows the result:
img = New Image(Char To Blob( "63Yyz... waJ", "base64compressed" ), "png");
The entire output is long so I’ve put “…” in rather than the full text which is about 50,000 characters in length!
Structurally:
img = newImage( charToBlob( strImg, "base64compressed" ), "png" );
And this is the code that I can use to embed my image. The string representation I get from my log window output of my utility script:
strImg = "63Yyz... waJ”; // the full 50k characters here img = newImage( charToBlob( strImg, "base64compressed" ), "png" );
And that’s it. Problem solved.
]]>
The new syntax of iteration is based on the for each function. The documentation for this function describes its use for iterating over elements of ‘containers’. A container is just an abstract name for something that contains a collection of items: most commonly lists, but also matrices and associative arrays.
Here is an example of the traditional use of a forloop in JSL:
lstAges = {12,13,14}; for (i=1,i<=nitems(lstAges),i++, age = lstAges[i]; show(age) );
Output:
age = 12 age = 13 age = 14
lstAges = {12,13,14}; for each( {age},lstAges, show(age) );
Output:
age = 12 age = 13 age = 14
dt = data table("Big Class"); heights = dt:height << get values; for each( {height},heights, show(height) );
In example 1.3 above the height measurements are in inches. Do people really still l use inches? Let’s convert to centimetres:
newHeights = {}; for (i=1,i<=nrows(heights),i++, newHeights[i] = 2.54 * heights[i] );
newHeights = transform each( {height},heights, height * 2.54 );
The iteration index is explicit in a forloop:
for (i=1,i<=nitems(lstAges),i++,
age = lstAges[i];
show(i,age)
);
Example 3.2 using the index keyword
for each({index},lstAges,
show(index)
);
Notice that this is the identical syntax for the use of ‘for each’. The only difference is the name of the element is ‘index’. Index is a keyword that indicates you want the index of the item and not the item value. If you want both, see the next example.
Example 3.3 using elementindex pairs
for each ({age,i}, lstAges,
show(age,i)
);
Example 4.1 working with associative arrays
For each can also be used to work with associative arrays:
lstAges = {12,13,14};
strAges = {"twelve","thirteen","fourteen"};
arr = Associative Array(lstAges,strAges);
for each ( { {key,value} }, arr,
show(key,value)
);
key = 12
value = "twelve"
key = 13
value = "thirteen"
key = 14
value = "fourteen"
Example 5.1 working with multiple lists
Sometimes we work with collections of lists to track multiple associated quantities. Here is an example:
lstParameters = {"Temperature","pH"};
lstLSL = {20,5.5};
lstTarget = {25,6.0};
lstUSL = {30,7.0};
for (i=1,i<=nitems(lstParameters),i++,
parameter = lstParameters[i];
lsl = lstLSL[i];
target = lstTarget[i];
usl = lstUSL[i];
);
Example 5.2 for each item in multiple lists
foreach({parameter,lsl,target,usl},across(lstParameters,lstLSL,lstTarget,lslUSL),
show({parameter,lsl,target,usl)
)
Example 5.3 misasligned lists
When referencing items across lists the presumption is that the lists are of equal length. Problems occur if that is not the case:
lstParameters = {"Temperature","pH","Humidity"};
lstLSL = {20,5.5};
lstTarget = {25,6.0};
lstUSL = {30,7.0};
for each({ {parameter,lsl,usl} },across(lstParameters,lstLSL,lstUSL),
show(parameter,lsl,usl)
)
Output:
parameter = "Temperature"
lsl = 20
usl = 30
parameter = "pH"
lsl = 5.5
usl = 7
parameter = "Humidity"
lsl = 20
usl = 30
Example 5.4 using the count option to resolve differences across containers
lstP = {"Temperature","pH","Humidity"};
lstLSL = {20,5.5};
lstTarget = {25,6.0};
lstUSL = {30,7.0};
for each({ {p,lsl,usl} },across(lstP,lstLSL,lstUSL,count("Shortest")),
show(p,lsl,usl)
)
Output:
p= "Temperature"
lsl = 20
usl = 30
p= "pH"
lsl = 5.5
usl = 7
Example 5.5 strict enforcement
try(
for each({ {p,lsl,usl} },
across(lstP,lstLSL,lstUSL,
count("Enforce Equal")),
show(p,lsl,usl)
)
,
show("oops")
);
]]>
http://www.pegaanalytics.co.uk/blog/iteration/feed/
0

Curve Fitting
http://www.pegaanalytics.co.uk/blog/curvefitting/
http://www.pegaanalytics.co.uk/blog/curvefitting/#comments
Wed, 13 May 2020 13:56:41 +0000
http://www.pegaanalytics.co.uk/blog/?p=2438
Continue reading Curve Fitting ]]>
The curve fitting platform allows you to select from a library of model types.
In this post I take a closer look at the different model types that are available to support curve fitting.
Each of the model categories contain a variety of models with differing numbers of parameters:
Polynomial Models
If you use linear regression (standard least squares) you will be familiar with this type of model:
. . .
Whilst gradient descent algorithms can be used to estimate these parameters, the primary role of curve fitting is to fit parameters that form part of a nonlinear equation – typically representing some mechanistic model relating to a scientific application. All other model types fall into this category of nonlinear models.
Sigmoid / Logistic Curves
The basic sigmoid function takes the following form:
It characterises the case where an unbounded x variable is transformed into a y variable that is contained within a range 0 to 1. It is therefore particularly useful for modelling a response that represents a proportion.
The logistic function introduces one or more parameters to generalise the behaviour of this Scurve. For example, a parameter can be introduced to control the growth rate:
The curve has a point of inflection at x=0. The introduction of a second parameter allows the location of this inflection point to be adjusted:
This is the formula for the Logistic 2P model.
Whilst y is a continuous response, these types of model are often used to model a binary outcome (0 or 1). In this case the y value is interpreted as the probability of an outcome of 1 given a specified value of x.
The Logistic 3P model introduces a third parameter allowing the curve to have an upper asymptote other than 1:
And the Logistic 4P model provides a description of both upper and lower asymptotes with parameters c and d:
There is also a Logistic 5P model that allows the curve to be asymmetric about the inflection point:
Sigmoid/Probit Curves
The logistic functions described earlier typically represent the case where the response is derived from the probability of a binary outcome. Alternatively, we can model the Scurve on the basis that it represents the cumulative distribution function Φ of a Normal distribution:
where the parameter a represents the growth rate and b is the point of inflection. This is the Probit 2P model.
The Probit 4P model introduces parameters to control the lower and upper asymptotes:
Sigmoid/Gompertz Curves
The 5parameter logistic model describes an Sshaped curve that is asymmetric about the inflection point. A Gompertz curve can be considered to be a special case of this model. As described in Wikipedia the model was first proposed as a description of human mortality.
A fourparameter model is also available that provides parameters for both lower and upper asymptotes.
Sigmoid/Weibull Growth Curve
Another Sshaped curve is the Weibull Growth model, often used in reliability engineering:
Where a is the upper asymptote, b is the growth rate, and c is the inflection point.
Exponential Growth and Decay
Exponential 2P is the basic exponential model:
The parameter b is a scaling parameter and λ represents the growth rate. If λ is negative, then it represents the rate of decay.
The Exponential 3P model adds an additive term to control the asymptote of the curve:
An alternative parameterisation is the mechanistic growth model:
JMP also supports biexponential models. These models are the sum of two exponentials and appear as 4parameter and 5parameter models:
Cell Growth
Growth of cells in a bioreactor can be characterised by a number of phases:
JMP’s Cell Growth 4P model takes the form:
where:
a = peak value if mortality rate is zero
b = response at time zero
c = cell division rate
d = cell mortality rate
Peak Models
The bellshaped curve associated with a Normal distribution is more generically described by a Gaussian function of the form:
The Lorentzian curve is superficially similar to the Gaussian bellshape, but has heavier tails:
Peak curves are used, for example, to model spectroscopic peaks.
For both models the parameter a corresponds to the maximum value of the peak; b represents the growth rate, and c is the critical point: the value of x where the curve reaches its maximum value.
Pharmacokinetic Models
Pharmocokinetic models seek to describe the kinetics of a drug once it has been administered into the body. The One Compartment Oral Dose model has the following parameterisation:
where:
a = area under the curve
b = elimination rate
c = absorption rate
JMP also supports a Two Compartment IV Bolus Dose model, but that is beyond my latex skills!
MichaelisMenten Model
Named after the biochemists Leonor Michaelis (18751949) and Maud Menten (18791960), this model is used to describe enzyme kinetics:
The parameter a represents the maximum reaction rate (in literature often referred to as V_{max}), and the b parameter (in literature often referred to as the Michaelis constant K_{m}) is the value of x such that the response is half V_{max} ; it is an inverse measure of the substrates affinity for the enzyme.
And There Is More
We are not limited to selecting from a predefined library of curve types. Any nonlinear function can be expressed as a column formula and fitted using the Nonlinear Platform. In fact it is one of my most frequently used platforms. But that is a topic for another day.
]]>
http://www.pegaanalytics.co.uk/blog/curvefitting/feed/
2

Working with String Variables
http://www.pegaanalytics.co.uk/blog/workingwithstringvariables/
http://www.pegaanalytics.co.uk/blog/workingwithstringvariables/#comments
Fri, 24 Apr 2020 10:43:35 +0000
http://www.pegaanalytics.co.uk/blog/?p=2473
Continue reading Working with String Variables ]]>
In this post I take a look at how JSL can be used to perform string manipulation.
Constructing a String Variable
A string variable is created by enclosing the text within doublequotes.
str = "this is a text string"
The script editor will automatically colour code string variables as purple (this behaviour can be customised under preferences).
Escape Sequences
What if the text string itself contains the doublequote character? In this instance the quote character needs to be preceded by a special escape notation that indicates that the character is part of the string and not the delimiter of the string:
str = "my name is "\!"David\!""
If a string requires a large number of doublequotes the use of escape sequences becomes tedious and error prone. Fortunately JSL provides an alternative notation that can be used:
str = " . . . \[ . . . ]\ . . . "
Text within the square brackets can be written without the need to use escape sequences, for example:
str = "\[ my name is "David"]\"
Concatenating Strings
Often a string variable is constructed by adding multiple strings together. String addition is referred to as concatenation and is performing using the concatenation operator: .
myName = "David";
str = "My name is "  myName;
Converting to a String
In the above example the string variable str was constructed from a literal quoted string plus a second variable that contained a string. Sometimes the second variable is numeric, in which case it must be explicitly converted to a string. This can be performed using the Char function:
age = 25;
str = "My age is "  Char(age)  " – yeah, I wish!"
String Substitution
In the above example it was necessary to concatenate three elements in order to construct the string that had the age variable embedded in it. Another technique is to use string substitution.
We could write the above string with a placeholder for the age:
str = "My age is pAge – yeah, I wish!"
And now we can substitute the actual value for the placeholder:
str = SubstituteInto(str,char(age))
Note that the value being substituted needs to be a string, so in this example the Char function is still required to convert age from a number to a string.
Conditional Logic and String Searches
With numeric values it is often the case that we want to perform some form of conditional logic based on their value:
If (yield > 80,
status = "good"
,
status = "poor"
);
With string values we can also perform conditional logic:
If (status == "good",
colour = "green"
,
colour = "red"
);
Or equivalently, using the Match function:
Match(status,
"good", colour = "green",
"bad", colour = "red"
);
String comparisons are casesensitive. JMP has functions to transform case: lowercase, uppercase, and titlecase. So a more robust comparison would be:
Match(lowercase(status),
"good", colour = "green",
"bad", colour = "red"
);
String Length
The number of a characters in a text string can be determined using the Length function:
Example:
str = "Hello World";
nChars = Length(str); // nChars = 11
Extracting Content
To locate portions of text within a string, JMP provides a variety of functions, including word, words, left, right, and substr.
strWord = Word( n, string, <delimiter> )
The word function returns the n’th word within a string. The default delimiter for each word within the string is a space character. However, the optional delimiter field can be used to identify an alternate character to be used.
Example:
str = "Hello 'John'";
secondWord = word(2,str); // secondWord = "'John'"
strName = word(2,str,"'"); // str = "John"
lstWords = Words( string, <delimiter> )
Whereas the word function identifies a single specific word within a string, the words function returns all the individual words as items of a list:
If an empty string is specified as the delimiter then each character of the string is treated as a separate word.
Example 1: isolating individual characters
strName = "John";
lstChars = Words(strName,"");
// lstChars = {"J", "o", "h", "n"}
Example 2: counting the number of words
Number Of Words = Function({str},{default local},
nitems(words(str))
);
str = "this is a sentence";
n = Number Of Words(str); // n = 4
Example 3: determining the file extension of a file
filePath = “c:\documents\big class.jmp”;
w = Words(filePath,”.”);
filetype = w[nitems(w)]; // filetype = “jmp”
strLeftMostChars = Left( string, n, <filler> )
The left function can be used to extract the n leftmost characters of a string. An optional filler character can be specified for instances where the string may be less than n characters in length.
Similarly there is a right function to extract the n rightmost characters.
Example : determining whether a file is a JMP table
filePath = “c:\documents\big class.jmp”;
if (right(filePath,4)==".jmp",
isJmpTable = 1
,
isJmpTable = 0
);
strSubstring = Substr( string, offset, <count> )
The substr function returns part of the string composed of count characters starting at position offset.
Example:
str = "Hello 'John'";
strQuotedName = Word(2,str);
strName = Substr(strQuotedName,2,length(strQuotedName)2);
// str = "John"
Pattern Matching
Pattern matching in JSL provides an exceptionally powerful and flexible mechanism for searching and manipulating text strings. Central to pattern matching is the creation of variables that contain pattern definitions. These definitions are then processing by pattern matching functions. I will illustrate the principle based on a scenario that I am currently working on.
I have a column formula that contains a model of the form:
Where K1, K2, N1 and C are parameters that are estimated using the nonlinear platform. I want to inspect the formula for the column and retrieve the K2 value, which represents the activation energy for this kinetic equation.
I can retrieve the formula by sending the getFormula message to the column; this is what it looks like:
Parameter(
{K1 = 0.016232665, K2 = 683.35374,
N1 = 1.6719301, C =0.06863445},
K1 * Exp( K2 / :Temperature) * :RH ^ N1 * :Time + C
)
Notice that there is a pattern to how the parameter values are specified:
K2 = <k2_value>,
I can describe this pattern by defining a pattern matching variable:
pattern = "K2 = " + PatArb()>>k2_value + ", "
This pattern variable says “find K2 followed by an equals sign, then some arbitrary text, then a comma”. It also stores the arbitrary text in the variable k2_value. The value is arbitrary in that it doesn’t have a known value, but it is the value I am seeking.
Now I can apply the pattern to a string representation to the formula, check that I have a successful match, and if so then I can convert the string value of k2_value to a number:
fml = char( :Y << get formula );
pattern = "K2 = " + PatArb()>>k2_value + ",";
success = patMatch(fml,pattern);
if (success,
k2 = num(k2_value);
,
write("Failed to find k2")
);
// now k2 = 683.35374;
]]>
http://www.pegaanalytics.co.uk/blog/workingwithstringvariables/feed/
2