The following is part of my notes on scientific computing with python:
# load numpy
import numpy as np
# load scipy
import scipy as sp
# load data visualization
import matplotlib.pyplot as plt # the tidy way
# allows embedding of plots in the notebook
%matplotlib inline
# load image processing
from IPython.display import Image
The scipy
package contains various toolboxes dedicated to common issues in scientific computing. Its different submodules correspond to different applications, such as:
scipy
can be compared to other standard scientific-computing libraries, such as the GSL
(GNU Scientific Library for C and C++), or Matlab’s toolboxes. scipy
is the core package for scientific routines in Python; it is meant to operate efficiently on numpy arrays, so that numpy
and scipy
work hand in hand.
Before implementing a routine, it is worth checking if the desired data processing is not already implemented in Scipy. As non-professional programmers, scientists often tend to re-invent the wheel, which leads to buggy, non-optimal, difficult-to-share and unmaintainable code. By contrast, Scipy‘s routines are optimized and tested, and should therefore be used when possible.
scipy
is composed of task-specific sub-modules:
Package | Function |
---|---|
scipy.cluster |
Vector quantization / Kmeans |
scipy.constants |
Physical and mathematical constants |
scipy.fftpack |
Fourier transform |
scipy.integrate |
Integration routines |
scipy.interpolate |
Interpolation |
scipy.io |
Data input and output |
scipy.linalg |
Linear algebra routines |
scipy.ndimage |
n-dimensional image package |
scipy.odr |
Orthogonal distance regression |
scipy.optimize |
Optimization |
scipy.signal |
Signal processing |
scipy.sparse |
Sparse matrices |
scipy.spatial |
Spatial data structures and algorithms |
scipy.special |
Any special mathematical functions |
scipy.stats |
Statistics |
They all depend on numpy
, but are mostly independent of each other. The standard way of importing Numpy and these Scipy modules is:
import numpy as np
from scipy import stats # same for other sub-modules
The main scipy
namespace mostly contains functions that are really numpy
functions (try scipy.cos
- its really np.cos
). Those are exposed for historical reasons only; there’s usually no reason to use import scipy
in your code: instead use the from scipy
notation.
scipy.io
¶# loading and saving matlab files
from scipy import io as spio # name it whatever you want
a = np.ones((3,3))
#spio.savemat('file.mat', {'a' : a}) # savemat expects a dictionary
#data = spio.loadmat('file.mat', struct_as_record=True)
# scipy.misc
from scipy import misc as spimisc
#spimisc.imread('image.png')
#plt.imread('image.png') # matplotlib also has a similar function
scipy.special
¶A large number of mathematical special functions are important for many computional physics problems. SciPy provides implementations of a very extensive set of special functions. For details, see the list of functions in the reference documention at http://docs.scipy.org/doc/scipy/reference/special.html#module-scipy.special. The docstring of the scipy.special
module is also very well-written, so we won’t list all functions here. Frequently used ones are:
scipy.special.jn()
(nth integer order Bessel function)scipy.special.ellipj()
for the Jacobian elliptic function, ...)scipy.special.gamma()
, also note scipy.special.gammaln(
which will give the log of Gamma to a higher numerical precision.scipy.special.erf()
To demonstrate the typical usage of special functions we will look in more detail at the Bessel functions:
from scipy import special as spec
# from scipy.special import jn, yn, jn_zeros, yn_zeros # could also just import specific functions
# The scipy.special module includes a large number of Bessel-functions
# Here we will use the functions jn and yn, which are the Bessel functions
# of the first and second kind and real-valued order. We also include the
# function jn_zeros and yn_zeros that gives the zeroes of the functions jn
# and yn.
n = 0 # order
x = 0.0
# Bessel function of first kind
print "J_%d(%f) = %f" % (n, x, spec.jn(n, x))
x = 1.0
# Bessel function of second kind
print "Y_%d(%f) = %f" % (n, x, spec.yn(n, x))
J_0(0.000000) = 1.000000 Y_0(1.000000) = 0.088257
x = linspace(0, 10, 100)
fig, ax = plt.subplots()
for n in range(4):
ax.plot(x, spec.jn(n, x), label=r"$J_%d(x)$" % n)
ax.legend();
# zeros of Bessel functions
n = 0 # order
m = 4 # number of roots to compute
spec.jn_zeros(n, m)
array([ 2.40482556, 5.52007811, 8.65372791, 11.79153444])
scispec?
scipy.linalg
¶The scipy.linalg
module provides standard linear algebra operations, relying on an underlying efficient implementation (BLAS, LAPACK). It contains a lot of matrix related functions, including linear equation solving, eigenvalue solvers, matrix functions (for example matrix-exponentiation), a number of different decompositions (SVD, LU, cholesky), etc.
Detailed documetation is available at: http://docs.scipy.org/doc/scipy/reference/linalg.html
Here we will look at how to use some of these functions:
from scipy import linalg as lnal
Linear equation systems on the matrix form
Ax=b
where A is a matrix and x,b are vectors can be solved like:
A = array([[1,2,3], [4,5,6], [7,8,9]])
b = array([1,2,3])
x = lnal.solve(A, b)
x
array([-0.33333333, 0.66666667, 0. ])
# check
np.dot(A, x) - b
array([ 0.00000000e+00, -2.22044605e-16, 0.00000000e+00])
We can also do the same with
AX=B
where A,B,X are matrices:
A = rand(3,3)
B = rand(3,3)
X = lnal.solve(A, B)
X
array([[-0.24793038, 2.12003048, -1.18210657], [ 0.32383755, -1.86063 , 2.05391593], [ 0.15140203, 1.80755783, 0.0934813 ]])
# check
lnal.norm(np.dot(A, X) - B)
3.3450938666784586e-16
The eigenvalue problem for a matrix A:
Avn=λnvn
where vn is the nth eigenvector and λn is the nth eigenvalue.
To calculate eigenvalues of a matrix, use the eigvals
and for calculating both eigenvalues and eigenvectors, use the function eig
:
evals = lnal.eigvals(A)
evals
array([ 1.34326185+0.j, -0.08925560+0.j, 0.43108031+0.j])
evals, evecs = lnal.eig(A)
evals
array([ 1.34326185+0.j, -0.08925560+0.j, 0.43108031+0.j])
evecs
array([[ 6.10823424e-01, 7.55812455e-01, -3.37148378e-04], [ 7.81863685e-01, -6.54297626e-01, -6.07161749e-01], [ 1.24835583e-01, 2.53406547e-02, 7.94578188e-01]])
The eigenvectors corresponding to the nth eigenvalue (stored in evals[n]
) is the nth column in evecs
, i.e., evecs[:,n]
. To verify this, let's try mutiplying eigenvectors with the matrix and compare to the product of the eigenvector and the eigenvalue:
n = 1
lnal.norm(np.dot(A, evecs[:,n]) - evals[n] * evecs[:,n])
1.6547354837046931e-16
There are also more specialized eigensolvers, like the eigh
for Hermitian matrices.
scilinalg?
scipy.fftpack
¶Fourier transforms are one of the universal tools in computational physics, which appear over and over again in different contexts. SciPy provides functions for accessing the classic FFTPACK library from NetLib, which is an efficient and well tested FFT library written in FORTRAN. The SciPy API has a few additional convenience functions, but overall the API is closely related to the original FORTRAN library.
To use the fftpack
module in a python program, include it using:
from scipy import fftpack as fftp
To demonstrate how to do a fast Fourier transform with SciPy, let's look at the FFT of the solution to the damped oscillator from the previous section:
N = len(t)
dt = t[1]-t[0]
# calculate the fast fourier transform
# y2 is the solution to the under-damped oscillator from the previous section
F = fftp.fft(y2[:,0])
# calculate the frequencies for the components in F
w = fftp.fftfreq(N, dt)
fig, ax = plt.subplots(figsize=(9,3))
ax.plot(w, abs(F));
Since the signal is real, the spectrum is symmetric. We therefore only need to plot the part that corresponds to the postive frequencies. To extract that part of the w
and F
we can use some of the indexing tricks for NumPy arrays that we saw in Lecture 2:
indices = where(w > 0) # select only indices for elements that corresponds to positive frequencies
w_pos = w[indices]
F_pos = F[indices]
fig, ax = plt.subplots(figsize=(9,3))
ax.plot(w_pos, abs(F_pos))
ax.set_xlim(0, 5);
As expected, we now see a peak in the spectrum that is centered around 1, which is the frequency we used in the damped oscillator example.
fftpack?
scipy.optimize
¶Optimization (finding minima or maxima of a function) is a large field in mathematics, and optimization of complicated functions or in many variables can be rather involved. Here we will only look at a few very simple cases. For a more detailed introduction to optimization with SciPy see: http://scipy-lectures.github.com/advanced/mathematical_optimization/index.html
To use the optimization module in scipy first include the optimize
module:
from scipy import optimize as opt
# min of single variable function
def f(x):
return 4*x**3 + (x-2)**2 + x**4
fig, ax = plt.subplots()
x = linspace(-5, 3, 100)
ax.plot(x, f(x));
We can use the fmin_bfgs
function to find the minima of a function:
x_min = opt.fmin_bfgs(f, -2)
x_min
Optimization terminated successfully. Current function value: -3.506641 Iterations: 6 Function evaluations: 30 Gradient evaluations: 10
array([-2.67298163])
opt.fmin_bfgs(f, 0.5)
Optimization terminated successfully. Current function value: 2.804988 Iterations: 3 Function evaluations: 15 Gradient evaluations: 5
array([ 0.46961745])
We can also use the brent
or fminbound
functions. They have a bit different syntax and use different algorithms.
opt.brent(f)
0.46961743402759754
opt.fminbound(f, -4, 2)
-2.6729822917520809
To find the root for a function of the form f(x)=0 we can use the fsolve
function. It requires an initial guess:
# root finding:
omega_c = 3.0
def f(omega):
# a transcendental equation: resonance frequencies of a low-Q SQUID terminated microwave resonator
return tan(2*pi*omega) - omega_c/omega
fig, ax = plt.subplots(figsize=(10,4))
x = linspace(0, 3, 1000)
y = f(x)
mask = where(abs(y) > 50)
x[mask] = y[mask] = NaN # get rid of vertical line when the function flip sign
ax.plot(x, y)
ax.plot([0, 3], [0, 0], 'k')
ax.set_ylim(-5,5);
-c:5: RuntimeWarning: divide by zero encountered in divide C:\Anaconda\lib\site-packages\matplotlib\lines.py:503: RuntimeWarning: invalid value encountered in greater_equal return np.alltrue(x[1:] - x[0:-1] >= 0)
opt.fsolve(f, 0.1)
array([ 0.23743014])
opt.fsolve(f, 0.6)
array([ 0.71286972])
opt.fsolve(f, 1.1)
array([ 1.18990285])
opt?
scipy.stats
¶The module scipy.stats
contains statistical tools and probabilistic descriptions of random processes. Random number generators for various random process can be found in numpy.random
.
For a complete documentation of its features, see http://docs.scipy.org/doc/scipy/reference/stats.html.
Note: There is also a very powerful python package for statistical modelling called statsmodels. See http://statsmodels.sourceforge.net for more details.
from scipy import stats as stats
a = np.random.normal(size=10000)
bins = np.arange(-4,5)
bins
array([-4, -3, -2, -1, 0, 1, 2, 3, 4])
histogram = np.histogram(a, bins=bins, normed=True)[0]
bins = 0.5*(bins[1:] + bins[:-1])
bins
array([-3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5])
b = stats.norm.pdf(bins)
plt.plot(bins,histogram)
plt.plot(bins,b)
[<matplotlib.lines.Line2D at 0x835bef0>]
# create a (discreet) random variable with poissionian distribution
X = stats.poisson(3.5) # photon distribution for a coherent state with n=3.5 photons
n = arange(0,15)
fig, axes = plt.subplots(3,1, sharex=True)
# plot the probability mass function (PMF)
axes[0].step(n, X.pmf(n))
# plot the commulative distribution function (CDF)
axes[1].step(n, X.cdf(n))
# plot histogram of 1000 random realizations of the stochastic variable X
axes[2].hist(X.rvs(size=1000));
# create a (continous) random variable with normal distribution
Y = stats.norm()
x = linspace(-5,5,100)
fig, axes = plt.subplots(3,1, sharex=True)
# plot the probability distribution function (PDF)
axes[0].plot(x, Y.pdf(x))
# plot the commulative distributin function (CDF)
axes[1].plot(x, Y.cdf(x));
# plot histogram of 1000 random realizations of the stochastic variable Y
axes[2].hist(Y.rvs(size=1000), bins=50);
X.mean(), X.std(), X.var() # poission distribution
(3.5, 1.8708286933869707, 3.5)
Y.mean(), Y.std(), Y.var() # normal distribution
(0.0, 1.0, 1.0)
# statistical tests:
t_statistic, p_value = stats.ttest_ind(X.rvs(size=1000), X.rvs(size=1000))
print "t-statistic =", t_statistic
print "p-value =", p_value
t-statistic = -0.216308742862 p-value = 0.828769176122
Since the p value is very large we cannot reject the hypothesis that the two sets of random data have different means. To test if the mean of a single sample of data has mean 0.1 (the true mean is 0.0):
stats.ttest_1samp(Y.rvs(size=1000), 0.1)
(array(-2.4326794508336436), 0.015162229922928263)
Low p-value means that we can reject the hypothesis that the mean of Y is 0.1.
Y.mean()
0.0
stats.ttest_1samp(Y.rvs(size=1000), Y.mean())
(array(-0.3414786959804177), 0.73281505241920053)
stats?
numpy.random?
scipy.interpolate
¶Interpolation is simple and convenient in scipy: The interp1d
function, when given arrays describing X and Y data, returns and object that behaves like a function that can be called for an arbitrary value of x (in the range covered by X), and it returns the corresponding interpolated y value:
from scipy import interpolate as intp
def f(x):
return sin(x)
n = arange(0, 10)
x = linspace(0, 9, 100)
y_meas = f(n) + 0.1 * np.random.randn(len(n)) # simulate measurement with noise
y_real = f(x)
linear_interpolation = intp.interp1d(n, y_meas)
y_interp1 = linear_interpolation(x)
cubic_interpolation = intp.interp1d(n, y_meas, kind='cubic')
y_interp2 = cubic_interpolation(x)
fig, ax = plt.subplots(figsize=(10,4))
ax.plot(n, y_meas, 'bs', label='noisy data')
ax.plot(x, y_real, 'k', lw=2, label='true function')
ax.plot(x, y_interp1, 'r', label='linear interp')
ax.plot(x, y_interp2, 'g', label='cubic interp')
ax.legend(loc=3);
intp?
scipy.integrate
¶Numerical evaluation of a function of the type
∫baf(x)dx
is called numerical quadrature, or simply quadature. SciPy provides a series of functions for different kind of quadrature, for example the quad
, dblquad
and tplquad
for single, double and triple integrals, respectively.
from scipy import integrate as intg
The quad
function takes a large number of optional arguments, which can be used to fine-tune the behaviour of the function (try help(quad)
for details).
The basic usage is as follows:
# define a simple function for the integrand
def f(x):
return x
x_lower = 0 # the lower limit of x
x_upper = 1 # the upper limit of x
val, abserr = intg.quad(f, x_lower, x_upper)
print "integral value =", val, ", absolute error =", abserr
integral value = 0.5 , absolute error = 5.55111512313e-15
If we need to pass extra arguments to integrand function we can use the args
keyword argument:
def integrand(x, n):
"""
Bessel function of first kind and order n.
"""
return jn(n, x)
x_lower = 0 # the lower limit of x
x_upper = 10 # the upper limit of x
val, abserr = intg.quad(integrand, x_lower, x_upper, args=(3,))
print val, abserr
0.736675137081 9.3891268825e-13
For simple functions we can use a lambda function (name-less function) instead of explicitly defining a function for the integrand:
val, abserr = intg.quad(lambda x: exp(-x ** 2), -Inf, Inf)
print "numerical =", val, abserr
analytical = sqrt(pi)
print "analytical =", analytical
numerical = 1.77245385091 1.42026367818e-08 analytical = 1.77245385091
As show in the example above, we can also use 'Inf' or '-Inf' as integral limits.
Higher-dimensional integration works in the same way:
def integrand(x, y):
return exp(-x**2-y**2)
x_lower = 0
x_upper = 10
y_lower = 0
y_upper = 10
val, abserr = intg.dblquad(integrand, x_lower, x_upper, lambda x : y_lower, lambda x: y_upper)
print val, abserr
0.785398163397 2.13695246107e-13
Note how we had to pass lambda functions for the limits for the y integration, since these in general can be functions of x.
SciPy provides two different ways to solve ODEs: An API based on the function odeint
, and object-oriented API based on the class ode
. Usually odeint
is easier to get started with, but the ode
class offers some finer level of control.
Here we will use the odeint
functions. For more information about the class ode
, try help(ode)
. It does pretty much the same thing as odeint
, but in an object-oriented fashion.
To use odeint
, first import it from the scipy.integrate
module:
from scipy.integrate import odeint, ode
Note: the import above just imports those methods, so we don't have to use the intg.method
because we've imported them directly. From my readng, this is generally discouraged.
A system of ODEs are usually formulated in standard form before it is attacked numerically. The standard form is:
y′=f(y,t)
where
y=[y1(t),y2(t),...,yn(t)]
and f is some function that gives the derivatives of the function yi(t). To solve an ODE we need to know the function f and an initial condition, y(0).
Note that higher-order ODEs can always be written in this form by introducing new variables for the intermediate derivatives.
Once we have defined the Python function f
and array y_0
(that is f and y(0) in the mathematical formulation), we can use the odeint
function as:
y_t = odeint(f, y_0, t)
where t
is and array with time-coordinates for which to solve the ODE problem. y_t
is an array with one row for each point in time in t
, where each column corresponds to a solution y_i(t)
at that point in time.
We will see how we can implement f
and y_0
in Python code in the examples below.
Let's consider a physical example: The double compound pendulum, described in some detail here: http://en.wikipedia.org/wiki/Double_pendulum
Image(url='http://upload.wikimedia.org/wikipedia/commons/c/c9/Double-compound-pendulum-dimensioned.svg')
The equations of motion of the pendulum are given on the wiki page:
˙θ1=6mℓ22pθ1−3cos(θ1−θ2)pθ216−9cos2(θ1−θ2)
˙θ2=6mℓ28pθ2−3cos(θ1−θ2)pθ116−9cos2(θ1−θ2).
˙pθ1=−12mℓ2[˙θ1˙θ2sin(θ1−θ2)+3gℓsinθ1]
˙pθ2=−12mℓ2[−˙θ1˙θ2sin(θ1−θ2)+gℓsinθ2]
To make the Python code simpler to follow, let's introduce new variable names and the vector notation: x=[θ1,θ2,pθ1,pθ2]
˙x1=6mℓ22x3−3cos(x1−x2)x416−9cos2(x1−x2)
˙x2=6mℓ28x4−3cos(x1−x2)x316−9cos2(x1−x2)
˙x3=−12mℓ2[˙x1˙x2sin(x1−x2)+3gℓsinx1]
˙x4=−12mℓ2[−˙x1˙x2sin(x1−x2)+gℓsinx2]
g = 9.82
L = 0.5
m = 0.1
def dx(x, t):
"""
The right-hand side of the pendulum ODE
"""
x1, x2, x3, x4 = x[0], x[1], x[2], x[3]
dx1 = 6.0/(m*L**2) * (2 * x3 - 3 * np.cos(x1-x2) * x4)/(16 - 9 * np.cos(x1-x2)**2)
dx2 = 6.0/(m*L**2) * (8 * x4 - 3 * np.cos(x1-x2) * x3)/(16 - 9 * np.cos(x1-x2)**2)
dx3 = -0.5 * m * L**2 * ( dx1 * dx2 * np.sin(x1-x2) + 3 * (g/L) * np.sin(x1))
dx4 = -0.5 * m * L**2 * (-dx1 * dx2 * np.sin(x1-x2) + (g/L) * np.sin(x2))
return [dx1, dx2, dx3, dx4]
# choose an initial state
x0 = [pi/4, pi/2, 0, 0]
# time coodinate to solve the ODE for: from 0 to 10 seconds
t = linspace(0, 10, 250)
# solve the ODE problem
x = intg.odeint(dx, x0, t)
# plot the angles as a function of time
fig, axes = plt.subplots(1,2, figsize=(12,4))
axes[0].plot(t, x[:, 0], 'r', label="theta1")
axes[0].plot(t, x[:, 1], 'b', label="theta2")
x1 = + L * np.sin(x[:, 0])
y1 = - L * np.cos(x[:, 0])
x2 = x1 + L * np.sin(x[:, 1])
y2 = y1 - L * np.cos(x[:, 1])
axes[1].plot(x1, y1, 'r', label="pendulum1")
axes[1].plot(x2, y2, 'b', label="pendulum2")
axes[1].set_ylim([-1, 0])
axes[1].set_xlim([1, -1]);
A very simple animation in python
# TODO - doesn't work in ipython inline display - fix!
from IPython.display import clear_output
import time
fig, ax = plt.subplots(figsize=(4,4))
for t_idx, tt in enumerate(t[:200]):
x1 = + L * np.sin(x[t_idx, 0])
y1 = - L * np.cos(x[t_idx, 0])
x2 = x1 + L * np.sin(x[t_idx, 1])
y2 = y1 - L * np.cos(x[t_idx, 1])
ax.cla()
ax.plot([0, x1], [0, y1], 'r.-')
ax.plot([x1, x2], [y1, y2], 'b.-')
ax.set_ylim([-1.5, 0.5])
ax.set_xlim([1, -1])
display(fig)
clear_output()
time.sleep(0.1)
ODE problems are important in computational physics, so we will look at one more example: the damped harmonic oscillation. This problem is well described on the wiki page: http://en.wikipedia.org/wiki/Damping
The equation of motion for the damped oscillator is:
d2xdt2+2ζω0dxdt+ω20x=0
where x is the position of the oscillator, ω0 is the frequency, and ζ is the damping ratio. To write this second-order ODE on standard form we introduce p=dxdt:
dpdt=−2ζω0p−ω20x
dxdt=p
In the implementation of this example we will add extra arguments to the RHS function for the ODE, rather than using global variables as we did in the previous example. As a consequence of the extra arguments to the RHS, we need to pass an keyword argument args
to the odeint
function:
def dy(y, t, zeta, w0):
"""
The right-hand side of the damped oscillator ODE
"""
x, p = y[0], y[1]
dx = p
dp = -2 * zeta * w0 * p - w0**2 * x
return [dx, dp]
# initial state:
y0 = [1.0, 0.0]
# time coodinate to solve the ODE for
t = linspace(0, 10, 1000)
w0 = 2*np.pi*1.0
# solve the ODE problem for three different values of the damping ratio
y1 = intg.odeint(dy, y0, t, args=(0.0, w0)) # undamped
y2 = intg.odeint(dy, y0, t, args=(0.2, w0)) # under damped
y3 = intg.odeint(dy, y0, t, args=(1.0, w0)) # critial damping
y4 = intg.odeint(dy, y0, t, args=(5.0, w0)) # over damped
fig, ax = plt.subplots()
ax.plot(t, y1[:,0], 'k', label="undamped", linewidth=0.25)
ax.plot(t, y2[:,0], 'r', label="under damped")
ax.plot(t, y3[:,0], 'b', label=r"critical damping")
ax.plot(t, y4[:,0], 'g', label="over damped")
ax.legend();
integ?
scipy.signal
¶from scipy import signal as sig
sig?
Object `sig` not found.
scipy.sparse
¶Sparse matrices are often useful in numerical simulations dealing with large systems, if the problem can be described in matrix form where the matrices or vectors mostly contains zeros. Scipy has a good support for sparse matrices, with basic linear algebra operations (such as equation solving, eigenvalue calculations, etc).
There are many possible strategies for storing sparse matrices in an efficient way. Some of the most common are the so-called coordinate form (COO), list of list (LIL) form, and compressed-sparse column CSC (and row, CSR). Each format has some advantanges and disadvantages. Most computational algorithms (equation solving, matrix-matrix multiplication, etc) can be efficiently implemented using CSR or CSC formats, but they are not so intuitive and not so easy to initialize. So often a sparse matrix is initially created in COO or LIL format (where we can efficiently add elements to the sparse matrix data), and then converted to CSC or CSR before used in real calcalations.
For more information about these sparse formats, see e.g. http://en.wikipedia.org/wiki/Sparse_matrix
When we create a sparse matrix we have to choose which format it should be stored in. For example,
from scipy import sparse as sp
# dense matrix
M = array([[1,0,0,0], [0,3,0,0], [0,1,1,0], [1,0,0,1]])
M
array([[1, 0, 0, 0], [0, 3, 0, 0], [0, 1, 1, 0], [1, 0, 0, 1]])
# convert from dense to sparse
A = sp.csr_matrix(M)
A
<4x4 sparse matrix of type '<type 'numpy.int32'>' with 6 stored elements in Compressed Sparse Row format>
# convert from sparse to dense
A.todense()
matrix([[1, 0, 0, 0], [0, 3, 0, 0], [0, 1, 1, 0], [1, 0, 0, 1]])
Note: A more efficient way to create sparse matrices: create an empty matrix and populate with using matrix indexing (avoids creating a potentially large dense matrix)
A = sp.lil_matrix((4,4)) # empty 4x4 sparse matrix
A[0,0] = 1
A[1,1] = 3
A[2,2] = A[2,1] = 1
A[3,3] = A[3,0] = 1
A
<4x4 sparse matrix of type '<type 'numpy.float64'>' with 6 stored elements in LInked List format>
A.todense()
matrix([[ 1., 0., 0., 0.], [ 0., 3., 0., 0.], [ 0., 1., 1., 0.], [ 1., 0., 0., 1.]])
# converting between different sparse matrix formats:
A
<4x4 sparse matrix of type '<type 'numpy.float64'>' with 6 stored elements in LInked List format>
A = sp.csr_matrix(A); A
<4x4 sparse matrix of type '<type 'numpy.float64'>' with 6 stored elements in Compressed Sparse Row format>
A = sp.csc_matrix(A); A
<4x4 sparse matrix of type '<type 'numpy.float64'>' with 6 stored elements in Compressed Sparse Column format>
# we can compute with sparse matrices like with dense matrices:
A.todense()
matrix([[ 1., 0., 0., 0.], [ 0., 3., 0., 0.], [ 0., 1., 1., 0.], [ 1., 0., 0., 1.]])
sp?
scipy.ndimage
¶from scipy import ndimage as ndimg
ndimg?
Object `ndimg` not found.