Skip to content

Commit

Permalink
add nj.flip and nj.rot90
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaspanel committed Nov 26, 2017
1 parent f3e6f92 commit 09b619d
Show file tree
Hide file tree
Showing 8 changed files with 529 additions and 323 deletions.
625 changes: 304 additions & 321 deletions dist/numjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/numjs.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "numjs",
"version": "0.14.2",
"version": "0.15.0",
"description": "Like NumPy, in JavaScript",
"main": "src/index.js",
"directories": {
Expand Down
67 changes: 67 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,71 @@ function stack (arrays, axis) {
return stacked;
}


/**
* Reverse the order of elements in an array along the given axis.
* The shape of the array is preserved, but the elements are reordered.
* New in version 0.15.0.
* @param {Array|NdArray} m Input array.
* @param {number} axis Axis in array, which entries are reversed.
* @return {NdArray} A view of `m` with the entries of axis reversed. Since a view is returned, this operation is done in constant time.
*/
function flip(m, axis) {
m = NdArray.new(m);
var indexer = ones(m.ndim).tolist();
var cleanaxis = axis;
while (cleanaxis < 0) {
cleanaxis += m.ndim;
}
if (indexer[cleanaxis] === undefined) {
throw new errors.ValueError('axis=' + axis + 'invalid for the ' + m.ndim + '-dimensional input array');
}
indexer[cleanaxis] = -1;
return m.step.apply(m, indexer);
}

/**
* Rotate an array by 90 degrees in the plane specified by axes.
* Rotation direction is from the first towards the second axis.
* New in version 0.15.0.
* @param {Array|NdArray} m array_like
* @param {number} [k=1] Number of times the array is rotated by 90 degrees.
* @param {Array|NdArray} [axes=(0,1)] The array is rotated in the plane defined by the axes. Axes must be different.
* @return {NdArray} A rotated view of m.
*/
function rot90 (m, k, axes) {
k = k || 1;
while (k < 0) {
k += 4;
}
k = k % 4;
m = NdArray.new(m);
axes = NdArray.new(axes || [0, 1]);
if (axes.shape.length !== 1 || axes.shape[0] !== 2) {
throw new errors.ValueError('len(axes) must be 2');
}
axes = axes.tolist();
if (axes[0] === axes[1] || abs(axes[0] - axes[1]) === m.ndim) {
throw new errors.ValueError("Axes must be different.")
}

if (k === 0) {
return m;
}
if (k === 2) {
return flip(flip(m, axes[0]), axes[1]);
}
var axesList = arange(m.ndim).tolist();
var keep = axesList[axes[0]];
axesList[axes[0]] = axesList[axes[1]];
axesList[axes[1]] = keep;
if (k === 1) {
return transpose(flip(m, axes[1]), axesList);
} else {
return flip(transpose(m, axesList), axes[1]);
}
}

module.exports = {
config: CONF,
dtypes: DTYPES,
Expand All @@ -726,6 +791,7 @@ module.exports = {
ones: ones,
empty: empty,
flatten: flatten,
flip: flip,
random: random,
softmax: softmax,
sigmoid: sigmoid,
Expand Down Expand Up @@ -767,6 +833,7 @@ module.exports = {
diag: diag,
identity: identity,
stack: stack,
rot90: rot90,
int8: function (array) { return NdArray.new(array, 'int8'); },
uint8: function (array) { return NdArray.new(array, 'uint8'); },
int16: function (array) { return NdArray.new(array, 'int16'); },
Expand Down
19 changes: 19 additions & 0 deletions test/karma/flip.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-env mocha */
'use strict';

describe('flip', function () {

it('should work with ndarray', function () {
var m = nj.arange(8).reshape([2,2,2]);
expect(nj.flip(m, 0).tolist()).to.eql([
[[4, 5],
[6, 7]],
[[0, 1],
[2, 3]]]);
expect(nj.flip(m, 1).tolist()).to.eql([
[[2, 3],
[0, 1]],
[[6, 7],
[4, 5]]]);
});
});
53 changes: 53 additions & 0 deletions test/karma/rot90.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-env mocha */
'use strict';

describe('rot90', function () {

it('should work with default params', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m).tolist()).to.eql([[2, 4], [1, 3]]);
});

it('should accept native array as input', function () {
var arr = nj.rot90([[1,2],[3,4]]);
expect(arr.tolist()).to.eql([[2, 4], [1, 3]]);
});

it('should work when k = 2 ', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 2).tolist()).to.eql([[4, 3], [2, 1]]);
});

