Python Programming Fundamentals for Class 11 and 12 – Numpy

As discussed previously, simple one dimensional array operations can be executed using list, tuple etc. But carrying out multi-dimensional array operations using list is not easy. Python has an array module which provides methods for creating array, but they are slower to index than list. A good choice for carrying array operations is by using “NumPy” package.

NumPy is a Python package (licensed under the BSD license) which is helpful in scientific computing by providing multi-dimensional array object, various derived objects (such as masked arrays and matrices), and collection of routines for fast operations on arrays, including mathematical, logical, shape manipulation, sorting, basic linear algebra, basic statistical operations, and many more. At the core of the NumPy package, there is ndarray object which encapsulates n-dimensional arrays of homogeneous data types. There are several important differences between NumPy array and the standard Python sequence:

  • NumPy array has a fixed size at creation, unlike Python list (which can grow dynamically). Changing the size of an ndarray will create a new array and delete the original.
  • All elements in a NumPy array are required to be of the same data type.
  • NumPy array facilitate advanced mathematical and other types of operations on large numbers of data. Typically, such operations are executed more efficiently and with less code than is possible using Python’s built-in sequences.

History
NumPy is built on (and is a successor to) the successful “Numeric” package. Numeric was reasonably complete and stable, remains available, but is now obsolete. Numeric was originally written in 1995 largely by Jim Hugunin, while he was a graduate student at MIT. In 2001, Travis Oliphant along with Eric Jones and Pearu Peterson created “SciPy”, which had the the strenght of Numeric package along additional functionality. At about the same time as SciPy was being built, some Numeric users were hitting up against the limited capabilities of Numeric. As a result, “numarray” (now obselete) was created by Perry Greenfield, Todd Miller, and RickWhite at the Space Science Telescope Institute as a replacement for Numeric. In early 2005, Travis Oliphant initiated an effort to bring the diverging community together under a common framework. The effort was paid off with the release of a new package Numpy (with version 0.9.2) in early 2006, which is an amalgam of the code base of Numeric with additional features of numarray. The NumPy name was christened from the unofficial name of “Numerical Python”.

Numpy installation
This section discusses the simple installation approaches of NumPy in different operating system.

