Numpy - arrays, matrices, linear algebra¶

Last week we were working with containers:

  • lists
  • dicts
  • zip
  • collections.Counter

This week we will be working with NumPy.

NumPy is a library for the Python, adding:

  • support for large, multi-dimensional arrays and matrices
  • along with a large collection of high-level mathematical functions to operate on these arrays.

Using NumPy in Python gives functionality comparable to MATLAB. They both allow the user to write fast programs as long as most operations work on arrays or matrices instead of scalars.

Complementary Python packages are available:

  • SciPy is a library that adds more MATLAB-like functionality
  • Matplotlib is a plotting package that provides MATLAB-like plotting functionality.

Internally, both MATLAB and NumPy rely on BLAS and LAPACK for efficient linear algebra computations.


In this notebook we:

  • review the basic constructions below indexing/slicing etc
  • review the operations +,* and np.dot(,)
  • draw some graphs
  • do some basic linear algebra
  • manipulate images as matrices

Later I'll show you how to use

  • masks
  • fancy indexing

these are really advanced topics.

Important¶

There is maybe one place where I use a for loop - usually when we use numpy we try and avoid loops because they are slow.

Later we will see how to avoid using conditionals like if statements.

In [1]:
import numpy as np
In [5]:
X = np.arange(10)
In [6]:
type(X)
Out[6]:
numpy.ndarray

reshaping an array is an important operation that we'll use a lot

In [14]:
X.reshape(2,5)
Out[14]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
In [13]:
X.reshape(5,2)
Out[13]:
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7],
       [8, 9]])
In [15]:
Y = X.reshape((5,2))

nearly back to the original array

In [19]:
Y.reshape(1,-1)
Out[19]:
array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])

np.ndarray

An array object represents a multidimensional, homogeneous array of fixed-size items. An associated data-type object describes the format of each element in the array (its byte-order, how many bytes it occupies in memory, whether it is

  • an integer
  • a floating point number
  • or something else (complex)

Arrays can be constructed using

  • np.array
  • np.zeros/np.ones
  • np.arange
In [21]:
Y = np.array([[1,2],[3,4]])
Y[:,0], Y[0,:].reshape(-1,1)
Out[21]:
(array([1, 3]),
 array([[1],
        [2]]))
In [22]:
Y, Y.dot(_[1])
Out[22]:
(array([[1, 2],
        [3, 4]]),
 array([[ 5],
        [11]]))
In [23]:
np.identity(4)
Out[23]:
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])
In [24]:
np.arange(10).shape
Out[24]:
(10,)
In [25]:
np.ones(6,dtype=np.int32)
Out[25]:
array([1, 1, 1, 1, 1, 1], dtype=int32)
In [26]:
np.ones((2,3), dtype=np.int32)
Out[26]:
array([[1, 1, 1],
       [1, 1, 1]], dtype=int32)
In [27]:
?np.ones
Signature: np.ones(shape, dtype=None, order='C', *, like=None)
Docstring:
Return a new array of given shape and type, filled with ones.

Parameters
----------
shape : int or sequence of ints
    Shape of the new array, e.g., ``(2, 3)`` or ``2``.
dtype : data-type, optional
    The desired data-type for the array, e.g., `numpy.int8`.  Default is
    `numpy.float64`.
order : {'C', 'F'}, optional, default: C
    Whether to store multi-dimensional data in row-major
    (C-style) or column-major (Fortran-style) order in
    memory.
like : array_like
    Reference object to allow the creation of arrays which are not
    NumPy arrays. If an array-like passed in as ``like`` supports
    the ``__array_function__`` protocol, the result will be defined
    by it. In this case, it ensures the creation of an array object
    compatible with that passed in via this argument.

    .. versionadded:: 1.20.0

Returns
-------
out : ndarray
    Array of ones with the given shape, dtype, and order.

See Also
--------
ones_like : Return an array of ones with shape and type of input.
empty : Return a new uninitialized array.
zeros : Return a new array setting values to zero.
full : Return a new array of given shape filled with value.


Examples
--------
>>> np.ones(5)
array([1., 1., 1., 1., 1.])

>>> np.ones((5,), dtype=int)
array([1, 1, 1, 1, 1])

