Now that we know how to create a vector, we are ready to create a multidimensional NumPy array. After we create the array, we will again want to display its shape:
In: m = array([arange(2), arange(2)]) In: m Out: array([[0, 1], [0, 1]])
In: m.shape Out: (2, 2)
We created a two-by-two array with the arange()
and array()
functions we have come to trust and love. Without any warning, the array()
function appeared on the stage.
The array()
function creates an array from an object that you give to it. The object needs to be array-like, for instance, a Python list. In the preceding example, we passed in a list of arrays. The object is the only required argument of the array()
function. NumPy functions tend to have a lot of optional arguments with predefined defaults. View the documentation for this function from the IPython shell with the help()
function given here:
In [1]: help(array)
Or use the following shorthand:
In [2]: array?
Of course, you can substitute array
in this example with another NumPy function you are interested in.
Q1. How is the shape of an ndarray
stored?
It shouldn't be too hard now to create a three-by-three array. Give it a go and check whether the array shape is as expected.
From time to time, we will want to select a particular element of an array. We will take a look at how to do this, but, first, create a two-by-two array again:
In: a = array([[1,2],[3,4]]) In: a Out: array([[1, 2], [3, 4]])
The array was created this time by passing a list of lists to the array()
function. We will now select one by one each item of the matrix. Remember, the indices are numbered starting from 0:
In: a[0,0] Out: 1 In: a[0,1] Out: 2 In: a[1,0] Out: 3 In: a[1,1] Out: 4
As you can see, selecting elements of the array is pretty simple. For the array a
, we just use the notation a[m,n]
, where m
and n
are the indices of the item in the array (the array can have even more dimensions than in this example). This screenshot shows a simple example of an array:
Python has an integer type, a float type, and a complex type; however, this is not enough for scientific computing and, for this reason, NumPy has a lot more data types with varying precision, dependent on memory requirements.
Integers represent whole numbers, such as -1, 0, and 1. Floating-point numbers correspond to real numbers as used in mathematics, for example, fractions or irrational numbers such as pi. Because of the way computers work, we are able to represent integers exactly, but floating-point numbers are approximated. Complex numbers can have an imaginary component usually denoted with i or j. By definition, i is the square root of -1. For instance, 2.5 + 3.7i is a complex number (for more information, refer to https://www.khanacademy.org/math/precalculus/imaginary_complex_precalc).
In practice, we need even more types with varying precision and, therefore, different memory size of the type. The majority of the NumPy numerical types end with a number. This number indicates the number of bits associated with the type. The following table (adapted from the NumPy user guide) gives an overview of NumPy numerical types:
Type |
Description |
---|---|
|
Boolean ( |
|
Platform integer (normally either |
|
Byte (-128 to 127) |
|
Integer (-32768 to 32767) |
|
Integer (-2 ** 31 to 2 ** 31 -1) |
|
Integer (-2 ** 63 to 2 ** 63 -1) |
|
Unsigned integer (0 to 255) |
|
Unsigned integer (0 to 65535) |
|
Unsigned integer (0 to 2 ** 32 - 1) |
|
Unsigned integer (0 to 2 ** 64 - 1) |
|
Half precision float: sign bit, 5 bits exponent, 10 bits mantissa |
|
Single precision float: sign bit, 8 bits exponent, 23 bits mantissa |
|
Double precision float: sign bit, 11 bits exponent, 52 bits mantissa |
|
Complex number, represented by two 32-bit floats (real and imaginary components) |
|
Complex number, represented by two 64-bit floats (real and imaginary components) |
For floating-point types, we can request information with the finfo()
function given here:
In: finfo(float16) Out: finfo(resolution=0.0010004, min=-6.55040e+04, max=6.55040e+04, dtype=float16)
For each data type, there exists a corresponding conversion function:
In: float64(42) Out: 42.0 In: int8(42.0) Out: 42 In: bool(42) Out: True In: bool(0) Out: False In: bool(42.0) Out: True In: float(True) Out: 1.0 In: float(False) Out: 0.0
Many functions have a data type argument, which is often optional:
In: arange(7, dtype=uint16) Out: array([0, 1, 2, 3, 4, 5, 6], dtype=uint16)
It is important to know that you are not allowed to convert a complex number into an integer or float. Trying to do that triggers a TypeError
, as shown in the following screenshot:
The same goes for conversion of a complex number into a float.
The j
part is the imaginary coefficient of the complex number. However, you can convert a float in to a complex number, for instance, complex(1.0)
.
Data type objects are instances of the numpy.dtype
class. Once again, arrays have a data type. To be precise, every element in a NumPy array has the same data type. The data type object can tell you the size of the data in bytes. The size in bytes is given by the itemsize
attribute of the dtype
class:
In: a.dtype.itemsize Out: 8
Character codes are included for backward compatibility with Numeric. Numeric is the predecessor of NumPy. Their use is not recommended, but the codes are provided here because they pop up in several places. We should instead use the dtype
objects. The table shows the character codes:
Type |
Character code |
---|---|
Integer |
|
Unsigned integer |
|
Single precision float |
|
Double precision float |
|
Boolean |
|
Complex |
|
String |
|
Unicode |
|
Void |
|
Look at the following code to create an array of single precision floats:
In: arange(7, dtype='f') Out: array([ 0., 1., 2., 3., 4., 5., 6.], dtype=float32)
Likewise this creates an array of complex numbers.
In: arange(7, dtype='D') Out: array([ 0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j])
Python classes have functions, which are called methods, if they belong to a class. Some of these methods are special and used to create new objects. These specialized methods are called constructors.
You can read more about Python classes at https://docs.python.org/2/tutorial/classes.html.
We have a variety of ways to create data types. Take the case of floating point data:
In: dtype(float) Out: dtype('float64')
In: dtype('f') Out: dtype('float32')
In: dtype('d') Out: dtype('float64')
In: dtype('f8') Out: dtype('float64')
A listing of all full data type names can be found with the sctypeDict.keys()
function:
In: sctypeDict.keys() Out: [0, … 'i2', 'int0']
The dtype
class has a number of useful attributes. For example, get information about the character code of a data type through the attributes of dtype
:
In: t = dtype('Float64') In: t.char Out: 'd'
The type attribute corresponds to the type of object of the array elements:
In: t.type Out: <type 'numpy.float64'>
The str
attribute of the dtype
class gives a string representation of the data type. It starts with a character representing endianness, if appropriate, then a character code, followed by a number corresponding to the number of bytes that each array item requires. Endianness, here, refers to the way bytes are ordered within a 32- or 64-bit word. In big-endian order, the most significant byte is stored first, indicated by >
. In little-endian order, the least significant byte is stored first, indicated by <
:
In: t.str Out: '<f8'