Windows
By default, NumPy is not shipped with official Python installer. But one can download (from website link: http://sourceforge.net/projects/numpy/files/NumPy/) the executable file of NumPy (recent version 1.8.1) followed by installing it. Before installing NumPy, please make sure that Python is already installed. NumPy is already included in Python(x,y) package, so one does not have to install NumPy separately, if the programmer is using Python(x,y).

Linux
One can install NumPy in Ubuntu (Linux) operating system by executing the following commands in the terminal (as shown in figure 6-1).

sudo apt-get install python-numpy

python-programming-fundamentals-class-11-12-numpy-fig7.1

Data types
Some of the data types supported by NumPy are given in table 7-1.
Table 7-1: Numpy data types

Data type Description
bool_ Boolean (True or False) stored as a byte.
int8 Byte (ranging from -128 to 127).
intl6 Integer (ranging from -32768 to 32767).
int32 Integer (ranging from -2147483648 to 2147483647).
int64 Integer (ranging from -9223372036854775808 to 9223372036854775807).
uint8 Unsigned integer (ranging from 0 to 255).
uintl6 Unsigned integer (ranging from 0 to 65535).
uint32 Unsigned integer (ranging from 0 to 4294967295).
uint64 Unsigned integer (ranging from 0 to 18446744073709551615).
float_ Shorthand for float64.
float16 Half precision float: sign bit, 5 bits exponent, 10 bits mantissa.
float32 Single precision float: sign bit, 8 bits exponent, 23 bits mantissa.
float64 Double precision float: sign bit, 11 bits exponent, 52 bits mantissa.
complex_ Shorthand for complex128.
complex64 Complex number, represented by two32 bit floats(real and imaginary components).
complex128 Complex number, represented by two 64-bit floats (real and imaginarycomponents).

 NumPy array
NumPy’s main object is the homogeneous multi-dimensional array. It is a table of elements (usually numbers), all of the same type, indexed by a tuple of positive integers. In NumPy, array dimensions are called “axes”, and the number of axes is known as “rank”. For example, the coordinates of a point in 3D space [ 1, 2, 1 ] is an array of rank 1, because it has one axis and that axis has a length of 3. In the following example, the array has rank 2 (it is two dimensional). The first dimension (axis) has a length of 2, the second dimension has a length of 3.

[[ 1., 0., 0.] ,
 [ o., 1., 2.]]

Some of the attributes of an ndarray object are:

ndarray.ndim
The number of axes (dimensions) or rank of the array.

ndarray.shape
The dimensions of the array. This is a tuple of integers indicating the size of the array in each dimension. For an array of n rows and m columns, shape will be (n, m). The length of the shape tuple is therefore the rank ndim.

ndarray.size
The total number of elements of the array. This is equal to the product of the elements of shape.

ndarray.dtype
An object describing the type of the elements in the array. One can create or specify dtype using standard Python type. Additionally, NumPy provides types of its own, numpy.int32, numpy . inti 6, and numpy. float 6 4 are some examples.

ndarray.itemsize
The size in bytes of each element of the array. For example, an array of elements of type float 6 4 has itemsize as 8 (=64/8), while one of type complex32 has itemsize as 4 (=32/8). It is equivalent
to ndarray . dtype . itemsize.

>>> import numpy as np
 >>> a=np.array([[ 0, 1, 2, 3, 4] ,[5, 6, 7, 8, 9] , [10, \—1 \—1 12, 13, 14] ] )
 >>> type(a)
 <type 'numpy.ndarray'>
 >>> a.shape
 (3, 5)
 >>> a.ndim
 2
 >>> a.dtype
 dtype('int32')
 >>> a.dtype.name
 ' int32'
 >>> a.itemsize
 4
 >>> a.size
 15

Array creation
There are several ways to create NumPy array, one approach is from a regular Python list or tuple using the array () method. The type of the resulting array is deduced from the type of the elements in the sequences.

>>> import numpy as np
 >>> a=np.array([1,2,3])
 >>> a.dtype
 dtype('int32')
 >>> b=np.array((1.2,3.4,5.6))
 >>> b.dtype
 dtype('float64')

A frequent error consists in calling array () with multiple numeric arguments, rather than providing a single list or tuple of numbers as an argument.

>>> a=np.array(1,2,3,4) # WRONG
 >>> a=np.array([1,2,3,4]) # RIGHT

array () transforms sequence of sequences into two-dimensional arrays, sequences of sequences of sequences into three-dimensional arrays, and so on.

>>> b=np.array([(1.5,2,3), (4,5,6)])
 >>> b
 array([[1.5, 2., 3.], [4. , 5. , 6. ]])

The type of the array can also be explicitly specified at creation time:

>>> c=np.array([[1,2],[3,4]],dtype=np.complex)
 >>> c
 array([[ l.+O.j, 2.+0.j], [ 3.+0.j, 4.+0.j]])

Often the elements of an array are initially unknown, but array size is known. Hence, NumPy offers several functions to create array with initial placeholder content. The function zeros () create an array full of zeros, the function ones () create an array full of ones, and the function empty () create an array whose initial content is random. By default, the dtype of the created array is float 6 4.

>>> np.zeros( (3,4))
 array([[ 0., 0., 0., 0.], [ 0., 0., 0., 0.], [ 0.,o.,0.,0. ] ] )
 >>> np.zeros ( [3, 4 ] )
 array([[ 0.,o.,o.,0.] ,[ o.,0.,o., 0.],[ o.,0., 0.,0.] ] )
 >>> np.ones( (2 ,3, 4),dtype=np.int16)
 array([[[1, 1, 1, 1],
 [1, 1, 1, 1] ,
 [1, 1, 1, 1]],
 [ [1, 1, 1, 1] ,
 [1, 1, 1, 1] ,
 [1, 1, 1, 1]] ], dtype=int16)
 >>> np . empty ( (2,3 ) )
 array([[ 1.39069238e-309, 1.39069238e-309, 1.39069238e-309],
 [ 1.39069238e-309, 1.39069238e-309, 1.39069238e-309]])

To create sequences of numbers, NumPy provides a function arange (), analogous to Python’s built- in function range (), that returns array instead of list.

>>> np.arange(10, 30, 5)
 array( [10, 15, 20, 25])
 >>> np.arange(0, 2, 0.3)
 array([ 0. ,0.3,0.6, 0.9, 1.2, 1.5, 1.8])

When arange () is used with floating point arguments, it is generally not possible to predict the number of elements obtained, due to the finite floating point precision. For this reason, it is usually better to use the function linspace (), that receives as an argument the number of elements that we want, instead of the step:

>>> np.linspace(0,2,5)
 array([ 0. , 0.5, 1. , 1.5, 2. ])
 >>> np.linspace(0,np.pi,4) _
 array([ 0. , 1.04719755, 2.0943951 , 3.14159265])

Printing array
While printing an array, NumPy display it in a similar way to nested lists, but with the following layout:

  • the last axis is printed from left to right.
  • the second-to-last is printed from top to bottom.
  • the rest are also printed from top to bottom, with each slice separated from the next by an empty line.
>>> np.arange(6) # 1d array
 array![0, 1, 2, 3, 4, 5])
 >>>
 >>> np.arange(12).reshape(4,3) # 2d array
 array([[ 0, 1, 2],
 [ 3, 4, 5],
 [ 6, 7, 8],
 [ 9, 10, 11]])
 >>>
 >>> np.arange(24).reshape(2,3,4) # 3d array
 array([[[ 0, 1, 2, 3] ,
 [ 4, 5, 6, 7] ,
 [ 8, 9, 10, 11] ] ,
 [ [12, 13, 14, 15] ,
 [16, 17, 18, 19] ,
 [20, 21, 22, 23] ] ]

If an array is too large to be printed, NumPy automatically skips the central part of the array and only print the corners:

>>> np.arange(10000)
 array] [ 0, 1, 2, ......, 9997, 9998, 9999])
 >>>
 >>> np . arange (10000 ) ..reshape (100, 100 )
 [ o, 1, 2, ..... 97, 98, 99] ,
 [ 100, 101, 102, ....... 197, 198, 199] ,
 [ 200, 201, 202, ...... 297, 298, 299] ,
 ......,
 [9700, 9701, 9702, . 9797, 9798, 9799],
 [9800, 9801, 9802, . 9897, 9898, 9899],
 [9900, 9901, 9902, . 9997, 9998, 9999]])