it('should work when k = 3', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 3).tolist()).to.eql([[3,1],[4,2]]);
});

it('should work when k = 4', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 4).tolist()).to.eql([[1,2],[3,4]]);
});

it('should raise an error if custom axes is not a 1d array of length 2', function () {
expect(function () {
nj.rot90([[1,2],[3,4]], 1, [0,1,2]);
}).to.throw();
});
it('should raise an error axes are the same', function () {
expect(function () {
nj.rot90([[1,2],[3,4]], 1, [0, 0]);
}).to.throw();
});
it('should support custom axes', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 1, [1,0]).tolist()).to.eql([[3,1],[4,2]]);
});
it('should work on ndarrays', function () {
var m = nj.arange(8).reshape([2,2,2]);
expect(nj.rot90(m, 1, [1,2]).tolist()).to.eql([
[[1, 3],
[0, 2]],
[[5, 7],
[4, 6]]]);
});
});
23 changes: 23 additions & 0 deletions test/mocha/flip.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-env mocha */
'use strict';

var expect = require('expect.js');

var nj = require('../../src');

describe('flip', function () {

it('should work with ndarray', function () {
var m = nj.arange(8).reshape([2,2,2]);
expect(nj.flip(m, 0).tolist()).to.eql([
[[4, 5],
[6, 7]],
[[0, 1],
[2, 3]]]);
expect(nj.flip(m, 1).tolist()).to.eql([
[[2, 3],
[0, 1]],
[[6, 7],
[4, 5]]]);
});
});
61 changes: 61 additions & 0 deletions test/mocha/rot90.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-env mocha */
'use strict';

var expect = require('expect.js');

var nj = require('../../src');

describe('rot90', function () {

it('should work with default params', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m).tolist()).to.eql([[2, 4], [1, 3]]);
});

it('should accept native array as input', function () {
var arr = nj.rot90([[1,2],[3,4]]);
expect(arr.tolist()).to.eql([[2, 4], [1, 3]]);
});

it('should work when k = 2 ', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 2).tolist()).to.eql([[4, 3], [2, 1]]);
});

it('should work when k = 3', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 3).tolist()).to.eql([[3,1],[4,2]]);
});

it('should work when k = 4', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 4).tolist()).to.eql([[1,2],[3,4]]);
});

it('should raise an error if custom axes is not a 1d array of length 2', function () {
expect(function () {
nj.rot90([[1,2],[3,4]], 1, [0,1,2]);
}).to.throwException(function (e) {
expect(e.toString()).to.equal('ValueError: len(axes) must be 2');
});
});
it('should raise an error axes are the same', function () {
expect(function () {
nj.rot90([[1,2],[3,4]], 1, [0, 0]);
}).to.throwException(function (e) {
expect(e.toString()).to.equal('ValueError: Axes must be different.');
});
});
it('should support custom axes', function () {
var m = nj.array([[1,2],[3,4]], 'int');
expect(nj.rot90(m, 1, [1,0]).tolist()).to.eql([[3,1],[4,2]]);
});
it('should work on ndarrays', function () {
var m = nj.arange(8).reshape([2,2,2]);
expect(nj.rot90(m, 1, [1,2]).tolist()).to.eql([
[[1, 3],
[0, 2]],
[[5, 7],
[4, 6]]]);
});
});

0 comments on commit 09b619d

Please sign in to comment.