Quantcast
Viewing all articles
Browse latest Browse all 21

Infix mathematical notation in Incanter

I obviously love Clojure’s prefix notation, but there are times when it is more concise, and familiar, to represent mathematical formulae using infix notation, so I have integrated the infix portion of Jeffrey Bester’s Clojure math library into Incanter. There is now a formula macro called $= for evaluating infix mathematical formulas in incanter.core.

Here’s an example,

user> (use 'incanter.core)
nil
user> ($= 7 + 8 - 2 * 6 / 2)
9

Note that there must be spaces between values and operators.

Vectors can be used instead of scalars. For instance, to add 5 to each element of the vector [1 2 3],

user> ($= [1 2 3] + 5)
(6 7 8)

or perform element-by-element arithmetic on vectors and matrices.

user> ($= [1 2 3] + [4 5 6])
(5 7 9)

user> ($= [1 2 3] * [1 2 3])
(1 4 9)

user> ($= [1 2 3] / [1 2 3])
(1 1 1)

user> ($= (matrix [[1 2] [4 5]]) + 6)
[ 7.0000  8.0000
10.0000 11.0000]

user> ($= (trans [[1 2] [4 5]]) + 6)
[7.0000 10.0000
8.0000 11.0000]

Examples of exponents using the ** function.

user> ($= 8 ** 3)
512.0

user> ($= 8 ** 1/2)
2.8284271247461903

user> ($= 2 ** -2)
0.25

user> ($= [1 2 3] ** 2)
(1.0 4.0 9.0)

Parens can be used for grouping,

user> ($= 10 + 20 * (4 - 5) / 6)
20/3

user> ($= (10 + 20) * 4 - 5 / 6)
715/6

user> ($= 10 + 20 * (4 - 5 / 6))
220/3

including arbitrarily nested groupings.

user> ($= ((((5 + 4) * 5))))
45

Of course, variables can be used within the formula macro,

user> (let [x 10
            y -5]
        ($= x + y / -10))
21/2

and mathematical functions like sin, cos, tan, sq, sqrt, etc. can be used with standard Clojure prefix notation.

user> ($= (sqrt 5) * 5 + 3 * 3)
20.18033988749895

user> ($= sq [1 2 3] + [1 2 3])
(2 6 12)

user> ($= sin 2 * Math/PI * 2)
5.713284232087328

user> ($= (cos 0) * 10)
10.0

user> ($= (tan 2) * Math/PI * 10)
-68.64505182223235

user> ($= (asin 1/2) * 10)
5.23598775598299

user> ($= (acos 1/2) * 10)
10.47197551196598

user> ($= (atan 1/2) * 10)
4.636476090008061

Functions can also be applied to vectors,

user> ($= [1 2 3] / (sq [1 2 3]) + [5 6 7])
(6 13/2 22/3)

user> ($= [1 2 3] + (sin [4 5 6]))
(0.2431975046920718 1.0410757253368614 2.720584501801074)

Boolean tests are also supported.

user> ($= 3 > (5 * 2/7))
true

user> ($= 3 <= (5 * 2/7))
false

user> ($= 3 != (5 * 2/7))
true

user> ($= 3 == (5 * 2/7))
false

user> ($= 3 != 8 && 6 < 12)
true

user> ($= 3 != 8 || 6 > 12)
true

Matrix multiplication uses the <*> function (equivalent to R’s %*% operator).

user> ($= [1 2 3] <*> (trans [1 2 3]))
[1.0000 2.0000 3.0000
2.0000 4.0000 6.0000
3.0000 6.0000 9.0000]

user> ($= (trans [[1 2] [4 5]]) <*> (matrix [[1 2] [4 5]]))
[17.0000 22.0000
22.0000 29.0000]

user> ($= (trans [1 2 3 4]) <*> [1 2 3 4])
30.0

user> ($= [1 2 3 4] <*> (trans [1 2 3 4]))
[1.0000 2.0000  3.0000  4.0000
2.0000 4.0000  6.0000  8.0000
3.0000 6.0000  9.0000 12.0000
4.0000 8.0000 12.0000 16.0000]

The Kronecker product uses the <x> function (equivalent to R’s %x% operator).

user> ($= [1 2 3] <x> [1 2 3])
[1.0000
2.0000
3.0000
2.0000
4.0000
6.0000
3.0000
6.0000
9.0000]

user> ($= (matrix [[1 2] [3 4] [5 6]]) <x> 4)
[ 4.0000  8.0000
12.0000 16.0000
20.0000 24.0000]

user> ($= (matrix [[1 2] [3 4] [5 6]]) <x> (matrix [[1 2] [3 4]]))
[ 1.0000  2.0000  2.0000  4.0000
 3.0000  4.0000  6.0000  8.0000
 3.0000  6.0000  4.0000  8.0000
 9.0000 12.0000 12.0000 16.0000
 5.0000 10.0000  6.0000 12.0000
15.0000 20.0000 18.0000 24.0000]

user> ($= [1 2 3 4] <x> 4)
[ 4.0000
 8.0000
12.0000
16.0000]

user> ($= [1 2 3 4] <x> (trans [1 2 3 4]))
[1.0000 2.0000  3.0000  4.0000
2.0000 4.0000  6.0000  8.0000
3.0000 6.0000  9.0000 12.0000
4.0000 8.0000 12.0000 16.0000]

Here’s an example using the formula macro in a function-plot of Image may be NSFW.
Clik here to view.
x^3 - 5x^2 + 3x +5
, (I will use the incanter.latex/add-latex function to add a LaTeX annotation to the chart).

(use '(incanter core charts latex))
(doto (function-plot (fn [x] ($= x ** 3 - 5 * x ** 2 + 3 * x + 5)) -10 10)
  (add-latex 0 250 "x^3 - 5x^2 + 3x +5")
  view)

Image may be NSFW.
Clik here to view.

The next example will use the car-speed-to-breaking-distance sample data (the :cars dataset). I’ll partition the data, first selecting rows where the ratio of dist/speed is less than 2, then selecting rows where the ratio is greater than 2, and finally adding a line showing this threshold.

First, use the with-data macro, passing it the :cars sample data. Then apply the $where function in order to filter the data; pass it a $fn, which is a bit of syntax sugar around Clojure’s fn function that does map destructuring for functions that operate on dataset rows. For instance

($fn [x y] (…))

becomes

(fn [{:keys x y}] (…))

The parameter names specified in $fn must correspond to the column names of the dataset.

(use '(incanter core datasets charts))
(with-data (get-dataset :cars)
  (doto (scatter-plot :speed :dist 
          :data ($where ($fn [speed dist] 
                          ($= dist / speed < 2))))
    (add-points :speed :dist 
      :data ($where ($fn [speed dist] 
                      ($= dist / speed >= 2))))
    (add-lines ($ :speed) ($= 2 * ($ :speed)))
    view))

Image may be NSFW.
Clik here to view.

The complete code for this post is available here.


Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.

Viewing all articles
Browse latest Browse all 21

Trending Articles