ndarray vs ndarray-pack vs ndarray-ops vs ndarray-scratch
Numerical Computing Libraries Comparison
1 Year
ndarrayndarray-packndarray-opsndarray-scratch
What's Numerical Computing Libraries?

These packages are designed to facilitate numerical computing in JavaScript, particularly for handling multi-dimensional arrays (ndarrays). They provide various functionalities such as mathematical operations, packing data, and managing temporary arrays efficiently. These libraries are essential for developers working on data-intensive applications, machine learning, and scientific computing, as they enable efficient manipulation and computation of large datasets in a structured manner.

Package Weekly Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
ndarray611,7731,228-225 years agoMIT
ndarray-pack424,59510-19 years agoMIT
ndarray-ops339,94467-611 years agoMIT
ndarray-scratch74,80711-510 years agoMIT
Feature Comparison: ndarray vs ndarray-pack vs ndarray-ops vs ndarray-scratch

Core Functionality

  • ndarray:

    ndarray provides the core functionality for creating and manipulating n-dimensional arrays. It supports various data types and allows for easy access and modification of array elements, making it a versatile choice for numerical tasks.

  • ndarray-pack:

    ndarray-pack focuses on the serialization of ndarrays, enabling efficient packing and unpacking of data structures. This is crucial for applications that need to save or transmit large arrays while minimizing data size and maintaining integrity.

  • ndarray-ops:

    ndarray-ops builds upon ndarray by adding a rich set of mathematical functions, including element-wise operations, reductions, and linear algebra routines. This package is essential for performing complex calculations directly on ndarrays without needing additional libraries.

  • ndarray-scratch:

    ndarray-scratch provides a mechanism for creating temporary ndarrays that can be used for intermediate calculations. This helps in optimizing performance by reducing the overhead of frequent memory allocation and deallocation.

Performance Optimization

  • ndarray:

    ndarray is optimized for performance with a focus on efficient memory usage and fast access to array elements. It is designed to handle large datasets effectively, making it suitable for performance-critical applications.

  • ndarray-pack:

    ndarray-pack is designed to minimize the overhead associated with data serialization. It uses efficient algorithms to pack ndarrays into compact formats, which is essential for performance in data transmission and storage scenarios.

  • ndarray-ops:

    ndarray-ops is optimized for mathematical operations, ensuring that computations are performed as quickly as possible. It leverages optimized algorithms for linear algebra and other operations, making it suitable for high-performance computing tasks.

  • ndarray-scratch:

    ndarray-scratch improves performance by managing temporary arrays efficiently. By reusing memory for temporary calculations, it reduces the frequency of garbage collection and memory fragmentation, leading to smoother performance.

Ease of Use

  • ndarray:

    ndarray offers a straightforward API that is easy to learn and use, making it accessible for developers new to numerical computing. Its simplicity allows for quick integration into projects without a steep learning curve.

  • ndarray-pack:

    ndarray-pack is designed to be easy to use, providing simple functions for packing and unpacking ndarrays. This ease of use is crucial for developers who need to handle data serialization without extensive overhead.

  • ndarray-ops:

    ndarray-ops, while adding complexity with its extensive set of operations, maintains a user-friendly interface that allows developers to perform advanced mathematical tasks without needing to understand the underlying implementations.

  • ndarray-scratch:

    ndarray-scratch is intuitive, allowing developers to create and manage temporary arrays with minimal effort. Its design focuses on reducing complexity in memory management, making it easier to implement.

Extensibility

  • ndarray:

    ndarray is designed to be extensible, allowing developers to build additional functionalities on top of its core features. This makes it suitable for projects that may require custom array operations or data types.

  • ndarray-pack:

    ndarray-pack can be integrated with other serialization formats, allowing for extensibility in how data is stored and transmitted. This is useful for developers who need to work within different data ecosystems.

  • ndarray-ops:

    ndarray-ops can be extended with custom mathematical functions, enabling developers to tailor the library to their specific needs. This flexibility is beneficial for specialized applications in scientific computing.

  • ndarray-scratch:

    ndarray-scratch can be combined with other ndarray libraries to create a more comprehensive numerical computing environment. Its temporary array management can enhance performance across various applications.