>>> np.ones((2, 1))
array([[1.],
       [1.]])

>>> s = (2,2)
>>> np.ones(s)
array([[1.,  1.],
       [1.,  1.]])
File:      ~/anaconda3/lib/python3.9/site-packages/numpy/core/numeric.py
Type:      function

Reshaping¶

Take an array and change its dimension

In [3]:
M = np.arange(1,13); M
Out[3]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])
In [4]:
M.reshape((3,4)), M # make a 2d array
Out[4]:
(array([[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]]),
 array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]))
In [5]:
N = M.reshape((3,4)); N # a 2d array
Out[5]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
In [6]:
N.reshape(12) #back to M
Out[6]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])
In [7]:
N.ravel() #works more generally - watch the videos
Out[7]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])
In [8]:
N.ravel(order='F') #fortran order - column by column
Out[8]:
array([ 1,  5,  9,  2,  6, 10,  3,  7, 11,  4,  8, 12])

Transpose - 1d arrays¶

  • arr.transpose
  • arr.T
In [9]:
N.transpose() 
Out[9]:
array([[ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11],
       [ 4,  8, 12]])
In [10]:
N.T
Out[10]:
array([[ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11],
       [ 4,  8, 12]])
In [11]:
np.arange(5).transpose() # doesn't work
Out[11]:
array([0, 1, 2, 3, 4])
In [16]:
np.arange(5).reshape((5,1)) #col vector
Out[16]:
array([[0],
       [1],
       [2],
       [3],
       [4]])

Slices and indexing¶

this can be complicated

  • [:] is a slice as before but it's behavior depends on the shape
  • [i,j] will give the ij th element of a 2d array (matrix)
  • [:,:] is a 2d slice (ie block in a matrix)

Learn more

In [35]:
np.arange(20)[3:-3] # one dimensional so works as with a list
Out[35]:
array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16])
In [59]:
N # but this is a 2d array
Out[59]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [43]:
N[:2] #first 2 rows
Out[43]:
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
In [44]:
N[0,0], N[0,1], N[1,0], N[-1,-1] # some elements
Out[44]:
(0, 1, 4, 11)
In [46]:
N[:, 1:3] #2nd and 3rd columns
Out[46]:
array([[ 1,  2],
       [ 5,  6],
       [ 9, 10]])
In [49]:
N[1,:] #2nd row
Out[49]:
array([4, 5, 6, 7])
In [61]:
N[:,1] #2nd col
Out[61]:
array([1, 5, 9])
In [52]:
N[:2,:2] #2x2 block
Out[52]:
array([[0, 1],
       [4, 5]])
In [58]:
N[1:,2:] #2x2 block
Out[58]:
array([[ 6,  7],
       [10, 11]])

Block assignment¶

In [48]:
A = np.zeros((4,4))
A
Out[48]:
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
In [49]:
A[:2, :2] = 1
A[2:, 2:] = 3
A[:2, 2:] = 2
A
Out[49]:
array([[1., 1., 2., 2.],
       [1., 1., 2., 2.],
       [0., 0., 3., 3.],
       [0., 0., 3., 3.]])

Copying values¶

In [42]:
B = np.ones_like(A)
B
Out[42]:
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])
In [43]:
B[1:,1:] = A[1:,1:]
B
Out[43]:
array([[1., 1., 1., 1.],
       [1., 1., 2., 2.],
       [1., 0., 3., 3.],
       [1., 0., 3., 3.]])

Row/column operations¶

In [52]:
A[1] = A[1] + A[2] #add rows
A
Out[52]:
array([[ 1.,  1.,  2.,  2.],
       [ 1.,  1., 11., 11.],
       [ 0.,  0.,  3.,  3.],
       [ 0.,  0.,  3.,  3.]])
In [58]:
A[:,0] *= 2 #multiply column by scalar
A
Out[58]:
array([[64.,  1.,  2.,  2.],
       [64.,  1., 11., 11.],
       [ 0.,  0.,  3.,  3.],
       [ 0.,  0.,  3.,  3.]])

Arithemetic in general¶

In [66]:
N
Out[66]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [68]:
2*N # scalar multiplication
Out[68]:
array([[ 0,  2,  4,  6],
       [ 8, 10, 12, 14],
       [16, 18, 20, 22]])