To disable this behaviour and force NumPy to print the entire array, the printing option set_printoptions need to be changed.

>>> np.set_printoptions(threshold='nan')

Basic Operations
Arithmetic operations when applied on NumPy arrays, they are implemented element-wise.

>> a=np.array([20,30,40,50])
 >>> b=np.arange(4)
 >>> c=a-b
 >>> c
 array ] [20, 29, 38, 47])
 >>> b**2
 array([0, 1, 4, 9])
 >>> a<39 array([ True, True, False, False], dtype=bool)

The product operator * operates element-wise in NumPy arrays. The matrix product can be performed using the dot () function or creating matrix objects (refer section 7.8).

>>> a=np.array([[1,1],
 ... [0,1]])
 >>> b=np.array( [ [2,0],
 ... [3,4]])
 >>> a*b array([ [2, 0],
 [0, 4]])
 >>> np.dot(a,b) array ( [ [5, 4],
 [3, 4]])

Some operations, such as +=, *=, etc., modifies an existing array, rather than creating a new array.

>>> a=np.array ( [[1,2], # a is integer type
 ... [3,4]])
 >>> b=np.array ( [[1.,2 . ], # b is float type
 ......[3.,4.] ] )
 >>> a*=2
 >>> a .
 array( [ [2, 4],
 [6, 8]])
 >>> b+=a
 >>> b
 array( [ [ 3., 6 . ],
 [ 9., 12.]])
 >>> a+=b # b is converted to integer type
 >>> a
 array ( [[ 5, 10],
 [15, 20]])

