2.3 Expressions

Make calculations with expression blocks, process arrays and tensors, conditional expressions, JavaScript.

In [1]:
var iframer = require('./iframer')

Why expressions

An expression is any valid unit of code that resolves to a value. (source)

To make a model useful, data is not enough. It's usually transformed in different ways to get a result. Expressions in StatSim are blocks that describe such transformations. An expression block includes the name field that makes it possible to use expression output later in the model. The second field is the actual expression. It shows the expression itself.

Simple formulas

StatSim is based on WebPPL which is based on JavaScript. So almost any expression you can write in JavaScript, you can also write in StatSim. WebPPL adds its own list of helper functions so you can also use it with JS native functions. Examples of correct expressions include:

  • 10 + 1
  • x * (y - 5) where x and y are declared somewhere else
  • fn(x) where fn is a function

Math object

Sometimes basic arithmetic operators are not enough. If you want to use a mathematical constant (like PI or E) or a function (like sin, cos etc) you can call Math object in your expression: Math.PI Math.sin(5) Math.min(x,y) Full list of Math properties and methods find here: Math (MDN)

Example: Loan calculator

At the moment we know how to add input parameters to the model (data blocks) and set relationship between them (expressions). Let's make something a little more useful model than one that sums a and b. Our new model calculates loan monthly payments. Inputs are Principal (amount of borrowed money, dollars), Rate (annual percentage rate, percents) and Term (amount of time in which the loan will be repayed, months). We assume our loan is a fixed rate mortgage, with the corresponding formula for monthly payments:

$$MonthlyPayment = Principal \times \frac{Rate (1 + Rate)^{Term}}{(1 + Rate)^{Term} - 1}$$

To make use of the formula we should convert annual percentage rate in % to the monthly rate using the formula:

$$MonthlyRate = \frac{AnnualRate}{12 \times 100}$$

So, in our model we should have 3 data inputs and 2 expressions (the first one to calculate monthly rate, the second for the final output). Corresponding expressions are:

  • AnnualRate / (12 * 100)
  • Principal * MonthlyRate * Math.pow(1 + MonthlyRate, Term) / (Math.pow(1 + MonthlyRate, Term) - 1)

You can see that we use the Math object and its function pow(x,n) that raises x to the power of n. In our case x is 1 + MonthlyRate and n is Term. This expression is quite long and has repeating pattern. It's a good practice to separate such repeating blocks. So let's create one more expression that calculates value Math.pow(1 + MonthlyRate, Term) and call it Pow. Our final expression is

  • Principal * MonthlyRate * Pow / (Pow - 1)
In [2]:
iframer('https://statsim.com/app/?a=%5B%7B%22b%22%3A%5B%7B%22n%22%3A%22Principal%22%2C%22sh%22%3Afalse%2C%22t%22%3A2%2C%22u%22%3Atrue%2C%22dims%22%3A%22%22%2C%22v%22%3A%22200000%22%7D%2C%7B%22n%22%3A%22AnnualRate%22%2C%22sh%22%3Afalse%2C%22t%22%3A2%2C%22u%22%3Atrue%2C%22dims%22%3A%22%22%2C%22v%22%3A%229%22%7D%2C%7B%22n%22%3A%22Term%22%2C%22sh%22%3Afalse%2C%22t%22%3A2%2C%22u%22%3Atrue%2C%22dims%22%3A%22%22%2C%22v%22%3A%22360%22%7D%2C%7B%22n%22%3A%22MonthlyRate%22%2C%22h%22%3Afalse%2C%22sh%22%3Afalse%2C%22t%22%3A1%2C%22v%22%3A%22AnnualRate%20%2F%20%2812%20%2A%20100%29%22%7D%2C%7B%22n%22%3A%22Pow%22%2C%22h%22%3Afalse%2C%22sh%22%3Afalse%2C%22t%22%3A1%2C%22v%22%3A%22Math.pow%281%20%2B%20MonthlyRate%2C%20Term%29%22%7D%2C%7B%22n%22%3A%22Payment%22%2C%22h%22%3Afalse%2C%22sh%22%3Atrue%2C%22t%22%3A1%2C%22v%22%3A%22Principal%20%2A%20MonthlyRate%20%2A%20%20Pow%20%2F%20%28Pow%20-%201%29%22%7D%5D%2C%22mod%22%3A%7B%22n%22%3A%22Mortgage%20Calculator%22%2C%22e%22%3A%22Monthly%20payment%20calculator%20for%20a%20fixed%20rate%20mortgage.%22%2C%22s%22%3A1%2C%22m%22%3A%22deterministic%22%7D%2C%22met%22%3A%7B%22sm%22%3A1000%7D%7D%5D')

In this example, we created three data blocks as input parameters, and three expressions, where Payments is the model output (Show in output flag). We also use the Math.pow() function that takes two arguments and raises the first one to the power of second. You can also create your own functions.

Functions

You can create functions just as you created expressions. Choose a proper name and then instead of writing operators and regular method, type a special function template function (x,y) {return x * y}. In round brackets put function parameters (inputs), after return write the output expression. Then you can use that function in other blocks. Currently I'm not sure if it's a good idea to separate functions in a separate block type. What do you think?

Array expressions

When your input is not a scalar value like 5 or true but a list of values (array), you can't just use the same scalar operators and write expressions as a + b where a and b are arrays. To compose array expressions use built-in functions.

  • map(fn, arr)
  • mapN(fn, n)
  • reduce(fn, init, arr)
  • filter(predicate, arr)
  • sum(arr)
  • product(arr)
  • listMean(arr)
  • ...

I'll not list all the functions. Just check the reference here

Tensors

Find the needed reference here

Conditional expressions

Express should return value. That's why you can't use if,else,while and other JavaScript statements that don't return values when executed. Luckily there's a conditional expression that can be used to control what value is returned. It looks like this:

  • (condition) ? true_expression : false_expression

If condition is true the whole expression evaluates to true_expression otherwise to false_expression. Read about conditional (ternary) operator here

By Anton Zemlyansky in