├── README.md ├── examples └── index.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # scijs/ndarray for MATLAB users 2 | 3 | > Common [scijs/ndarray](https://github.com/scijs/ndarray) operations for people familar with MATLAB (or at least not familiar with [scijs](https://github.com/scijs)) 4 | 5 | ## Introduction 6 | 7 | This document is a work in progress! Inspired by [Numpy for Matlab users](https://docs.scipy.org/doc/numpy-dev/user/numpy-for-matlab-users.html), it aspires to be [NumPy for MATLAB users](http://mathesaurus.sourceforge.net/matlab-numpy.html). The intent is both to communicate what is possible in JavaScript using scijs and to illuminate which parts still need work. I'll be mostly offline until the new year so I may not respond immediately, but comments, question and pull requests are more than welcome. 8 | 9 | ## Memory Management 10 | 11 | First things first, ndarrays are similar to but different to work with than MATLAB arrays. To get a sense for how managing ndarrays differs from managing MATLAB arrays, consider the diagonal of a 5×5 matrix: 12 | 13 | ```javascript 14 | var pool = require('ndarray-scratch') 15 | var diag = require('ndarray-diagonal') 16 | 17 | // A 5x5 matrix of ones: 18 | var a = pool.ones([5,5]) 19 | 20 | // A view of the diagonal of a: 21 | var x = diag(a) 22 | ``` 23 | 24 | Even though they have different dimensionality and shape, the internal data contained in ndarray `x` is identical by reference to that in `a`. The difference is that the strides and offsets of `x` are set to select only the diagonal elements of `a`. To see this directly, note the data of `x` still has 25 numbers even though it's shape is a vector of length 5: 25 | 26 | ```javascript 27 | console.log(x) 28 | // => View1dfloat64 { 29 | // data: 30 | // Float64Array {'0': 1, '1': 1, '2': 1, ..., '22': 1, '23': 1, '24': 1 }, 31 | // shape: [ 5 ], 32 | // stride: [ 6 ], 33 | // offset: 0 } 34 | ``` 35 | 36 | Cloning simplifies the representation by allocating new storage and copying only the elements exposed by view `x`: 37 | 38 | ```javascript 39 | console.log(pool.clone(x)) 40 | // => View1dfloat64 { 41 | // data: Float64Array { '0': 1, '1': 1, '2': 1, '3': 1, '4': 1 }, 42 | // shape: [ 5 ], 43 | // stride: [ 1 ], 44 | // offset: 0 } 45 | ``` 46 | 47 | As a result, a nice advantage of ndarrays the ability to manipulate representations without the need to iterate directly or allocate additional storage (beyond the lightweight ndarray wrapper). The example below uses in-place operations of [ndarray-ops](https://github.com/scijs/ndarray-ops) to assign the scalar 3 to the diagonal and double the first two columns of `a`: 48 | 49 | ```javascript 50 | var ops = require('ndarray-ops') 51 | var show = require('ndarray-show') 52 | 53 | // Set each element of the diagonal of a (5x5 matrix of ones) to 3: 54 | ops.assigns(diag(a, 3)) 55 | 56 | // Double the first two columns: 57 | ops.mulseq(a.hi(null,2)) 58 | 59 | console.log(show(a)) 60 | // => 6.000 2.000 1.000 1.000 1.000 61 | // 2.000 6.000 1.000 1.000 1.000 62 | // 2.000 2.000 3.000 1.000 1.000 63 | // 2.000 2.000 1.000 3.000 1.000 64 | // 2.000 2.000 1.000 1.000 3.000 65 | ``` 66 | 67 | ## Operations 68 | 69 | The table below collects common matlab operations as well as their ndarray analogs. Not all operations have a counterpart, some because of features and shortcomings of the JavaScript language, some because of differences in memory management, and some because they're simply not yet implemented. 70 | 71 | MATLAB | JavaScript | Notes 72 | :-----------------|:--------------------|:--------------- 73 | `ndims(a)` | [`a.dimension`](https://github.com/scijs/ndarray#arraydimension) | get the number of dimensions of `a` 74 | `numel(a)` | [`a.size`](https://github.com/scijs/ndarray#arraysize) | get the number of elements of an arary 75 | `size(a)` | [`a.shape`](https://github.com/scijs/ndarray#members) | get the size of the array 76 | `size(a,n)` | [`a.shape`](https://github.com/scijs/ndarray#members)`[n-1]` | get the number of elements of the n-th dimension of array `a` 77 | `[1 2 3; 4 5 6 ]` | [`ndarray`](https://github.com/scijs/ndarray#constructor)`([1,2,3,4,5,6],[2,3])` | 2×3 matrix literal (using `Array` type) 78 |   | [`ndarray`](https://github.com/scijs/ndarray#constructor)`(new Float64Array([1,2,3,4,5,6]),[2,3])` | 2×3 matrix literal (using 64-bit typed array) 79 |   | [`pack`](https://github.com/scijs/ndarray-pack)`([[1,2,3],[4,5,6]])` | 2×3 matrix literal from nested array 80 | `a(end)` | [`a.get`](https://github.com/scijs/ndarray#arraygetij)`(a.shape[0]-1)` | access last element in the 1×n matrix `a` 81 | `a(2, 5)` | [`a.get`](https://github.com/scijs/ndarray#arraygetij)`(1, 4)` | access element in second row, fifth column 82 | `a(2, :)` | [`a.pick`](https://github.com/scijs/ndarray#arraypickp0-p1-)`(1, null)` | entire second row of `a` 83 | `a(1:5, :)` | [`a.hi`](https://github.com/scijs/ndarray#arrayhiijk)`(5, null)` | the first five rows of `a` 84 | `a(end-4:end, :)` | [`a.lo`](https://github.com/scijs/ndarray#arrayloijk)`(a.shape[0]-5, null)` | the last five rows of `a` 85 | `a(1:3, 5:9)` | [`a.hi`](https://github.com/scijs/ndarray#arrayhiijk)`(3, 9).lo(0, 4)` | rows one to three and columns five to nine of `a` 86 | `a([2, 4, 5], [1, 3])`| | rows 2, 4, and 5 and columns 1 and 3. 87 | `a(3:2:21, :)` | [`a.hi`](https://github.com/scijs/ndarray#arrayhiijk)`(21, null).`[`lo`](https://github.com/scijs/ndarray#arrayloijk)`(2, null).`[`step`](https://github.com/scijs/ndarray#arraystepijk)`(2, 1)` | every other row of `a`, starting with the third and going to the twenty-first 88 | `a(1:2:end, :)` | [`a.step`](https://github.com/scijs/ndarray#arraystepijk)`(2, 1)` | every other row of `a`, starting with the first 89 | `a(end:-1:1, :)` or `flipup(a)` | [`a.step`](https://github.com/scijs/ndarray#arraystepijk)`(-1, 1)` | `a` with rows in reverse order 90 | `a([1:end 1], :)` | | `a` with copy of the first rows appended to the end 91 | `a.'` | [`a.transpose`](https://github.com/scijs/ndarray#arraytransposep0-p1-)`(1, 0)` | transpose of `a` 92 | `a'` | | conjugate transpose of `a` 93 | `c = a * b` | [`gemm`](https://github.com/scijs/ndarray-gemm)`(c, a, b)`| matrix multiply 94 | `c = a + b` | [`ops.add`](https://github.com/scijs/ndarray-ops)`(c, a, b)` | matrix addition 95 | `c = a + 2` | [`ops.adds`](https://github.com/scijs/ndarray-ops)`(c, a, 2)` | matrix + scalar addition 96 | `a += b` (not available in MATLAB) | [`ops.addeq`](https://github.com/scijs/ndarray-ops)`(a, b)` | in-place matrix addition 97 | `c = a .* b` | [`ops.mul`](https://github.com/scijs/ndarray-ops)`(c, a, b)` | element-wise multiply 98 | `a = a .* b` | [`ops.muleq`](https://github.com/scijs/ndarray-ops)`(a, b)` | element-wise multiply (in-place) 99 | `c = a ./ b` | [`ops.div`](https://github.com/scijs/ndarray-ops)`(c, a, b) ` | element-wise division 100 | `a = a ./ b` | [`ops.diveq`](https://github.com/scijs/ndarray-ops)`(a, b)` | element-wise division (in-place) 101 | `a.^3` | [`ops.pows`](https://github.com/scijs/ndarray-ops)`(a, 3)` | element-wise scalar exponentiation 102 | `(a>0.5)` | | matrix whose i,jth element is (a\_ij > 0.5) 103 | `find(a>0.5)` | | find the indices where (a > 0.5) 104 | `a(:, find(v>0.5))`| | extract the columns of a where vector v > 0.5 105 | `a(a<0.5)=0` | | `a` with elements less than 0.5 zeroed out 106 | `a .* (a>0.5)` | | `a` with elements less than 0.5 zeroed out 107 | `a(:) = 3` | [`ops.assigns`](https://github.com/scijs/ndarray-ops)`(a, 3)` | set all values to the same scalar value 108 | `y = x` | `y =`[`pool.clone`](https://www.npmjs.com/package/ndarray-scratch#pool-clone-array)`(x)` | clone by value 109 | `y = x(2, :)` | `y = x.pick(1, null)`| slices are by reference 110 | `1:10` | | create an increasing vector 111 | `0:9` | | create an increasing vector 112 | `zeros(3, 4)` | [`pool.zeros`](https://github.com/scijs/ndarray-scratch#poolzerosshapedtype)`([3, 4], 'float64')` | 3×4 rand-2 array full of 64-bit floating point zeros 113 | `zeros(3, 4, 5)` | [`pool.zeros`](https://github.com/scijs/ndarray-scratchpoolzerosshapedtype)`([3, 4, 5], 'float64')` | 3×4×5 rank-3 array full of 64-bit floating point zeros 114 | `ones(3, 4)` | [`pool.ones`](https://github.com/scijs/ndarray-scratch#poolonesshapedtype)`([3, 4], 'float64')` | 3×4 rank-2 array full of 64-bit floating point ones 115 | `eye(3)` | [`pool.eye`](https://github.com/scijs/ndarray-scratch#pooleyeshapedtype)`([3, 3], 'float64')` | 3×3 identity matrix with 64-bit floating point precision 116 | `diag(a)` | [`diag`](https://github.com/scijs/ndarray-diagonal)`(a)`| vector of diagonal elements of `a` (returns diagonal by reference) 117 | `diag(a, 0)` | `b = `[`pool.zeros`](https://github.com/scijs/ndarray-scratch)`(a.shape)`
[`ops.assign`](https://github.com/scijs/ndarray-ops)`(`[`diag`](https://github.com/scijs/ndarray-diagonal)`(b), `[`diag`](https://github.com/scijs/ndarray-diagonal)`(a))` | square diagonal matrix whose nonzero values are the elements of a 118 | `rand(3, 4)` | [`fill`](https://www.npmjs.com/package/ndarray-fill)`(`[`pool.zeros`](https://github.com/scijs/ndarray-scratch#poolmallocshape-dtype)`([3, 4]), Math.random)` | random 3×4 matrix 119 | `linspace(1, 3, 4)` | [`linspace`](https://github.com/scijs/ndarray-linspace)`(1, 3, 4)` | 4 equally spaced samples between 1 and 3, inclusive 120 | `[x, y] = meshgrid(0:8, 0:5)` | | two 2D arrays: one of x values, one of y values 121 | `[x, y] = meshgrid([1, 2, 4], [2, 4, 5])` | | 122 | `repmat(a, m, n)` | [`tile`](https://github.com/scijs/ndarray-tile)`(a, [m, n])`| create m×n copies of `a` 123 | `[a b]` | [`concatCols`](https://github.com/scijs/ndarray-concat-cols)`([a, b])` | concatenate columns of `a` and `b` 124 | `[a; b]` | [`concatRows`](https://github.com/scijs/ndarray-concat-rows)`([a, b])` | concatenate rows of `a` and `b` 125 | `max(max(a))` | | maximum element of `a` 126 | `max(a)` | [`ops.max`](https://github.com/scijs/ndarray-ops#map-reduce-aggregate-operators)`(a)` | maximum element in `a` 127 | `norm(v)` | [`ops.norm2`](https://github.com/scijs/ndarray-ops#map-reduce-aggregate-operators)`(v)` | L2 norm of vector `v` 128 | `c = a & b` | [`ops.band`](https://github.com/scijs/ndarray-ops#conventions)`(c, a, b)` | element-by-element AND operator 129 | c = a | b | [`ops.bor`](https://github.com/scijs/ndarray-ops#conventions)`(c, a, b)` | element-by-element OR operator 130 | `inv(a)` | | inverse of square matrix `a` 131 | `pinv(a)` | | pseudo-inverse of matrix `a` 132 | `rank(a)` | | rank of matrix `a` 133 | `a\b` | [`lup`](https://github.com/scijs/ndarray-lup-factorization)`(a, a, P)`
[`solve`](https://github.com/scijs/ndarray-lup-solve)`(a, a, P, b)` | solution of `a x = b` for `x` 134 | `b/a` | | solution of `x a = b` for `x` 135 | `chol(a)` | [`chol`](https://github.com/scijs/ndarray-cholesky-factorization)`(a, L)` | cholesky factorization of matrix 136 | `[V, D] = eig(a)` | | eigenvalues and eigenvectors of `a` 137 | `[V, D] = eig(a, b)` | | eigenvalues and eigenvectors of `a`, `b` 138 | `[Q, R, P] = qr(a, 0)` | [`qr.factor`](https://github.com/scijs/ndarray-householder-qr#usage)`(A, d)`
[`qr.constructQ`](https://github.com/scijs/ndarray-householder-qr#usage)`(A, Q)` | QR decomposition. (Depending on the use, you can likely use Q without constructing explicitly. [See documentation](https://github.com/scijs/ndarray-householder-qr#usage).) 139 | `[L, U, P] = lu(a)` | [`lup`](https://github.com/scijs/ndarray-lup-factorization#requirendarray-lup-factorization-a-l-p-)`(A, L, P)` | LU decomposition 140 | `fft(a)` | [`fft`](https://github.com/scijs/ndarray-fft#requirendarray-fftdir-x-y)`(1, ar, ai)` | Fourier transform of `a`. Javascript does not have a complex type so real and imaginary parts must be passed separately. 141 | `ifft(a)` | [`fft`](https://github.com/scijs/ndarray-fft#requirendarray-fftdir-x-y)`(-1, ar, ai)`| inverse Fourier transform of `a` 142 | `[b, I] = sortrows(a, i)` | [`sort`](https://github.com/scijs/ndarray-sort)`(a)` | sort the rows of the matrix 143 |   | [`sort`](https://github.com/scijs/ndarray-sort)`(a.transpose(1, 0))` | sort the column of the matrix 144 | `regress(y, X)` | [`qr.factor`](https://github.com/scijs/ndarray-householder-qr#factor-a-d-)`( A, d );`
[`qr.solve`](https://github.com/scijs/ndarray-householder-qr#solve-a-d-x-)`( A, d, y );` | multilinear regression 145 | `decimate(x, q)` | [`resample`](https://github.com/scijs/ndarray-resample)`(output, input)` | downsample with low-pass filtering ([`resample`](https://github.com/scijs/ndarray-resample) downsamples by a factor of two) 146 | `unique` | | 147 | `squeeze(a)` | [`squeeze`]()`(a)` | Remove singleton dimensions of `a` 148 | 149 | 150 | ## License 151 | 152 | © 2015 Ricky Reusser. MIT License. 153 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | var ndarray = require('ndarray') 2 | var show = require('ndarray-show') 3 | var iota = require('iota-array') 4 | var diag = require('ndarray-diagonal') 5 | var fill = require('ndarray-fill') 6 | var ops = require('ndarray-ops') 7 | var pool = require('ndarray-scratch') 8 | 9 | var a = ndarray(new Float64Array([1,2,3,4,5,6]),[2,3]) 10 | 11 | console.log('a =\n'+show(a)) 12 | console.log('\na.dimension = ',a.dimension) 13 | 14 | console.log('a.size =', a.size) 15 | console.log('a.shape =',a.shape) 16 | console.log('a.shape[0] =',a.shape[0]) 17 | console.log('a.shape[1] =',a.shape[1]) 18 | console.log('\na[:,:-1:]=\n'+show(a.step(-1,1))) 19 | 20 | var b = ndarray(new Float64Array([1,2,3,4,5,6])) 21 | console.log('\nb ='+show(b)) 22 | console.log('\nb[-1] =',b.get(b.shape[0]-1)) 23 | 24 | 25 | var c = ndarray(iota(56), [8,7]) 26 | console.log('\nc =\n'+show(c)) 27 | 28 | console.log('\nfirst five rows of c =\n'+show(c.hi(5,null))) 29 | console.log('\nlast five rows of c =\n'+show(c.lo(c.shape[0]-5,null))) 30 | console.log('\nrows one to three and columns five to seven =\n'+show(c.hi(3,7).lo(0,4))) 31 | 32 | 33 | var d = pool.ones([5,5]) 34 | 35 | ops.assigns(diag(d),2) 36 | ops.mulseq(d.hi(null,2),2) 37 | 38 | console.log(show(d)) 39 | 40 | 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scijs-ndarray-for-matlab-users", 3 | "version": "0.1.0", 4 | "description": "Common scijs/ndarray operations for people familar with MATLAB (or at least not familiar with scijs)", 5 | "main": "index.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "iota-array": "^1.0.0", 16 | "ndarray": "^1.0.18", 17 | "ndarray-diagonal": "^1.0.0", 18 | "ndarray-fill": "^1.0.1", 19 | "ndarray-ops": "^1.2.2", 20 | "ndarray-scratch": "^1.2.0", 21 | "ndarray-show": "^2.0.0" 22 | } 23 | } 24 | --------------------------------------------------------------------------------