As a general rule, custom-defined Python functions work with numpy.ndarrays as well. If
the implementation allows, arrays can be used with functions just as int or float objects
can. Consider the following function:
In [ 127 ]: def f(x):
return 3 * x + 5
We can pass standard Python objects as well as numpy.ndarray objects (for which the
operations in the function have to be defined, of course):
In [ 128 ]: f(0.5) # float object
Out[128]: 6.5
In [ 129 ]: f(r) # NumPy array
Out[129]: array([[ 4.32037538, 2.98735285, 12.71955087],
[ 7.9213717 , 0.88472192, 3.34350378],
[ 1.1266386 , 1.37057593, 3.59851226],
[ 1.51400308, 5.61506954, 2.10923576]])
What NumPy does is to simply apply the function f to the object element-wise. In that
sense, by using this kind of operation we do not avoid loops; we only avoid them on the
Python level and delegate the looping to NumPy. On the NumPy level, looping over the
numpy.ndarray object is taken care of by highly optimized code, most of it written in C
and therefore generally much faster than pure Python. This explains the “secret” behind
the performance benefits of using NumPy for array-based use cases.
When working with arrays, one has to take care to call the right functions on the
respective objects. For example, the sin function from the standard math module of
Python does not work with NumPy arrays:
In [ 130 ]: import math
math.sin(r)
Out[130]: TypeError
only length-1 arrays can be converted to Python scalars
The function is designed to handle, for example, float objects — i.e., single numbers, not
arrays. NumPy provides the respective counterparts as so-called ufuncs, or universal
functions:
In [ 131 ]: np.sin(r) # array as input
Out[131]: array([[-0.22460878, -0.62167738, 0.53829193],
[ 0.82702259, -0.98025745, -0.52453206],
[-0.96114497, -0.93554821, -0.45035471],
[-0.91759955, 0.20358986, -0.82124413]])
In [ 132 ]: np.sin(np.pi) # float as input
Out[132]: 1.2246467991473532e-16
NumPy provides a large number of such ufuncs that generalize typical mathematical
functions to numpy.ndarray objects.
[ 22 ]
UNIVERSAL FUNCTIONS
Be careful when using the from library import * approach to importing. Such an approach can cause the NumPy
reference to the ufunc numpy.sin to be replaced by the reference to the math function math.sin. You should, as a
rule, import both libraries by name to avoid confusion: import numpy as np; import math. Then you can use
math.sin alongside np.sin.
Memory Layout
When we first initialized numpy.ndarray objects by using numpy.zero, we provided an
optional argument for the memory layout. This argument specifies, roughly speaking,
which elements of an array get stored in memory next to each other. When working with