In [78]:
np.ones_like(N) 
Out[78]:
array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]])
In [77]:
N + np.ones_like(N) #addition
Out[77]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

A trick¶

This uses an advanced trick called broadcasting to do the same thing.

Basically the 1 is automatically converted to np.ones_like(N)

In [79]:
N + 1
Out[79]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

More addition¶

In [76]:
N[:,:-1] + np.identity(3) 
Out[76]:
array([[ 1.,  1.,  2.],
       [ 4.,  6.,  6.],
       [ 8.,  9., 11.]])

Multiplication, applying functions¶

  • (*) is element by element
  • f() is element by element
In [83]:
N
Out[83]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [82]:
N*N
Out[82]:
array([[  0,   1,   4,   9],
       [ 16,  25,  36,  49],
       [ 64,  81, 100, 121]])
In [85]:
np.sqrt(N*N)
Out[85]:
array([[ 0.,  1.,  2.,  3.],
       [ 4.,  5.,  6.,  7.],
       [ 8.,  9., 10., 11.]])
In [87]:
np.sqrt(np.arange(10))
Out[87]:
array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,
       2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])
In [61]:
def f(x):
    return np.log(2*x + 1)
In [62]:
f(np.arange(10))
Out[62]:
array([0.        , 1.09861229, 1.60943791, 1.94591015, 2.19722458,
       2.39789527, 2.56494936, 2.7080502 , 2.83321334, 2.94443898])

Speed comparison¶

https://ipython.org/ipython-doc/dev/interactive/magics.html#magic-timeit

In [63]:
%%timeit
f(np.arange(1000))
26.4 µs ± 2.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [64]:
%%timeit
[f(x) for x in np.arange(1000)]
2.23 ms ± 76.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [65]:
g = np.vectorize(f)
In [66]:
%%timeit
g(np.arange(1000))
1.18 ms ± 22.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Scalar product¶

Remember: $ v.v = \| v\|^2$

In [28]:
v = np.arange(5)
v
Out[28]:
array([0, 1, 2, 3, 4])
In [29]:
np.dot(v,v)
Out[29]:
30

this notation is probably better because it avoids parenthisis

In [30]:
v @ v
Out[30]:
30
In [146]:
np.linalg.norm(v) ** 2
Out[146]:
30.0
In [31]:
v @ np.ones_like(v)
Out[31]:
10
In [32]:
np.sum(v)
Out[32]:
10

Matrix x vector, Matrix x Matrix¶

In [33]:
R = np.ones((2,2))
R[0,1] *= -1
R
Out[33]:
array([[ 1., -1.],
       [ 1.,  1.]])
In [34]:
R @ np.array([1,0]), R @ np.array([0,1])
Out[34]:
(array([1., 1.]), array([-1.,  1.]))

matrix multiplication¶

In [35]:
R @ R
Out[35]:
array([[ 0., -2.],
       [ 2.,  0.]])

Graphs¶

we'll use matplotlib to draw some graphs.

Matplotlib can be very complicated so we'll study it properly next week.

In [38]:
import matplotlib.pyplot as plt
import numpy as np

def f(x):
    return np.log(2*x + 1)
In [40]:
plt.plot(f(np.arange(10)) );
In [41]:
T = np.linspace(0, 2*np.pi, 20)
T
Out[41]:
array([0.        , 0.33069396, 0.66138793, 0.99208189, 1.32277585,
       1.65346982, 1.98416378, 2.31485774, 2.64555171, 2.97624567,
       3.30693964, 3.6376336 , 3.96832756, 4.29902153, 4.62971549,
       4.96040945, 5.29110342, 5.62179738, 5.95249134, 6.28318531])
In [7]:
? np.linspace
Signature: np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
Docstring:
Return evenly spaced numbers over a specified interval.

Returns `num` evenly spaced samples, calculated over the
interval [`start`, `stop`].

The endpoint of the interval can optionally be excluded.

.. versionchanged:: 1.16.0
    Non-scalar `start` and `stop` are now supported.

Parameters
----------
start : array_like
    The starting value of the sequence.
