This is a notebook of self-contained examples of plotting things with Pylab and Pandas that fit into a 280-character Tweet or less. I started compiling these on Twatter a few years ago because I wanted to be able to refer back and see how things were done.
len(dir())
20
%pylab inline
Populating the interactive namespace from numpy and matplotlib
len(dir())
998
So, %pylab inline
added 978 things to the global namespace. Fortunately nowadays it tells you if it clobbered a variable of yours — but of course it can't tell you if you later clobber a variable of its.
Here's an example of 3-D plotting I did in 2017.
%pylab inline
import mpl_toolkits.mplot3d
t=linspace(-9,9,dtype='i')
x,y=meshgrid(t,t)
gca(projection='3d').plot_surface(x,y,x*x^y)
Populating the interactive namespace from numpy and matplotlib
<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x7f9d9347c690>
Newer versions of pandas have a pandas.plotting package that includes a grid of scatterplots, like R's default visualization of dataframes. We can come close to doing this manually in the size of a Tweet:
#Jupyter
%pylab inline
import pandas as pd
rnd=np.random.random
t=arange(10)
f=pd.DataFrame(dict(a=t, b=t+rnd(10), c=rnd(10)))
subplot(221);plot(f.a,f.b,'.')
subplot(222);plot(f.a,f.c,'mo:')
subplot(223);plot(f.b,f.a,'^-')
subplot(224);plot(f.b,f.c,'rv--')
f
Populating the interactive namespace from numpy and matplotlib
a | b | c | |
---|---|---|---|
0 | 0 | 0.069389 | 0.854604 |
1 | 1 | 1.304161 | 0.326364 |
2 | 2 | 2.540106 | 0.084371 |
3 | 3 | 3.213285 | 0.921841 |
4 | 4 | 4.956148 | 0.203230 |
5 | 5 | 5.428098 | 0.432901 |
6 | 6 | 6.176376 | 0.467403 |
7 | 7 | 7.635925 | 0.112893 |
8 | 8 | 8.019817 | 0.715147 |
9 | 9 | 9.415758 | 0.853984 |
Pandas dataframes do have a couple of default methods of plotting them. The plot() and hist() methods don't play nicely with subplots, unfortunately.
The Seaborn package can be installed from the Cheese Shop. It substantially improves the default appearance of matplotlib graphics if you use some of its stylesheets; I like the "seaborn-poster" stylesheet, although its lines are maybe a bit thick for my taste most of the time.
#Jupyter #pandas #matplotlib
%pylab inline
import seaborn
style.use('seaborn-poster') #pretty
rnd = random.random
t = arange(99) * .1
f = pd.DataFrame(dict(a=t, b=t+rnd(99), c=rnd(99)+rnd(99)+rnd(99)))
f.boxplot()
f.hist()
f.plot()
WARNING: pylab import has clobbered these variables: ['f'] `%matplotlib` prevents importing * from pylab and numpy -c:9: FutureWarning: The default value for 'return_type' will change to 'axes' in a future release. To use the future behavior now, set return_type='axes'. To keep the previous behavior and silence this warning, set return_type='dict'.
Populating the interactive namespace from numpy and matplotlib
<matplotlib.axes._subplots.AxesSubplot at 0x7f9d86bad250>
#Jupyter #pandas
%pylab inline
import seaborn
style.use('seaborn-poster')
import pandas as pd
rnd = random.random
u = arange(6)
g = pd.DataFrame(dict(b=u+rnd(6), c=rnd(6)), index=list('ABCDEF'))
print(g.b)
print(g.ix['C'])
xlim(0, 6)
for r in g.itertuples():
text(r.b, r.c, r.Index)
g
Populating the interactive namespace from numpy and matplotlib A 0.743091 B 1.870397 C 2.788198 D 3.826694 E 4.740553 F 5.849244 Name: b, dtype: float64 b 2.788198 c 0.724659 Name: C, dtype: float64
WARNING: pylab import has clobbered these variables: ['f'] `%matplotlib` prevents importing * from pylab and numpy
b | c | |
---|---|---|
A | 0.743091 | 0.650267 |
B | 1.870397 | 0.487187 |
C | 2.788198 | 0.724659 |
D | 3.826694 | 0.974588 |
E | 4.740553 | 0.906029 |
F | 5.849244 | 0.302149 |
list(g.b)
[0.74309074425591537, 1.8703970044511204, 2.7881983059852824, 3.826693933574715, 4.7405526158949387, 5.8492439907286791]
g.index
Index([u'A', u'B', u'C', u'D', u'E', u'F'], dtype='object')
Here's a Mandelbrot in three lines of code.
%%time
#Jupyter
%pylab inline
import seaborn
style.use('seaborn-poster')
rc('figure', figsize=(13,10))
z = c = linspace(-2, 0.5, 1400) + linspace(-1j, 1j, 1400)[:, None]
for i in range(32): z = z**2 + c
imshow(abs(z) < 2, extent=[c.real.min(), c.real.max(), c.imag.min(), c.imag.max()])
Populating the interactive namespace from numpy and matplotlib CPU times: user 1.28 s, sys: 1.25 s, total: 2.53 s Wall time: 2.6 s
It was actually less characters of code with two more lines:
x=linspace(-2,0.5,700)
y=linspace(-1,1,700)[:,None]
z=c=x+y*1j
for i in range(32):z=z*z+c
imshow(abs(z)<2,extent=[x.min(),x.max(),y.min(),y.max()])
Also the older version is sort of less noisy too but it has to use the same coordinates in both dimensions:
w = linspace(-2, 2, 999)
x, y = meshgrid(w, w)
z = c = x + y * 1j
for i in range(99):
z=z**2 + c
imshow(abs(z) < 2, extent=[-2, 2, -2, 2])
c.imag
array([[-1. , -1. , -1. , ..., -1. , -1. , -1. ], [-0.99713877, -0.99713877, -0.99713877, ..., -0.99713877, -0.99713877, -0.99713877], [-0.99427754, -0.99427754, -0.99427754, ..., -0.99427754, -0.99427754, -0.99427754], ..., [ 0.99427754, 0.99427754, 0.99427754, ..., 0.99427754, 0.99427754, 0.99427754], [ 0.99713877, 0.99713877, 0.99713877, ..., 0.99713877, 0.99713877, 0.99713877], [ 1. , 1. , 1. , ..., 1. , 1. , 1. ]])
Still to cover on Mastodon: parametric plots, contour plots (done), histograms without Pandas (done), colorbars (done), aspect ratios (done), stem plots (done), matshow (done), text in plots (done), FFTs (done), log-linear plots (done), convolution, Sympy (done), and plotting multiple data series sequentially (done) and simultaneously (done). tile()? Done. 3d contours.
(newaxis, newaxis)
(None, None)
#Jupyter #DSP
%pylab inline
import seaborn
style.use('seaborn-poster') #pretty
x = linspace(0, 2*pi, 99)
env = bartlett(len(x))
subplot(311)
stem(x, sin(8*x)*env, 'b', 'bo')
stem(x, cos(9*x)*env*2, 'r:', 'r.')
Populating the interactive namespace from numpy and matplotlib
<Container object of 3 artists>
#Jupyter #Python #matplotlib
%pylab inline
import seaborn
style.use('seaborn-poster') #pretty
rcParams['lines.linewidth'] = 0.5
x = arange(1, 64)
f = x * (x-48)**2 - 4096
plot(x, f, label='$\phi$')
plot(x, x * 100, label='$100x$') # 2nd func
m = f.argmax()
axvline(x[m])
text(x[m] + .5, f[m] + 300, 'max')
n = f.argmin()
axvline(x[n])
annotate('min', (x[n], f[n]), textcoords='offset pixels', xytext=(-100,0), arrowprops=dict(arrowstyle='->'), va='center')
legend()
gca().set_xscale('log')
matshow(bitwise_and.outer(x, x), cmap=cm.seismic) # plot #2. seaborn default is grayscale
colorbar()
Populating the interactive namespace from numpy and matplotlib
<matplotlib.colorbar.Colorbar at 0x7f9d8697e410>
%pylab inline
import seaborn
style.use('seaborn-poster')
import sympy
sympy.init_printing()
x = sympy.symbols('x')
y = (x-1)*(x-2)*(x-5)
d = y.diff().simplify()
sympy.plotting.plot(y, d, d.integrate(), (x, -1, 7))
y, y.expand(), d, sympy.solve(d), y.integrate().factor()
Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['f'] `%matplotlib` prevents importing * from pylab and numpy
Suppose we want a function with local extrema at $x = 16$ and $x = 48$. Then our derivative could be $\pm (x-16)(x-48)$.
import sympy
sympy.init_printing()
x = sympy.symbols('x')
(((x-16)*(x-48)).integrate()).factor()
Although it turns out that when your zeroes are that far apart, plotting the derivatives on the same Y-axis is a little impractical, and also I thought it was bad as an example of minimization to have a double zero because it's unclear whether what's being illustrated is that the function has a zero or its derivative does, so here I'm using a somewhat different cubic example.
#Jupyter #Sympy #Numpy #matplotlib
%pylab inline
import seaborn
style.use('seaborn-poster')
import sympy
sympy.init_printing()
x = sympy.symbols('x')
y = x*(x-4)*(x-3) - 3
yp = y.diff()
ypp = yp.diff()
# sympy.plotting can't axvline, so lambdify to plot with numpy
yf, ypf, yppf = [sympy.lambdify(x, f, 'numpy') for f in [y, yp, ypp]]
xs = linspace(0, 5)
plot(xs, zip(yf(xs), ypf(xs), yppf(xs)))
es = sympy.solve(yp) + sympy.solve(ypp)
for e in es:
axvline(e)
y, yp, yp.simplify(), es
Populating the interactive namespace from numpy and matplotlib
Alternatives to zip
include stack((...), -1)
, column_stack((...))
, and stack((...)).T
.
plot(xs, stack((yf(xs), ypf(xs), yppf(xs)), -1))
[<matplotlib.lines.Line2D at 0x7f9d7f801d90>, <matplotlib.lines.Line2D at 0x7f9d7f801e90>, <matplotlib.lines.Line2D at 0x7f9d7f801f90>]
#Jupyter #DSP #FFT #matplotlib #FM
%pylab inline
import seaborn
style.use('seaborn-poster')
# omit 6π sample, avoid leakage
t = linspace(0, 6*pi, 193)[:-1]
# FM synth, -60dB noise
f = sin(t + cos(3*t)/4 + cos(2*t)/4) + random.random(len(t))/999
subplot(2, 1, 1); xlim(t.min(), t.max()); plot(t, f)
# PSD
subplot(2, 1, 2).set_yscale('log'); xticks(arange(0, 101, 3)); stem(abs(fft.fft(f))[:len(f)//2]**2)
Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['f', 'e'] `%matplotlib` prevents importing * from pylab and numpy
<Container object of 3 artists>
#Jupyter #matplotlib
%pylab inline
import seaborn
style.use('seaborn-poster')
# meshgrid alternative
x = tile(linspace(-1, 1), [50, 1])
# make circles round
gca().set_aspect(1)
# use non-grayscale background; set_facecolor in matplotlib ≥2.0
# seaborn-poster grayscale colormap is murder for contour plots
gca().set_axis_bgcolor('#3fcfef')
# paraboloid
contour(x, x.T, x**2 + x.T**2)
colorbar()
Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['f'] `%matplotlib` prevents importing * from pylab and numpy
<matplotlib.colorbar.Colorbar at 0x7f9d7f4039d0>
#Jupyter #matplotlib
%pylab inline
import seaborn
style.use('seaborn-poster')
f = random.random((6, 99)).sum(axis=0)
subplot(411); tick_params(labelbottom=False); h = hist(f, 20)
subplot(412, sharex=gca()); tick_params(labelbottom=False, labelleft=False); boxplot(f, vert=False)
subplot(212, sharex=gca()); plot(h[1][1:], h[0].cumsum(), 'wo-'); ylabel('cdf')
f.sort(); plot(f, arange(len(f)), ':')
xlim(.5, 5.5)
h
Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['f'] `%matplotlib` prevents importing * from pylab and numpy
(array([ 1., 3., 2., 2., 3., 2., 6., 9., 8., 10., 5., 6., 10., 11., 5., 7., 2., 3., 2., 2.]), array([ 1.24792621, 1.39927068, 1.55061515, 1.70195962, 1.85330409, 2.00464856, 2.15599302, 2.30733749, 2.45868196, 2.61002643, 2.7613709 , 2.91271537, 3.06405983, 3.2154043 , 3.36674877, 3.51809324, 3.66943771, 3.82078218, 3.97212664, 4.12347111, 4.27481558]), <a list of 20 Patch objects>)
Here's a self-contained 3-D contour and parametric example, plotted in SVG! But I still want it to use convolution; just not sure quite what to convolve.
#Jupyter #matplotlib #SVG
%pylab inline
import mpl_toolkits.mplot3d
from scipy.signal import convolve2d
import seaborn
style.use('seaborn-poster')
%config InlineBackend.figure_format = 'svg' # or 'pdf'; default 'png'
x = linspace(-10, 10, 20)
y = x[:, None]
rs = x**2 + y**2
f = sin(rs*6)/rs
w = random.random((60, 60)) > .996
g = convolve2d(f, w)
gx, gy = indices(g.shape)
gca(projection='3d').contour(gx, gy, g)
t = linspace(-1, 1, 99)
plot(30*(1+sin(9*t)*t), 30*(1+cos(9*t)*t), t)
matshow(g)
Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['f'] `%matplotlib` prevents importing * from pylab and numpy
<matplotlib.image.AxesImage at 0x7f9d76f6b6d0>
Another way of setting some points in a matrix. This one sets almost exactly the requested number of points.
from scipy.signal import convolve2d
m = zeros((60, 60))
m[random.randint(0, m.shape[0], 10), random.randint(0, m.shape[0], 10)] = random.random(10) - .5
matshow(convolve2d(m, f))
<matplotlib.image.AxesImage at 0x7f9d776a4cd0>