Skip to content

Tentative NMatrix Tutorial

Will Levine edited this page Jul 7, 2015 · 29 revisions

Credit & Disclaimer

  • This tutorial is meant to mimic as closely as possible the Tentative NumPy Tutorial (as of August 2013) but for SciRuby's NMatrix. Because of this, examples and wording are used extensively from the Tentative Numpy Tutorial with little or no modification from the original. This is intentional, hereby disclosed, and hopefully justified given the nature of this project. The authors of this tutorial express deep appreciation to those responsible for the excellent Numpy and Tentative Numpy Tutorial. In addition, NMatrix was based heavily off of Masahiro Tanaka's excellent NArray work.

Prerequisites

Before reading this tutorial you should know a bit of Ruby. If you would like to refresh your memory, take a look at the Ruby quickstart tutorial, the tutorials at rubymonk, or tryruby. If you wish to work the examples in this tutorial, you must also have some software installed on your computer. Minimally:

You may find these useful for plotting:

Also, take a look at:

  • pry for an enhanced interactive shell (better than irb)
  • SciRuby(http://sciruby.com/) for additional scientific tools with growing NMatrix compatibility

Rules for Editing

  1. Follow the Tentative NumPy Tutorial as closely as possible.
  2. Aim to implement similar functionality, but in a ruby-ish way.
  3. Make a note on the wiki if something needs to be implemented to provide a particular functionality. Then, ideally, go and implement it and update the wiki.

The Basics

An NMatrix is a homogeneous multidimensional array. It is a table of elements (usually numbers), all of the same type, indexed by a tuple of positive integers. In NMatrix dimensions are called axes. The number of axes is dim. For example, the coordinates of a point in 3D space [1, 2, 1] is an array of dim 1, because it has one axis. That axis has a length of 3. In example pictured below, the ruby Array has dim 2 (it is 2-dimensional). The first dimension (axis) has a length of 2, the second dimension has a length of 3.

# an array of dim 1 -- it has only one axis
[1, 2, 1]          # plain ol' ruby Array
N[1, 2, 1]         # an NMatrix
# an array of dim 2 -- has 3 columns and 2 rows.
[[1, 0, 0],        # the plain ol' ruby Array of two arrays
[0, 1, 2]] 
N[[1, 0, 0], [0, 1, 2]]  # as an NMatrix

Key attributes of an NMatrix are:

NMatrix#dim
the number of axes (dimensions) of the array.

NMatrix#shape
A matrix with m number of rows and n number of columns will have a shape (m,n). The right-most number changes most rapidly while traversing the array (like Matlab, Numpy, and ruby Arrays of Arrays and opposite FORTRAN and PDL).

NMatrix#dtype
A symbol representing the type of array. (:byte, :int8, :int16, :int32, :int64, :float32, :float64, :complex64, :complex128, :object)

NMatrix#stype
The storage type of the array (:dense, :list, :yale). Dense is the default and yale is for working with sparse arrays. List is less frequently used.

[NOTE: .itemsize and .size have no real equivalent right now. Also, access to underlying data (.data in numpy) is not well-defined]

An example

You should be able to type in every code block in this document from here on and duplicate these results. Some output lines are omitted for brevity.

% irb --simple-prompt
>> require 'nmatrix'
=> true
>> a = NMatrix.seq([3,5])

>> a.shape
=> [3, 5]
>> a.dim
=> 2
>> a.class
=> NMatrix
>> a.dtype
=> :int32
>> a.stype
=> :dense
# [[no concept of itemsize or size right now]]

>> b = N[6, 7, 8]
>> #<NMatrix:0x007fec8d777780 shape:[3] dtype:int32 stype:dense>
>> b.class
=> NMatrix

If you are using pry you should see a pretty-printed matrix in the output. If you are using irb, you can print out the matrix using pp:

>> require 'pp'
>> pp a
[
  [ 0,  1,  2,  3,  4]   [ 5,  6,  7,  8,  9]   [10, 11, 12, 13, 14] ]
>> pp b
[6, 7, 8]

NMatrix Creation

There are several ways to create NMatrix objects.

>> a = N[[2,3,4]]
>> a.dtype
=> :int32
>> b = N[[1.2, 3.5, 5.1]]
>> b.dtype
=> :float64
>> c = N[[2],[3],[4]] # same result as N[[2,3,4]].transpose

The simplest is using the N[ ] method. It understands arrays of arrays and will correctly deduce the correct type of the NMatrix (based on the element with highest typecode).

>> b = NMatrix[ [1.5, 2, 3], [4, 5, 6] ] 

Note that even when creating vectors, the number of opening brackets controls the dimensionality. A vector created with N[1,2,3] will have no orientation; but N[ [1,2,3] ] will be a row vector, and N[ [1],[2],[3] ] will be a column vector.

Printing Arrays

Basic Operations

Arithmetic operations on NMatrix objects apply elementwise. A new NMatrix is created and filled with the result.

 >> a = N[20.0,30.0,40.0,50.0]
 => [20.0, 30.0, 40.0, 50.0 shape:[4] dtype:float64 stype:dense>
 >> b = N.seq [4]
 => [0, 1, 2, 3 shape:[4] dtype:int32 stype:dense> 
 >> c = a-b
 => [20.0, 29.0, 38.0, 47.0 shape:[4] dtype:float64 stype:dense>
 >> b**2
 => [0, 1, 4, 9 shape:[4] dtype:int32 stype:dense>
 >> a.map{|e| Math.sin(e)} * 10
 => [9.129452507276277, -9.880316240928618, 7.451131604793488, -2.6237485370392877 shape:[4] dtype:float64 stype:dense>
 >> a < 35
 => [true, true, false, false shape:[4] dtype:object stype:dense> 

Unlike in many matrix languages, the product operator * operates elementwise on NMatrix objects. The matrix product can be performed using the dot function.

 >> a = N[[1,1], [0,1]]
 >> b = N[[2,0], [3,4]]
 >> a*b
 => [2, 0, 0, 4 shape:[2,2] dtype:int32 stype:dense>
 >> a.dot b
 => [5, 4, 3, 4 shape:[2,2] dtype:int32 stype:dense> 

TODO: +=-like operators

When operating with NMatrix objects of different types, the type of the resulting array corresponds to the more general or precise one (a behavior known as upcasting).

 >> a = NVector.ones(3, :int32)
 >> b = NVector.linspace(0, Math::PI, 3)
 >> b.dtype
 => :float64
 >> c = a+b
 => [1.0, 2.5707963267948966, 4.141592653589793 shape:[1,3] dtype:float64 stype:dense>
 >> d = c*Complex(0,1)
 => [(0.0+1.0i), (0.0+2.5707963267948966i), (0.0+4.141592653589793i) shape:[1,3] dtype:complex128 stype:dense>

Many unary options, such as computing the sum of all the elements in the NMatrix, are implemented as methods of the NMatrix class.

 >> a = N.random([2,3])
 => [0.5441637193570046, 0.8916235693579796, 0.7244554237682101, 0.6617806999277982, 0.5792396150088834, 0.7118546436543527 shape:[2,3] dtype:float64 stype:dense
 >> a.sum
 => [1.2059444192848028, 1.4708631843668631, 1.4363100674225628 shape:[1,3] dtype:float64 stype:dense> 
 >> a.min
 => [0.5441637193570046, 0.5792396150088834, 0.7118546436543527 shape:[1,3] dtype:float64 stype:dense> 
 >> a.max
 => [0.6617806999277982, 0.8916235693579796, 0.7244554237682101 shape:[1,3] dtype:float64 stype:dense> 

By default, these operations are applied over the first dimension of the NMatrix. However, by specifying an optional parameter, you can apply an operation over the specified dimension of an NMatrix:

 >> b = N.indgen([3,4])
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 shape:[3,4] dtype:int32 stype:dense> 
 >> b.sum(0)
 => [12, 15, 18, 21 shape:[1,4] dtype:int32 stype:dense> 
 >> b.sum(1)
 => [6, 22, 38 shape:[3,1] dtype:int32 stype:dense> 

Universal Methods

Indexing, Slicing and Iterating

Shape Manipulation

Changing the shape of an array

Stacking together different arrays

Splitting one array into several smaller ones

Copies and Views

No Copy at All

View or Shallow Copy

Deep Copy

Functions and Methods Overview

Less Basic

Broadcasting rules

Fancy indexing and index tricks

Indexing with Arrays of Indices

Indexing with Boolean Arrays

The ix_() function

Indexing with strings

Linear Algebra

Simple Array Operations

Indexing: Comparing Matrices and 2D Arrays

Tricks and Tips

"Automatic" Reshaping

Vector Stacking

Histograms

References

[Work in progress]

Clone this wiki locally