stop : array_like
    The end value of the sequence, unless `endpoint` is set to False.
    In that case, the sequence consists of all but the last of ``num + 1``
    evenly spaced samples, so that `stop` is excluded.  Note that the step
    size changes when `endpoint` is False.
num : int, optional
    Number of samples to generate. Default is 50. Must be non-negative.
endpoint : bool, optional
    If True, `stop` is the last sample. Otherwise, it is not included.
    Default is True.
retstep : bool, optional
    If True, return (`samples`, `step`), where `step` is the spacing
    between samples.
dtype : dtype, optional
    The type of the output array.  If `dtype` is not given, infer the data
    type from the other input arguments.

    .. versionadded:: 1.9.0

axis : int, optional
    The axis in the result to store the samples.  Relevant only if start
    or stop are array-like.  By default (0), the samples will be along a
    new axis inserted at the beginning. Use -1 to get an axis at the end.

    .. versionadded:: 1.16.0

Returns
-------
samples : ndarray
    There are `num` equally spaced samples in the closed interval
    ``[start, stop]`` or the half-open interval ``[start, stop)``
    (depending on whether `endpoint` is True or False).
step : float, optional
    Only returned if `retstep` is True

    Size of spacing between samples.


See Also
--------
arange : Similar to `linspace`, but uses a step size (instead of the
         number of samples).
geomspace : Similar to `linspace`, but with numbers spaced evenly on a log
            scale (a geometric progression).
logspace : Similar to `geomspace`, but with the end points specified as
           logarithms.

Examples
--------
>>> np.linspace(2.0, 3.0, num=5)
array([2.  , 2.25, 2.5 , 2.75, 3.  ])
>>> np.linspace(2.0, 3.0, num=5, endpoint=False)
array([2. ,  2.2,  2.4,  2.6,  2.8])
>>> np.linspace(2.0, 3.0, num=5, retstep=True)
(array([2.  ,  2.25,  2.5 ,  2.75,  3.  ]), 0.25)

Graphical illustration:

>>> import matplotlib.pyplot as plt
>>> N = 8
>>> y = np.zeros(N)
>>> x1 = np.linspace(0, 10, N, endpoint=True)
>>> x2 = np.linspace(0, 10, N, endpoint=False)
>>> plt.plot(x1, y, 'o')
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.plot(x2, y + 0.5, 'o')
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.ylim([-0.5, 1])
(-0.5, 1)
>>> plt.show()
File:      ~/anaconda3/lib/python3.6/site-packages/numpy/core/function_base.py
Type:      function
In [42]:
Y = np.sin(T)
X = np.cos(T)
plt.plot(T,X*X + 3*Y)
plt.plot(T,Y);
In [43]:
plt.plot(X,Y);
In [83]:
S = np.linspace(0, 2*np.pi, 400)
plt.plot(np.cos(5*S), np.cos(13*S));

Basic linear algebra¶

  • linear systems
  • eigenvectors, eigenvalues

Linear systems¶

Solve $M.X = Y$

  • np.linalg.solve
  • $X = M^{-1}Y$
In [53]:
M = np.identity(3)
M[0] = np.arange(1,4)
M[1,2] = 4
M
Out[53]:
array([[1., 2., 3.],
       [0., 1., 4.],
       [0., 0., 1.]])
In [54]:
np.linalg.det(M)
Out[54]:
1.0
In [55]:
np.linalg.inv(M)
Out[55]:
array([[ 1., -2.,  5.],
       [ 0.,  1., -4.],
       [ 0.,  0.,  1.]])
In [57]:
np.dot(M,_)
Out[57]:
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
In [59]:
np.linalg.solve(M, np.ones(3))
Out[59]:
array([ 4., -3.,  1.])
In [60]:
np.dot(M, _)
Out[60]:
array([1., 1., 1.])
In [61]:
np.dot(np.linalg.inv(M), np.ones(3))
Out[61]:
array([ 4., -3.,  1.])

Eigenvalues, eigenvectors¶

