NumJy is a limited implementation of NumPy, the numeric package for Python, for Jython, i.e. the Java-based version of Python. It allows Jython code to handle multi-dimensional arrays in a syntax similar to NumPy.
To access NumJy, import it similar to NumPy, but using 'numjy' instead of 'numpy':
from numjy import *
When running unter Python, this will actually import the original NumPy. Under Jython, it imports the NumJy packages that emulate a subset of NumPy.
Type | Description |
---|---|
float, float64 | 64-bit floating point |
float32 | 32-bit floating point |
int, int64 | 64-bit integer |
int32 | 32-bit integer |
int16 | 16-bit integer |
int8 | 8-bit integer |
bool | Boolean (True, False), stored as 8-bit integer |
These serve as type identifiers when selecting a particular data type, for example:
a = linspace(1, 10, 20, dtype=float32)
You can convert Python arrays or lists into NumJy arrays with
the array
command:
array([ 1, 2, 3, 4]) array([ 1.0, 2.0, 3.0, 4.0 ]) array([ [ 1, 2], [3, 4] ])
There are commands to create specific arrays:
>>> zeros( (2, 3) ) array([ [ 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.0 ] ]) >>> ones(( 3,2)) array([ [ 1.0, 1.0 ], [ 1.0, 1.0 ], [ 1.0, 1.0 ] ]) >>> linspace(2, 10, 5) array([ 2.0, 4.0, 6.0, 8.0, 10.0 ]) >>> arange(1, 5, 0.5) array([ 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5 ]) >>> arange(5, 1, -2) array([ 5.0, 3.0 ])
For details, refer to their help:
>>> help(array) >>> help(arange) >>> help(linspace)
All arrays are internally stored as a flat list, but they are presented in a multi-dimensional shape.
Arrays can be viewed in a different shape as long as their overall size remains the same:
>>> # 1-D array >>> arange(6) array([ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 ]) >>> arange(6).shape(6,) >>> # Change shape to 2x3 >>> arange(6).reshape(2, 3) array([ [ 0.0, 1.0, 2.0 ], [ 3.0, 4.0, 5.0 ] ]) >>> arange(6).reshape(2,3).shape(2, 3) >>> # Change shape to 3x2 >>> arange(6).reshape(3, 2) array([ [ 0.0, 1.0 ], [ 2.0, 3.0 ], [ 4.0, 5.0 ] ]) >>> # Error when trying to change the overall size >>> arange(6).reshape(7) ...IllegalArgumentException: Cannot change shape from [6] to [7]
The number of dimensions is also known as 'rank' of the array. A basic 1-dimensional array has rank 1. Re-shaped to 2x3 or 3x2 it has a rank of 2:
>>> arange(6).ndim 1 >>> arange(6).reshape(3, 2).ndim 2
NumJy array elements are accessed just like other Python arrays via square brackets, using as many index elements as required by the dimensions of the array:
>>> arange(6)[0] 0.0 >>> arange(6)[5] 5.0 >>> arange(6)[-1] 5.0 >>> arange(6).reshape(2,3)[1,1] 4.0
Note how negative indices reference elements from the 'end' of their respective dimension. For the example with 6 elements, numbered 0 to 5, the index -1 references the same element as the index 5.
Operations like reshape()
, transpose()
or the slicing described in the following section create a new view of the original array.
To preserve memory as well as to allow certain operations on the data,
the underlying data is not copied!
Example: Changing the reshaped view 'b' of 'a' also affects corresponding elements in 'a':
>>> a=arange(6) >>> b=a.reshape(2, 3) >>> b array([ [ 0.0, 1.0, 2.0 ], [ 3.0, 4.0, 5.0 ] ]) >>> b[1,2]=666 >>> b array([ [ 0.0, 1.0, 2.0 ], [ 3.0, 4.0, 666.0 ] ]) >>> a array([ 0.0, 1.0, 2.0, 3.0, 4.0, 666.0 ])
The original array is available as the 'base':
>>> b = arange(6).reshape(2, 3) >>> b array([ [ 0.0, 1.0, 2.0 ], [ 3.0, 4.0, 5.0 ] ]) >>> b.base array([ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 ])
If necessary, it is possible to create a copy which will then no longer be assiciated with any 'base':
>>> b = copy(a.reshape(2, 3)) >>> b = a.reshape(2, 3).copy() >>> b.base is None True
NumJy supports the basic slicing operations of NumPy. When accessing a 1-dimensional array, the result is a sub-section of the original array:
>>> arange(6) array([ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 ]) >>> # Use start:stop slice >>> arange(6)[2:4] array([ 2.0, 3.0 ]) >>> # Use start:stop:step slice >>> arange(6)[1:5:2] array([ 1.0, 3.0 ]) >>> # start:... >>> arange(6)[1:] array([ 1.0, 2.0, 3.0, 4.0, 5.0 ]) >>> # ...stop >>> arange(6)[:3] array([ 0.0, 1.0, 2.0 ]) >>> # Specify stop relative to length >>> arange(6)[:-1] array([ 0.0, 1.0, 2.0, 3.0, 4.0 ]) >>> # Specify step >>> arange(6)[::2] array([ 0.0, 2.0, 4.0 ])
The result is a view of the original array. Changing the slice will also update the original array:
>>> orig = arange(6) >>> sub = orig[2:4] >>> sub[1] = 666 >>> sub array([ 2.0, 666.0 ]) >>> orig array([ 0.0, 1.0, 2.0, 666.0, 4.0, 5.0 ])
When accessing multi-dimensional arrays, use one start:stop:step slice per dimension. When fewer indices are provided, the missing indices default to the complete axis:
>>> orig=arange(6).reshape(2, 3) >>> orig array([ [ 0.0, 1.0, 2.0 ], [ 3.0, 4.0, 5.0 ] ]) >>> # Using only [1] is the same as [1, :] >>> row1 = orig[1] >>> row1 array([ 3.0, 4.0, 5.0 ]) >>> # Access column >>> col2 = orig[:, 2] >>> col2 array([ 2.0, 5.0 ])
Note how the col2
in the above example is not a strict column of shape 2x1.
Instead, it is a 1-D array with 2 elements.
In the example, the index 2
was used to select just one element of the second axis.
When using a plain index to access a individual element on an axis, that axis is 'collapsed'
and removed from the result.
To force NumPy/NumJy to keep that single-element axis, specify a complete slice, even if the slice addresses only a single element:
>>> orig[:, 2:3:1] array([ [ 2.0 ], [ 5.0 ] ])
Since slicing creates a view into the original array, it is possible to assign data to N-dimensional slices, but the array shapes must match:
>>> # Create 2x3 array >>> orig = arange(6).reshape(2, 3) >>> # In all rows, update every other column with a 2x2 data >>> orig[:, ::2] = array([ [ 40, 42], [ 43, 45 ]]) >>> orig array([ [ 40.0, 1.0, 42.0 ], [ 43.0, 4.0, 45.0 ] ])
Math operations in NumJy and NumPy are fundamentally element-by-element. When for example adding two vectors, their elements are added:
>>> arange(6) array([ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 ]) >>> arange(6) + arange(6) array([ 0.0, 2.0, 4.0, 6.0, 8.0, 10.0 ])
It is also possible to add scalars to all elements of an array:
>>> arange(6) + 10 array([ 10.0, 11.0, 12.0, 13.0, 14.0, 15.0 ])
When operating on two NumJy/NumPy arrays, their shapes must be compatible according to the broadcasting rule:
The size of the trailing axes for both arrays in an operation must either be the same, or one of them must be one.
For example, these arrays are all compatible to the shape 10x5x3:
The resulting array will combine all dimensions as in the following example, which could represent a 10x5 image where each pixel in turn has 3 RGB values:
Image dimension : 10 x 5 x 3 Operand dimension: 3 Result : 10 x 5 x 3
The elements of the smaller array are applied to the trailing dimensions of the bigger array. Dimensions of size 1 are 'broadcast' over all element of the corresponding dimension in the other array.
Examples:
>>> # Apply array of shape ( 1 ) to all elements of ( 2, 2 ) >>> array([ ... [ 1.0, 2.0 ], ... [ 3.0, 4.0 ] ] ) + array( [ 10 ] ) array([ [ 11.0, 12.0 ], [ 13.0, 14.0 ] ]) >>> # Apply 'column' array of shape ( 2, 1 ) to columns of ( 2, 2 ) >>> array([ ... [ 1.0, 2.0 ], ... [ 3.0, 4.0 ] ] ) + array( [ [ 10 ], [ 20 ] ]) array([ [ 11.0, 12.0 ], [ 23.0, 24.0 ] ])
For a longer explanation, see http://www.scipy.org/EricsBroadcastingDoc
Basic add, substract, ... operation between arrays, returning an array with 'broadcast' shape:
Operator | Description |
---|---|
-a | Compute element-wise negative |
a + b | Add elements of arrays a, b |
a - b | Subtract ... |
a * b | Multiply ... |
a / b | Divide ... Division by 0 results in 0, NaN, Infinity or big numbers, depending on the data type. |
a ** b | Element-wise exponentiation, raise a to b's power |
a += b | Increment elements of array a by elements of b |
a -= b | Subtract ... |
a *= b | Multiply ... |
a /= b | Divide ... |
a **= b | Raise to b's power |
a > b | True/False if corresponding array elements are greater than, ... |
a >= b | ... greater or equal ... |
a < b | ... less than ... |
a <= b | ... greater or equal ... |
a == b | ... equal ... |
a != b | ... not equal ... |
Functions that operate on each array element, returning an array of same shape as their input:
Operator | Description |
---|---|
abs(a) | Compute element-wise absolute values |
exp(a) | ... exponential ... |
log(a) | ... logarithm ... |
log10(a) | ... logarithm (base 10) ... |
sqrt(a) | ... square root ... |
Functions that operate on two arrays, returning an array with 'broadcast' shape:
Operator | Description |
---|---|
pow(a, b) | Same as a ** b |
Functions that return a scalar:
Operator | Description |
---|---|
any(a) | Return True if any element is non-zero |
all(a) | Return True if all elements are non-zero |
nonzero(a) | Return indices of array elements that are non-zero |
min(a) | Minimum element |
max(a) | Maximum element |
sum(a) | Sum over all elements |
While the normal array multiplication is element-by-element as described before and not in the matrix sense, there are matrix type operations. They are limited to arrays of certain shapes.
>>> # 2-D matrix, 1-D vector >>> mat = array( [ [ 1, 2 ], [ 3, 4 ] ]) >>> vec = array( [ 10, 20 ] ) >>> # Transpose a matrix >>> transpose(mat) array([ [ 1.0, 3.0 ], [ 2.0, 4.0 ] ]) >>> # .. shortcut >>> mat.T array([ [ 1.0, 3.0 ], [ 2.0, 4.0 ] ]) >>> # Matrix 'dot' product >>> dot(mat, vec) array([ 50.0, 110.0 ]) >>> dot(mat, mat) array([ [ 7.0, 10.0 ], [ 15.0, 22.0 ] ])
Example for computing rotation:
from math import cos, sin, radians angle = radians(90) rotate = array( [ [ cos(angle), -sin(angle) ], [ sin(angle), cos(angle) ] ] ) vec = array( [ 1, 0 ]) # Results in [ 0, 1 ] result = dot(rotate, vec)