Using infix formulas in Incanter

There's a lot to like about lisp: macros, the simple syntax, and the rapid development cycle. Most of the time, it is fine if you treat math operators as functions and use prefix notations, which is a consistent, function-first syntax. This allows you to treat math operators in the same way as everything else so that you can pass them to reduce, or anything else you want to do.

However, we're not taught to read math expressions using prefix notations (with the operator first). And especially when formulas get even a little complicated, tracing out exactly what's happening can get hairy.

Getting ready

For this recipe we'll just need Incanter in our project.clj file, so we'll use the dependencies statement—as well as the use statement—from the Loading Clojure data structures into datasets recipe.

For data, we'll use the matrix that we created in the Converting datasets to matrices recipe.

How to do it…

Incanter has a macro that converts a standard math notation to a lisp notation. We'll explore that in this recipe:

  1. The $= macro changes its contents to use an infix notation, which is what we're used to from math class:
    user=> ($= 7 * 4)
    28
    user=> ($= 7 * 4 + 3)
    31
  2. We can also work on whole matrixes or just parts of matrixes. In this example, we perform a scalar multiplication of the matrix:
    user=> ($= va-matrix * 4)
     A 591x3 matrix
     ---------------
     3.28e+04  1.71e+04  8.22e+03
     2.08e+03  9.16e+02  4.68e+02
     1.19e+03  6.52e+02  3.08e+02
     ...
     1.41e+03  7.32e+02  3.72e+02
     1.31e+04  6.64e+03  3.49e+03
     3.02e+04  9.60e+03  6.90e+03 
    user=> ($= (first va-matrix) * 4)
     A 1x3 matrix
     -------------
     3.28e+04  1.71e+04  8.22e+03
  3. Using this, we can build complex expressions, such as this expression that takes the mean of the values in the first row of the matrix:
    user=> ($= (sum (first va-matrix)) /
               (count (first va-matrix)))
    4839.333333333333
  4. Or we can build expressions take the mean of each column, as follows:
    user=> ($= (reduce plus va-matrix) / (count va-matrix))
     A 1x3 matrix
     -------------
     9.19e+03  3.83e+03  2.25e+03

How it works…

Any time you're working with macros and you wonder how they work, you can always get at their output expressions easily, so you can see what the computer is actually executing. The tool to do this is macroexpand-1. This expands the macro one step and returns the result. It's sibling function, macroexpand, expands the expression until there is no macro expression left. Usually, this is more than we want, so we just use macroexpand-1.

Let's see what these macros expand into:

user=> (macroexpand-1 '($= 7 * 4))
(incanter.core/mult 7 4)
user=> (macroexpand-1 '($= 7 * 4 + 3))
(incanter.core/plus (incanter.core/mult 7 4) 3)
user=> (macroexpand-1 '($= 3 + 7 * 4))
(incanter.core/plus 3 (incanter.core/mult 7 4))

Here, we can see that the expression doesn't expand into Clojure's * or + functions, but it uses Incanter's matrix functions, mult and plus, instead. This allows it to handle a variety of input types, including matrices, intelligently.

Otherwise, it switches around the expressions the way we'd expect. Also, we can see by comparing the last two lines of code that it even handles operator precedence correctly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.194.106