When operating with arrays of different types, the type of the resulting array corresponds to the more general or precise one (a behavior known as “upcasting”).

>>> a=np.array([1.1,2.2,3.3])
 >>> a.dtype.name
 'float6 4'
 >>> b=np.array([4,5,6])
 >>> b.dtype.name
 'int32'
 >>> c=a+b
 >>> c
 array( [ 5.1, 7.2, 9.3])
 >>> c.dtype.name
 'float64'

Many unary operations, such as computing the sum of all the elements in the array, are implemented as methods of the ndarray class .

>>> a=np.array([[5,8],
 ... [3,6]])
 >>> a.sumO
 22
 >>> a.min()
 3
 >>> a.max()
 8

By default, these operations apply to the array as though it were a list of numbers, regardless of its shape. However, by specifying the axis parameter you can apply an operation along the specified axis of an array:

>>> a=np.arange(12).reshape(3,4)
 >>> a
 array([[ 0, 1, 2, 3],
 [ 4, 5, 6, 7],
 [ 8, 9, 10, 11]])
 >>> a.sum(axis=0) # Sum of each column
 array([12, 15, 18, 21])
 >>> a.min(axis=1) # Minimum of each row
 array([0, 4, 8])
 >>> a.cumsum(axis=1) # Cumulative sum along each row
 array([[ 0, 1, 3, 6],
 [ 4, 9, 15, 22],
 [ 8, 17, 27, 38] ] )

Universal functions
NumPy provides familiar mathematical functions such as sin(), cos(), exp(), etc. In NumPy, these are called “universal functions”. Within NumPy, these functions operate element-wise on an array, producing an array as output.

>>> a=np.arange(3)
 >>> a
 array([0, 1, 2])
 >>> np.exp(a)
 array([ 1. , 2.71828183, 7.3890561 ]
 >>> np.sqrt(a)
 array ( [ 0. , 1. , 1.41421356]

Copying array
When operating and manipulating arrays, their elements sometimes needs to be copied into a new array, and sometimes not. This is often a source of confusion for beginners. Simple assignment does not make copy of array data.

>>> a=np.arange(12)
 >>> b=a
 >>> id(a)
 85660040
 >>> id(b)
 85660040
 >>> b is a # a and b are two names for the same ndarray object
 True

Python passes mutable objects as references, so function calls make no copy.

>>> def F(x):
 ... print id(x)
 ...
 >>> id(a)
 85660040
 >>> F(a)
 85660040

The copy method makes a complete copy of the array data.

>>> c=a.copy()
 >>> c
 array( [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
 >>> c is a
 False
 >>> c[0]=999
 >>> c
 array([999, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
 >>> a
 array][ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])

The Matrix Class
There is also a matrix class, which returns a matrix from an array-like object, or from a string of data. A matrix is a specialized 2-D array that retains its 2-D nature through operations.

>>> np.matrix([[1.0,2.0], [3.0,4.0]])
 matrix ( [ [ 1., 2 . ],
 [3., 4.]] )
 >>> a=np.matrix('1.0 2.0;3.0 4.0')
 >>> a
 matrix( [ [ 1., 2 . ],
 [ 3., 4.]])
 >>> a. T # Transpose of a matrix
 matrix( [ [ 1., 3 . ],
 [ 2., 4.]])
 >>> x=np.matrix('5.0 7.0')
 >>> y=x.T
 >>> y
 matrix ( [ [ 5 . ], [ 7.]])
 >>> a*y Matrix multiplication
 matrix( [ [ 19 . ], [ 43.]])
 >>> a. I # Inverse of a matrix
 matrix([ [-2. , 1. ] , [ 1.5, -0.5]])

Python Programming FundamentalsComputer Science