# Matrices

violahs edited this page Mar 22, 2014 · 22 revisions

## Creating Matrices

The `matrix` function returns an instance of an incanter.Matrix, which is an
extension of clatrix.core.Matrix (and no longer
cern.colt.matrix.tdouble.impl.DenseColDoubleMatrix2D). It implements the Clojure
interface clojure.lang.ISeq. Therefore Clojure sequence operations can be applied to
matrices. A matrix consists of a sequence of rows, where each row is a
one-dimensional row matrix. One-dimensional matrices are, in turn, sequences of
numbers. Equivalent to R’s matrix function.

To create a matrix, either pass a Clojure sequence (e.g. vector or list) of
sequences to the matrix function:

``(def A (matrix [[1 2 3] [4 5 6] [7 8 9]])) ; produces a 3x3 matrix``

Or pass a ‘one-dimensional’ sequence along with the number of desired columns:

``(def A2 (matrix [1 2 3 4 5 6 7 8 9] 3)) ; produces the same 3x3 matrix``

Passing a sequence without the `ncol` argument will produce a column vector:

``(def B (matrix [1 2 3 4 5 6 7 8 9])) ; produces a 9x1 column vector``

To create a matrix initialized with a single value, pass the initial value, the number of rows,
and the number of columns to the `matrix` function:

``(matrix 0 3 4) ; produces 3x4 matrix of zeros``

To create an identity matrix, use the `identity-matrix` function:

``(identity-matrix 4) ; produces 4x4 identity matrix``

To create a diagonal matrix, pass a sequence of values for the diagonal to the `diag` function:

``(diag [1 2 3 4]) ; produces 4x4 matrix with [1 2 3 4] along the diagonal``

And to extract the diagonal elements of a matrix, pass it to the diag function:
``(diag A) ; produces the sequence (1.0 5.0 9.0)``

To create a symmetric matrix, pass a sequence of values representing the lower-triangular elements
of the matrix (in row-major order) to the `symmetric-matrix` function:

``````
(symmetric-matrix
[1
2 3
4 5 6
7 8 9 10])``````

## Matrix Operations

Incanter includes ‘vectorized’ versions of standard math operations that operate on numbers,
Clojure sequences, and Incanter matrices, including:
`plus minus mult div abs exp sqrt pow cos acos sin asin tan atan sum prod`.

To add two (or more) matrices together element-by-element use the `plus` function:

``(plus A A2)``

To add a scalar to a matrix:

``(plus 3 A)``

Incanter also includes standard matrix operations, including `mmult` (matrix multiplication),
`solve`, `trans`, `det`, `trace` and matrix decomposition:
`decomp-cholesky decomp-eigenvalue decomp-lu decomp-qr decomp-svd`

## Manipulating Matrices

The incanter.Matrix class implements Clojure’s ISeq interface, so operations that work on built-in
sequences also work on matrices, include `first`, `rest`, `map`, `reduce`, `filter`, etc.
A two-dimensional matrix is a sequence of rows, which are in turn a sequence of doubles.

``````(first A) ; produces a row matrix [1 2 3]
(rest A) ; produces a sub matrix [[4 5 6] [7 8 9]]
(first (first A)) ; produces 1.0
(rest (first A)) ; produces a row matrix [2 3]``````

Since `(plus row1 row2)` adds the two rows element-by-element,
`(reduce plus A)` produces the sums of the columns.

And since `(sum row1)` sums the elements of the row,
`(map sum A)` produces the sums of the rows.

Clojure’s filter function can be used to filter rows:

``(filter #(> (nth % 1) 4) A) ; returns the rows where the second column is greater than 4.``

## Selecting Subsets of Matrices

Start by loading one of the included data sets from the `datasets` library:

``````(use 'incanter.datasets)
(def speed (to-matrix (get-dataset :speed)))``````

Select the element in the first column, first row:

``(sel speed 0 0)``

or
``(sel speed :rows 0 :cols 0)``

Select the first column of data:

``(sel speed :cols 0)``

Select multiple columns or rows:

``````(sel speed :cols [0 2]) ; first and third column of all rows
(sel speed :rows (range 10) :cols (range 2)) ; first two rows of the first 2 columns
(sel speed :rows (range 10)) ; all columns of the first 10 rows``````

Exclude one or more columns or rows:

``````(sel speed :except-cols 1) ; all columns except the second
(sel speed :except-rows (range 10)) ; all but the first 10 rows``````

Use the filter option to return the rows where the distance (third column) is greater than 50:

``(sel speed :filter #(> (nth % 2) 50))``

Or only the first 10 even rows:

``(sel speed :rows (range 10) :filter #(even? (int (nth % 0))))``

## Destructuring Matrices

Matrices can be destructured by rows. The following returns a hashmap with entries for each row:

``````(let '[[row1 row2 row3] A]
{:r1 row1 :r2 row2 :r3 row3})``````

To destructure by columns, transpose the matrix:

``````(let '[[col1 col2 col3] (trans A)]
{:x1 col1 :x2 col2 :x3 col3})``````

## Destructuring Matrices by Group

The `group-on` function will take a matrix and one or more column indices and return a collection of sub-matrices based on the value(s) of those columns. For instance the following code will load the sample plant-growth data set, and take the mean of the first column (using the `:cols` option in `group-on`) for each of the three sub-group defined by the value of column two (the group variable).

``````(use '(incanter core stats datasets))
(def plant-growth (to-matrix (get-dataset :plant-growth)))
(map mean (group-on plant-growth 1 :cols 0)) ;; this returns (5.032 4.661 5.526) ``````

Multiple columns can be used to define the groups. For instance if we use dummy-variables to encode the treatment groups in the plant-growth data set, we will need to use the last two indicator columns to define the groupings.

``````(def plant-growth-dummies (to-matrix (get-dataset :plant-growth) :dummies true))
(map mean (group-on plant-growth-dummies [1 2] :cols 0))
;; this returns (5.032 5.526 4.661)``````

Notice that the order of the means is not the same as before. The groups are sorted based on the value of the grouping variable ONLY when a single column value is used to define them, otherwise the order of the returned groups cannot be relied upon.

We can also use `group-on` in a destructuring bind (this is best done when using a single column to define the groupings so that the order of the groups will match the binding variables).

``````;; plot the plant groups
(use 'incanter.charts)
(let [[ctrl trt1 trt2] (group-on plant-growth 1 :cols 0)]
(doto (box-plot ctrl)
(add-box-plot trt1)
(add-box-plot trt2)
view))``````

## References

For further information on using matrices in Incanter see:

##### Clone this wiki locally
You can’t perform that action at this time.
Press h to open a hovercard with more details.