2. Manipulate data with ndarray

We’ll start by introducing the NDArray, MXNet’s primary tool for storing and transforming data. If you’ve worked with NumPy before, you’ll notice that a NDArray is, by design, similar to NumPy’s multi-dimensional array.

2.1. Get started

To get started, let’s import the ndarray package (nd is shortform) from MXNet.

In [1]:
# Uncomment the following line to install the latest MXNet
# !pip install --pre mxnet
# Or use the following version to accelerate Intel CPU performance.
# !pip install --pre mxnet-mkl

from mxnet import nd

Next, let’s see how to create a 2D array (also called a matrix) with values from two sets of numbers: 1, 2, 3 and 4, 5, 6. This might also be referred to as a tuple of a tuple of integers.

In [2]:
nd.array(((1,2,3),(5,6,7)))
Out[2]:

[[ 1.  2.  3.]
 [ 5.  6.  7.]]
<NDArray 2x3 @cpu(0)>

We can also create a very simple matrix with the same shape (2 rows by 3 columns), but fill it with 1s.

In [3]:
x = nd.ones((2,3))
x
Out[3]:

[[ 1.  1.  1.]
 [ 1.  1.  1.]]
<NDArray 2x3 @cpu(0)>

Often we’ll want to create arrays whose values are sampled randomly. For example, sampling values uniformly between -1 and 1. Here we create the same shape, but with random sampling.

In [4]:
y = nd.random.uniform(-1,1,(2,3))
y
Out[4]:

[[ 0.09762704  0.18568921  0.43037868]
 [ 0.68853152  0.20552671  0.71589124]]
<NDArray 2x3 @cpu(0)>

You can also fill an array of a given shape with a given value, such as 2.0.

In [5]:
x = nd.full((2,3), 2.0)
x
Out[5]:

[[ 2.  2.  2.]
 [ 2.  2.  2.]]
<NDArray 2x3 @cpu(0)>

As with NumPy, the dimensions of each NDArray are accessible by accessing the .shape attribute. We can also query its size, which is equal to the product of the components of the shape. In addition, .dtype tells the data type of the stored values.

In [6]:
(x.shape, x.size, x.dtype)
Out[6]:
((2, 3), 6, numpy.float32)

2.2. Operations

NDArray supports a large number of standard mathematical operations. Such as element-wise multiplication:

In [7]:
x * y
Out[7]:

[[ 0.19525409  0.37137842  0.86075735]
 [ 1.37706304  0.41105342  1.43178248]]
<NDArray 2x3 @cpu(0)>

Exponentiation:

In [8]:
y.exp()
Out[8]:

[[ 1.10255146  1.20404804  1.53783977]
 [ 1.99078989  1.22817183  2.0460093 ]]
<NDArray 2x3 @cpu(0)>

And grab a matrix’s transpose to compute a proper matrix-matrix product:

In [9]:
nd.dot(x, y.T)
Out[9]:

[[ 1.42738986  3.21989894]
 [ 1.42738986  3.21989894]]
<NDArray 2x2 @cpu(0)>

2.3. Indexing

MXNet NDArrays support slicing in all the ridiculous ways you might imagine accessing your data. Here’s an example of reading a particular element, which returns a 1D array with shape (1,).

In [10]:
y[1,2]
Out[10]:

[ 0.71589124]
<NDArray 1 @cpu(0)>

Read the second and third columns from y.

In [11]:
y[:,1:3]
Out[11]:

[[ 0.18568921  0.43037868]
 [ 0.20552671  0.71589124]]
<NDArray 2x2 @cpu(0)>

and writing to a specific element

In [12]:
y[:,1:3] = 2
y
Out[12]:

[[ 0.09762704  2.          2.        ]
 [ 0.68853152  2.          2.        ]]
<NDArray 2x3 @cpu(0)>

Multi-dimensional slicing is also supported.

In [13]:
y[1:2,0:2] = 4
y
Out[13]:

[[ 0.09762704  2.          2.        ]
 [ 4.          4.          2.        ]]
<NDArray 2x3 @cpu(0)>

2.4. Converting between MXNet NDArray and NumPy

Converting MXNet NDArrays to and from NumPy is easy. The converted arrays do not share memory.

In [14]:
a = x.asnumpy()
(type(a), a)
Out[14]:
(numpy.ndarray, array([[ 2.,  2.,  2.],
        [ 2.,  2.,  2.]], dtype=float32))
In [15]:
nd.array(a)
Out[15]:

[[ 2.  2.  2.]
 [ 2.  2.  2.]]
<NDArray 2x3 @cpu(0)>