Community and Support

  • ndarray:

    ndarray has a growing community of users and contributors, providing a solid base for support and collaboration. This community aspect can be beneficial for troubleshooting and sharing best practices.

  • ndarray-pack:

    ndarray-pack, while more niche, has a supportive community that understands the importance of data serialization in numerical computing, providing valuable insights and improvements.

  • ndarray-ops:

    ndarray-ops benefits from a dedicated user base focused on mathematical operations, which can lead to better support for advanced use cases and optimizations based on community feedback.

  • ndarray-scratch:

    ndarray-scratch, being a utility for temporary arrays, has a smaller community but is supported by users who prioritize performance optimization, ensuring that best practices are shared.

How to Choose: ndarray vs ndarray-pack vs ndarray-ops vs ndarray-scratch
  • ndarray:

    Choose ndarray if you need a foundational library for creating and manipulating multi-dimensional arrays. It provides a simple API for array creation and basic operations, making it suitable for general-purpose numerical tasks.

  • ndarray-pack:

    Opt for ndarray-pack when you need to efficiently serialize ndarrays for storage or transmission. This package is particularly useful for applications that require data persistence or network communication, as it allows for compact representation of multi-dimensional arrays.

  • ndarray-ops:

    Select ndarray-ops if you require a comprehensive set of mathematical operations on ndarrays. This package extends ndarray with a variety of functions for linear algebra, statistical operations, and more, making it ideal for complex numerical computations.

  • ndarray-scratch:

    Use ndarray-scratch for managing temporary ndarrays that are needed for intermediate calculations. This package helps in optimizing memory usage by providing a way to create and dispose of temporary arrays without cluttering the main memory space.

README for ndarray

ndarray

Modular multidimensional arrays for JavaScript.

browser support

build status

stable

Browse a number of ndarray-compatible modules in the scijs documentation
Coming from MATLAB or numpy? See: scijs/ndarray for MATLAB users
Big list of ndarray modules

Introduction

ndarrays provide higher dimensional views of 1D arrays. For example, here is how you can turn a length 4 typed array into an nd-array:

var mat = ndarray(new Float64Array([1, 0, 0, 1]), [2,2])

//Now:
//
// mat = 1 0
//       0 1
//

Once you have an nd-array you can access elements using .set and .get. For example, here is an implementation of Conway's game of life using ndarrays:

function stepLife(next_state, cur_state) {

  //Get array shape
  var nx = cur_state.shape[0], 
      ny = cur_state.shape[1]

  //Loop over all cells
  for(var i=1; i<nx-1; ++i) {
    for(var j=1; j<ny-1; ++j) {

      //Count neighbors
      var n = 0
      for(var dx=-1; dx<=1; ++dx) {
        for(var dy=-1; dy<=1; ++dy) {
          if(dx === 0 && dy === 0) {
            continue
          }
          n += cur_state.get(i+dx, j+dy)
        }
      }
      
      //Update state according to rule
      if(n === 3 || n === 3 + cur_state.get(i,j)) {
        next_state.set(i,j,1)
      } else {
        next_state.set(i,j,0)
      }
    }
  }
}

You can also pull out views of ndarrays without copying the underlying elements. Here is an example showing how to update part of a subarray:

var x = ndarray(new Float32Array(25), [5, 5])
var y = x.hi(4,4).lo(1,1)

for(var i=0; i<y.shape[0]; ++i) {
  for(var j=0; j<y.shape[1]; ++j) {
    y.set(i,j,1)
  }
}

//Now:
//    x = 0 0 0 0 0
//        0 1 1 1 0
//        0 1 1 1 0
//        0 1 1 1 0
//        0 0 0 0 0

ndarrays can be transposed, flipped, sheared and sliced in constant time per operation. They are useful for representing images, audio, volume graphics, matrices, strings and much more. They work both in node.js and with browserify.

Install

Install the library using npm:

npm install ndarray

You can also use ndarrays in a browser with any tool that follows the CommonJS/node module conventions. The most direct way to do this is to use browserify. If you want live-reloading for faster debugging, check out beefy.

API

Once you have ndarray installed, you can use it in your project as follows:

var ndarray = require("ndarray")

Constructor

ndarray(data[, shape, stride, offset])

The default module.exports method is the constructor for ndarrays. It creates an n-dimensional array view wrapping an underlying storage type

  • data is a 1D array storage. It is either an instance of Array, a typed array, or an object that implements get(), set(), .length
  • shape is the shape of the view (Default: data.length)
  • stride is the resulting stride of the new array. (Default: row major)
  • offset is the offset to start the view (Default: 0)

Returns an n-dimensional array view of the buffer