In [17]:
A = np.arange(9).reshape(3,3)
A
Out[17]:
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
In [18]:
np.linalg.det(A)
Out[18]:
0.0
In [19]:
A[0] - 2* A[1]  + A[2]
Out[19]:
array([0, 0, 0])
In [20]:
vps, basis = np.linalg.eig(A)
In [22]:
vps #eigenvalues
Out[22]:
array([ 1.33484692e+01, -1.34846923e+00, -2.48477279e-16])

3rd eigenvalue is nearly 0¶

In [67]:
vps[2]
Out[67]:
-2.484772788451793e-16
In [23]:
basis 
Out[23]:
array([[ 0.16476382,  0.79969966,  0.40824829],
       [ 0.50577448,  0.10420579, -0.81649658],
       [ 0.84678513, -0.59128809,  0.40824829]])
In [25]:
np.dot(A, basis)
Out[25]:
array([[ 2.19934474e+00, -1.07837039e+00, -3.33066907e-16],
       [ 6.75131503e+00, -1.40518298e-01, -8.88178420e-16],
       [ 1.13032853e+01,  7.97333791e-01, -1.33226763e-15]])

Kernel¶

the third column of the basis is in the kernel

In [24]:
np.dot(A, basis[:,2])
Out[24]:
array([-3.33066907e-16, -4.44089210e-16, -8.88178420e-16])
In [25]:
basis[:,2]/basis[0,2]
Out[25]:
array([ 1., -2.,  1.])
In [70]:
A[0] - 2* A[1]  + A[2]
Out[70]:
array([0, 0, 0])

Calculate the eigenvalues¶

from the characteristic polynomial

$$t \times(t^2 -12t -18)$$
In [26]:
np.poly(A).astype(np.int32) # coeficients of characteristic polynomial
Out[26]:
array([  1, -12, -18,   0], dtype=int32)

Use the quadratic formula¶

In [45]:
disc = np.sqrt(12**2 + 4*18)
disc, 6*np.sqrt(6)
Out[45]:
(14.696938456699069, 14.696938456699067)
In [37]:
(12 + disc )/2, (12 - disc )/2
Out[37]:
(13.348469228349535, -1.3484692283495345)

Exercises¶

  • create a list of 3x3 matrices with random integer coefficients
  • what is the probability that such a matrix is invertible if the coefficients are 0 and 1 ?
  • use np.linalg.eigvals to find the biggest eigenvalue for the matrices

  • make a van der monde matrix with first row a random list of 5 real numbers
  • calculate its determinant and verify the formula
In [120]:
import random

from numpy.linalg import det
from numpy.random import randint
from numpy.linalg import eigvals

#? np.random.randint


X = [ 1   for k in range(100000) if abs(det( randint(0,2,9).reshape(3,3))) < .5 ]

len(X)
Out[120]:
66019
In [161]:
eigvals( 2*np.random.rand(2,2) -1)
Out[161]:
array([0.22865269+0.20750086j, 0.22865269-0.20750086j])
In [117]:
? np.linalg.eigvals
Signature:  np.linalg.eigvals(a)
Docstring:
Compute the eigenvalues of a general matrix.

Main difference between `eigvals` and `eig`: the eigenvectors aren't
returned.

Parameters
----------
a : (..., M, M) array_like
    A complex- or real-valued matrix whose eigenvalues will be computed.

Returns
-------
w : (..., M,) ndarray
    The eigenvalues, each repeated according to its multiplicity.
    They are not necessarily ordered, nor are they necessarily
    real for real matrices.

Raises
------
LinAlgError
    If the eigenvalue computation does not converge.

See Also
--------
eig : eigenvalues and right eigenvectors of general arrays
eigvalsh : eigenvalues of real symmetric or complex Hermitian
           (conjugate symmetric) arrays.
eigh : eigenvalues and eigenvectors of real symmetric or complex
       Hermitian (conjugate symmetric) arrays.

Notes
-----

.. versionadded:: 1.8.0

Broadcasting rules apply, see the `numpy.linalg` documentation for
details.

This is implemented using the ``_geev`` LAPACK routines which compute
the eigenvalues and eigenvectors of general square arrays.

Examples
--------
Illustration, using the fact that the eigenvalues of a diagonal matrix
are its diagonal elements, that multiplying a matrix on the left
by an orthogonal matrix, `Q`, and on the right by `Q.T` (the transpose
of `Q`), preserves the eigenvalues of the "middle" matrix.  In other words,
if `Q` is orthogonal, then ``Q * A * Q.T`` has the same eigenvalues as
``A``:

