Tamás Gál (tamas.gal@fau.de)
The latest version of this notebook is available at https://github.com/escape2020/school2021
import numpy as np
import sys
print(f"Python version: {sys.version}\n"
f"NumPy version: {np.__version__}")
rng = np.random.default_rng(42) # initialise our random number generator
Python version: 3.9.4 | packaged by conda-forge | (default, May 10 2021, 22:10:52) [Clang 11.1.0 ] NumPy version: 1.20.3
def describe(np_obj):
"""Print some information about a NumPy object"""
print("object type: {0}\n"
"size: {o.size}\n"
"ndim: {o.ndim}\n"
"shape: {o.shape}\n"
"dtype: {o.dtype}"
.format(type(np_obj), o=np_obj))
from IPython.core.magic import register_line_magic
@register_line_magic
def shorterr(line):
"""Show only the exception message if one is raised."""
try:
output = eval(line)
except Exception as e:
print("\x1b[31m\x1b[1m{e.__class__.__name__}: {e}\x1b[0m".format(e=e))
else:
return output
del shorterr
ndarray
¶a = np.array([1, 2, 3, 4, 5, 6])
a
array([1, 2, 3, 4, 5, 6])
type(a)
numpy.ndarray
a.size # number of elements
6
a.ndim
1
a.shape
(6,)
a.dtype
dtype('int64')
b = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
b
array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]])
describe(b)
object type: <class 'numpy.ndarray'> size: 10 ndim: 2 shape: (2, 5) dtype: int64
a.min(), a.max(), a.mean(), a.sum()
(1, 6, 3.5, 21)
b
array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]])
b.sum()
55
b.sum(axis=0)
array([ 7, 9, 11, 13, 15])
b.sum(axis=1)
array([15, 40])
a
array([1, 2, 3, 4, 5, 6])
a - 42
array([-41, -40, -39, -38, -37, -36])
a * 42 / np.pi
array([13.36901522, 26.73803044, 40.10704566, 53.47606088, 66.8450761 , 80.21409132])
a**np.e, np.e**a
(array([ 1. , 6.58088599, 19.81299075, 43.30806043, 79.43235917, 130.38703324]), array([ 2.71828183, 7.3890561 , 20.08553692, 54.59815003, 148.4131591 , 403.42879349]))
a * a # element-wise
array([ 1, 4, 9, 16, 25, 36])
a @ a # use np.dot(a, a) if you are using < Python 3.5
91
a
array([1, 2, 3, 4, 5, 6])
a < 3
array([ True, True, False, False, False, False])
a == 4
array([False, False, False, True, False, False])
(a > 3) & (a < 5) # bitwise AND
array([False, False, False, True, False, False])
a < np.array([2, 3, 5, 2, 1, 5])
array([ True, True, True, False, False, False])
np.sum(a > 2)
4
a[0] # indexing starts at 0
1
a[-1] # -1 refers to the last element
6
a[2:6:3] # just like in Python: [start:end:step]
array([3, 6])
a[::-1] # reversing an array
array([6, 5, 4, 3, 2, 1])
b[::-1] # reverses axis 0
array([[ 6, 7, 8, 9, 10], [ 1, 2, 3, 4, 5]])
b
array([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]])
b[0, 2]
3
b[0, 1:4]
array([2, 3, 4])
b[:, 1:4] # the `:` selects the whole axis
array([[2, 3, 4], [7, 8, 9]])
b[:, 2:5:2]
array([[ 3, 5], [ 8, 10]])
b[::-1, ::-1] # reverses both axes
array([[10, 9, 8, 7, 6], [ 5, 4, 3, 2, 1]])
d = np.array([4, 3, 2, 5, 4, 5, 4, 4])
mask = np.array([True, False, False, True, False, False, True, True])
mask
array([ True, False, False, True, False, False, True, True])
d[mask]
array([4, 5, 4, 4])
d[[1, 3, 1, 6]]
array([3, 5, 3, 4])
d
array([4, 3, 2, 5, 4, 5, 4, 4])
d[[False, True, False, False, True, False, False, True]]
array([3, 4, 4])
d[[0, 1, 0, 0, 1, 0, 0, 1]] # although we know that True==1 and False==0
array([4, 3, 4, 4, 3, 4, 4, 3])
d[np.array([0, 1, 0, 0, 1, 0, 0, 1], dtype=bool)]
array([3, 4, 4])
dtype
¶np.dtype
numpy.dtype
a, a.dtype
(array([1, 2, 3, 4, 5, 6]), dtype('int64'))
e = a * 42 / np.pi # NumPy will choose the "right" `dtype` automatically
e, e.dtype
(array([13.36901522, 26.73803044, 40.10704566, 53.47606088, 66.8450761 , 80.21409132]), dtype('float64'))
dtype
s¶np.dtype('f')
dtype('float32')
np.dtype('f8')
dtype('float64')
np.dtype('i')
dtype('int32')
np.dtype('i2')
dtype('int16')
np.dtype('c16')
dtype('complex128')
np.dtype('S8') # String with a fixed length of 8
dtype('S8')
dtype
s¶dt = np.dtype('>i4')
dt.byteorder # endinanness:
'>'
dt.itemsize
4
dt.name
'int32'
dtypes
¶dt = np.dtype([('x', 'f8'), ('y', 'f8'), ('E', 'i4')])
dt.itemsize
20
dt['x']
dtype('float64')
np.dtype("i4, (3,4)f8, c8") # three fields, second field has shape (3, 4)
dtype([('f0', '<i4'), ('f1', '<f8', (3, 4)), ('f2', '<c8')])
dtype
s¶np.array([1, 2, 3], dtype='c8')
array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
dt = np.dtype([('x', 'f8'), ('y', 'f8'), ('E', 'i4')])
f = np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype=dt)
f
array([(1., 2., 3), (4., 5., 6), (7., 8., 9)], dtype=[('x', '<f8'), ('y', '<f8'), ('E', '<i4')])
f['x']
array([1., 4., 7.])
f['E']
array([3, 6, 9], dtype=int32)
f[2]['y']
8.0
np.arange(7)
array([0, 1, 2, 3, 4, 5, 6])
np.ones(10)
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
np.zeros(5)
array([0., 0., 0., 0., 0.])
np.zeros((2, 4))
array([[0., 0., 0., 0.], [0., 0., 0., 0.]])
np.empty(20)
array([2.68156159e+154, 2.68156159e+154, 4.94065646e-323, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000, 2.68156159e+154, 1.72723384e-077, 2.68156159e+154, 1.72723384e-077, 9.88131292e-324, 1.39067116e-308])
np.eye(5)
array([[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]])
np.linspace(1, 2, 10)
array([1. , 1.11111111, 1.22222222, 1.33333333, 1.44444444, 1.55555556, 1.66666667, 1.77777778, 1.88888889, 2. ])
np.ones_like(b)
array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]])
np.ones(10, dtype='i2')
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int16)
rng = np.random.default_rng(42) # always create a generator with a seed!
rng.integers(1, 10, (2, 20))
array([[1, 7, 6, 4, 4, 8, 1, 7, 2, 1, 5, 9, 7, 7, 7, 8, 5, 2, 8, 5], [5, 4, 2, 9, 8, 6, 4, 8, 5, 4, 5, 3, 1, 5, 8, 1, 8, 8, 3, 6]])
rng.random((3, 4))
array([[0.75808774, 0.35452597, 0.97069802, 0.89312112], [0.7783835 , 0.19463871, 0.466721 , 0.04380377], [0.15428949, 0.68304895, 0.74476216, 0.96750973]])
rng.uniform(0, 5, 10)
array([1.62912679, 1.85229853, 2.34777906, 0.9473568 , 0.64960753, 2.37852463, 1.13454675, 3.34906997, 2.18575959, 4.16339098])
g = np.array([1, 2, 3, 4])
h = np.array([5, 6, 7, 8])
g * h # if the shapes match, operations are usually done element-by-element
array([ 5, 12, 21, 32])
g * 23 # as we have already seen, the rule relaxes when the shapes meet certain constraints
array([23, 46, 69, 92])
ValueError: frames are not aligned
if the shapes are incompatibleA (4d array): 5 x 1 x 4 x 1
B (3d array): 7 x 1 x 5
Result (4d array): 5 x 7 x 4 x 5
arr_1 = np.array([[1, 2, 3], [4, 5, 6]])
arr_2 = np.array([[1], [2]])
print('arr_1 shape:', arr_1.shape)
print('arr_2 shape:', arr_2.shape)
arr_3 = arr_1 + arr_2
print('arr_3 shape:', arr_3.shape)
arr_3
arr_1 shape: (2, 3) arr_2 shape: (2, 1) arr_3 shape: (2, 3)
array([[2, 3, 4], [6, 7, 8]])
i = np.arange(20).reshape(4, 5)
i
array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]])
describe(i)
object type: <class 'numpy.ndarray'> size: 20 ndim: 2 shape: (4, 5) dtype: int64
i * np.array([0, 1, 2, 4, 5])
array([[ 0, 1, 4, 12, 20], [ 0, 6, 14, 32, 45], [ 0, 11, 24, 52, 70], [ 0, 16, 34, 72, 95]])
j = np.array([0, 10, 20, 30])
k = np.array([7, 8, 9])
%shorterr j+k
ValueError: operands could not be broadcast together with shapes (4,) (3,)
j[:, np.newaxis] # inserts a new axis, making it two dimensional
array([[ 0], [10], [20], [30]])
j[:, np.newaxis] + k
array([[ 7, 8, 9], [17, 18, 19], [27, 28, 29], [37, 38, 39]])
ufunc
)¶ufunc
is a "vectorized" wrapper for a function that takes a fixed number of scalar inputs and produces a fixed number of scalar outputs.¶NumPy provides a bunch of ufunc
s:
add()
, subtract()
, square()
, log10()
, ...)sin()
, cos()
, tan()
, deg2rad()
, ...)bitwise_and()
, right_shift()
, ...)greater()
, less_equal()
, fmax()
, ...)isnan()
, isinf()
, floor()
, ...)They all are subclasses of np.ufunc
type(np.cos) # they all are subclasses of np.ufunc
numpy.ufunc
ufunc
with np.frompyfunc(func, nin, nout)
¶m = rng.integers(0, 100, 17)
m
array([62, 70, 9, 31, 76, 83, 43, 80, 84, 38, 89, 28, 23, 68, 63, 13, 83])
def step_23(x):
return 1 if x > 23 else 0
%shorterr step_23(m)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
ustep_23 = np.frompyfunc(step_23, 1, 1)
ustep_23(42)
1
ustep_23(5)
0
ustep_23(m)
array([1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1], dtype=object)
ustep_23(rng.integers(0, 100, (2, 3, 4)))
array([[[0, 1, 0, 1], [1, 1, 1, 1], [1, 1, 1, 1]], [[1, 1, 1, 0], [0, 1, 0, 1], [1, 1, 1, 1]]], dtype=object)
original = np.arange(10)
original
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
ref_to_original = original # will point to `original`
ref_to_original[2] = 99
original # changing `ref_to_original` has changed `original`
array([ 0, 1, 99, 3, 4, 5, 6, 7, 8, 9])
single_value = original[5] # single element access returns a copy
single_value
5
single_value = 9999
original # not affected when `single_value` is changed
array([ 0, 1, 99, 3, 4, 5, 6, 7, 8, 9])
original = np.arange(10)
original
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a_slice = original[2:4] # slices return (memory) views
a_slice
array([2, 3])
a_slice[1] = 1000 # changing elements of `original` are actual changes to `a_slice`
original
array([ 0, 1, 2, 1000, 4, 5, 6, 7, 8, 9])
original[3:6] = [101, 102, 103] # changing multiple elements at once
original
array([ 0, 1, 2, 101, 102, 103, 6, 7, 8, 9])