Members

The central concept in ndarray is the idea of a view. The way these work is very similar to SciPy's array slices. Views are affine projections to 1D storage types. To better understand what this means, let's first look at the properties of the view object. It has exactly 4 variables:

  • array.data - The underlying 1D storage for the multidimensional array
  • array.shape - The shape of the typed array
  • array.stride - The layout of the typed array in memory
  • array.offset - The starting offset of the array in memory

Keeping a separate stride means that we can use the same data structure to support both row major and column major storage

Element Access

To access elements of the array, you can use the set/get methods:

array.get(i,j,...)

Retrieves element i,j,... from the array. In psuedocode, this is implemented as follows:

function get(i,j,...) {
  return this.data[this.offset + this.stride[0] * i + this.stride[1] * j + ... ]
}

array.set(i,j,...,v)

Sets element i,j,... to v. Again, in psuedocode this works like this:

function set(i,j,...,v) {
  return this.data[this.offset + this.stride[0] * i + this.stride[1] * j + ... ] = v
}

array.index(i,j, ...)

Retrieves the index of the cell in the underlying ndarray. In JS,

function index(i,j, ...) {
  return this.offset + this.stride[0] * i + this.stride[1] * j + ...
}

Properties

The following properties are created using Object.defineProperty and do not take up any physical memory. They can be useful in calculations involving ndarrays

array.dtype

Returns a string representing the undelying data type of the ndarray. Excluding generic data stores these types are compatible with typedarray-pool. This is mapped according to the following rules:

Data type | String --------: | :----- Int8Array | "int8" Int16Array | "int16" Int32Array | "int32" Uint8Array | "uint8" Uint16Array | "uint16" Uint32Array | "uint32" BigInt64Array | "bigint64" BigUint64Array | "biguint64" Float32Array | "float32" Float64Array | "float64" Array | "array" Uint8ArrayClamped | "uint8_clamped" Buffer | "buffer" Other | "generic"

Generic arrays access elements of the underlying 1D store using get()/set() instead of array accessors.

array.size

Returns the size of the array in logical elements.

array.order

Returns the order of the stride of the array, sorted in ascending length. The first element is the first index of the shortest stride and the last is the index the longest stride.

array.dimension

Returns the dimension of the array.

Slicing

Given a view, we can change the indexing by shifting, truncating or permuting the strides. This lets us perform operations like array reversals or matrix transpose in constant time (well, technically O(shape.length), but since shape.length is typically less than 4, it might as well be). To make life simpler, the following interfaces are exposed:

array.lo(i,j,k,...)

This creates a shifted view of the array. Think of it as taking the upper left corner of the image and dragging it inward by an amount equal to (i,j,k...).

array.hi(i,j,k,...)

This does the dual of array.lo(). Instead of shifting from the top-left, it truncates from the bottom-right of the array, returning a smaller array object. Using hi and lo in combination lets you select ranges in the middle of an array.

Note: hi and lo do not commute. In general:

a.hi(3,3).lo(3,3)  !=  a.lo(3,3).hi(3,3)

array.step(i,j,k...)

Changes the stride length by rescaling. Negative indices flip axes. For example, here is how you create a reversed view of a 1D array:

var reversed = a.step(-1)

You can also change the step size to be greater than 1 if you like, letting you skip entries of a list. For example, here is how to split an array into even and odd components:

var evens = a.step(2)
var odds = a.lo(1).step(2)

array.transpose(p0, p1, ...)

Finally, for higher dimensional arrays you can transpose the indices without replicating the data. This has the effect of permuting the shape and stride values and placing the result in a new view of the same data. For example, in a 2D array you can calculate the matrix transpose by:

M.transpose(1, 0)

Or if you have a 3D volume image, you can shift the axes using more generic transformations:

volume.transpose(2, 0, 1)

array.pick(p0, p1, ...)

You can also pull out a subarray from an ndarray by fixing a particular axis. The way this works is you specify the direction you are picking by giving a list of values. For example, if you have an image stored as an nxmx3 array you can pull out the channel as follows:

var red   = image.pick(null, null, 0)
var green = image.pick(null, null, 1)
var blue  = image.pick(null, null, 2)

As the above example illustrates, passing a negative or non-numeric value to a coordinate in pick skips that index.

More information

For more discussion about ndarrays, here are some talks, tutorials and articles about them:

License

(c) 2013-2016 Mikola Lysenko. MIT License