>>> from numpy import linalg as LA
>>> x = np.random.random()
>>> Q = np.array([[np.cos(x), -np.sin(x)], [np.sin(x), np.cos(x)]])
>>> LA.norm(Q[0, :]), LA.norm(Q[1, :]), np.dot(Q[0, :],Q[1, :])
(1.0, 1.0, 0.0)

Now multiply a diagonal matrix by ``Q`` on one side and by ``Q.T`` on the other:

>>> D = np.diag((-1,1))
>>> LA.eigvals(D)
array([-1.,  1.])
>>> A = np.dot(Q, D)
>>> A = np.dot(A, Q.T)
>>> LA.eigvals(A)
array([ 1., -1.]) # random
File:      ~/anaconda3/lib/python3.8/site-packages/numpy/linalg/linalg.py
Type:      function

Advanced - images as arrays¶

Images can be manipulated like matrix. I learned how to here.

You can use any image you like but please start by dowloading this one

In [45]:
import imageio
import matplotlib.pyplot as plt
In [46]:
im = imageio.imread('https://macbuse.github.io/PROG/anonymous.png')
In [47]:
im.max()
Out[47]:
255
In [173]:
im.shape
Out[173]:
(769, 556)
In [48]:
plt.imshow(im);
In [50]:
im[100:150,:] = 255
im[600:650,:] = 178
plt.imshow(im);
In [ ]:
 
In [67]:
im[100:150,200:400] = 0 
plt.imshow(im);
In [ ]:
 

Convert to a color image¶

color images are 3 layers

  • R = red
  • G = green
  • B = blue

each layer is a 2x2 matrix of floats between 0 and 1

In [52]:
col_im = np.array((im.T/255.,)*3).T #duplicate the layer and convert to floats
In [40]:
plt.imshow(col_im);
In [53]:
R,G,B = col_im.T
In [54]:
G *= 0
B *= 0
In [68]:
plt.imshow(col_im);

It's better to make a copy¶

In [177]:
col_im = np.stack((im.T/255.,)*3).T #duplicate the layer and convert to floats
In [87]:
R,G,B = np.copy(col_im).T
G *= .3

plt.imshow(np.stack((R,G,B)).T);

Resizing an image using strides¶

you can resize (resample) an image but you should really use scipy.transform.resize

In [69]:
plt.imshow(im[::2,:]);
In [70]:
plt.imshow(im[:,::2]);
In [71]:
plt.imshow(im[::2,::2]);

of course¶

there are a lot of other things you can do

In [100]:
xx = np.copy(im)
xx[::5,:] = 0
plt.imshow(xx);
In [100]:
 

duplicate the image à la Warhol¶

In [101]:
yy = im[::2,::2]
dy, dx = yy.shape

pp = np.zeros((2*dy,2*dx), dtype=np.uint8)

pp[:dy,:dx] = yy
pp[:dy,dx:] = yy//4
pp[dy:,:dx] = yy//2

plt.imshow(pp);

In the video he uses concatenate¶

rather than writing blocks

In [102]:
plt.imshow(np.concatenate((yy,yy), axis=1 ));
In [103]:
plt.imshow(np.concatenate((yy, .3*yy), axis=0 ));
In [104]:
tmp = np.concatenate((yy,.5*yy,.25*yy), axis = 1)
In [105]:
plt.imshow( np.concatenate((tmp,.5*tmp,.25*tmp))); 

Exercises¶

  • make Marilyn more interesting by changing colors
  • reverse (reflect) an image vertically/horizontally
  • make an image with stripes or a gradient
  • make an image for a chess board


I have a copy of the image here

you are going to fix my code below to load it

In [108]:
mary = imageio.imread('./marilyn.jpg')
In [109]:
plt.imshow(mary);
In [110]:
mary.shape
Out[110]:
(1024, 1024, 3)
In [111]:
sx = 1024//3
In [112]:
mm = mary[sx:2*sx,sx:2*sx]
In [113]:
plt.imshow(mm);
In [ ]:
 
In [ ]: