Picking a new color for each contour component in matplotlib - matplotlib

Sometimes a specific contour level has several components. For instance:
import numpy as np
import matplotlib.pyplot as plt
delta = 1./100
x = np.arange(-2.0, 3.0, delta)
y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z = Y * Y - X * X * X + X
plt.figure()
CS = plt.contour(X, Y, Z, [0])
How do I color each component using its own color?

I found a way to do it! :) But it's hacky, so I'll leave my answer un-accepted until someone comes up with a better way to do it. Here is my solution (matplotlib 1.4.3).
As noted in the comments, what I asked is not something that matplotlib.contour knows how to do. But after investigating the code a little bit I came up with a solution that works and isn't too bad.
Behind the scenes, a class called QuadContourSet is used to store all the contour paths in "line collections", one line collection per level. The line collections are styled all together.
My idea was to subclass this class and replace the function _get_allsegs_and_allkinds with a function that separates the line collections to one line collection per component, instead of per-level. This is hacky so I named it HackyContourSet but it's good enough for my purposes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import contour
class HackyContourSet(contour.QuadContourSet):
def _get_allsegs_and_allkinds(self):
allkinds = None
allsegs = []
for level in self.levels:
nlist = self.Cntr.trace(level)
nseg = len(nlist) // 2
segs = nlist[:nseg]
# Original code: allsegs.append(segs) - put all level segments in a
# collection. New code: Put each segment in a separate collection.
for seg in segs:
allsegs.append([seg])
# The following line is needed to make QuadContourSet think there are
# more levels, so it would actually draw the additional collections.
self.levels = [0] * len(allsegs)
return allsegs, allkinds
####################
delta = 1./100
x = np.arange(-2.0, 3.0, delta)
y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z = Y * Y - X * X * X + X
plt.figure()
plt.cla()
axes = plt.gca()
CS = HackyContourSet(axes, X, Y, Z, [0], colors=list('rb'))

Related

Why does FuncAnimation revert back to the origin?

I am trying to animate a sample path of Brownian motion by using FuncAnimation, but the animation keeps reverting back to the origin.
Here is my code.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# create the time interval and partition
t = 2.5
n = 100
# How many sample paths?
path_amt = 2
# Create a brownian sample path
def bsp(t, n):
dB = np.sqrt(t / n) * np.random.normal(0, 1, size=n)
B = np.zeros(n+1)
B[1:] = np.cumsum(dB)
return(B)
# Simulate "path_amt" sample paths
def sample_paths(i, t ,n):
BSP = np.zeros((i, n+1))
for k in range(i):
BSP[k,:] = bsp(t, n)
return(BSP)
B_paths = sample_paths(path_amt, t, n)
This part is essentially just coming up with two independent Browinan motions. Each Brownian motion is a 1-d array of length n+1. I then store the two brownian motions in a (2, n+1) array titled B_paths, so each row represents a brownian motion. Here is the code for the animation.
# Create the animation function for the sample path
x = []
y = []
t_axis = np.linspace(0, t, n+1)
fig, ax = plt.subplots()
ax.set_xlim(0, 3)
ax.set_ylim(-4, 4)
line, = ax.plot(0, 0)
def anim_func(i):
x.append(t_axis[int(i * n / t)])
y.append(B_paths[0][int(i * n / t)])
line.set_xdata(x)
line.set_ydata(y)
return line,
animation = FuncAnimation(fig, func = anim_func, \
frames = np.linspace(0, t, n+1), interval = 10)
plt.show()
Because the animation is looping. Once frame reaches t=2.5, then it starts over, but inside your anim_func you don't clear x, y.
You can either modify this function:
def anim_func(i):
x.append(t_axis[int(i * n / t)])
y.append(B_paths[0][int(i * n / t)])
line.set_xdata(x)
line.set_ydata(y)
if i == t:
x.clear()
y.clear()
return line,
Or set repeat=False in the FuncAnimation call.

Matplotlib 3d barplot failing to draw just one face

import numpy as np
import matplotlib.pyplot as plt
x, y = np.array([[x, y] for x in range(5) for y in range(x+1)]).T
z = 1/ (5*x + 5)
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.bar3d(x, y, np.zeros_like(z), dx = 1, dy = 1, dz = z)
yields
How do I get the face at (1,0) to display properly?
There is currently no good solution to this. Fortunately though, it happens only for some viewing angles. So you can choose an angle where it plots fine, e.g.
ax.view_init(azim=-60, elev=25)

Get the y value of a given x

I have a simple question but have not found any answer..
Let's have a look at this code :
from matplotlib import pyplot
import numpy
x=[0,1,2,3,4]
y=[5,3,40,20,1]
pyplot.plot(x,y)
It is plotted and all the points ared linked.
Let's say I want to get the y value of x=1,3.
How can I get the x values matching with y=30 ? (there are two)
Many thanks for your help
You could use shapely to find the intersections:
import matplotlib.pyplot as plt
import numpy as np
import shapely.geometry as SG
x=[0,1,2,3,4]
y=[5,3,40,20,1]
line = SG.LineString(list(zip(x,y)))
y0 = 30
yline = SG.LineString([(min(x), y0), (max(x), y0)])
coords = np.array(line.intersection(yline))
print(coords[:, 0])
fig, ax = plt.subplots()
ax.axhline(y=y0, color='k', linestyle='--')
ax.plot(x, y, 'b-')
ax.scatter(coords[:, 0], coords[:, 1], s=50, c='red')
plt.show()
finds solutions for x at:
[ 1.72972973 2.5 ]
The following code might do what you want. The interpolation of y(x) is straight forward, as the x-values are monotonically increasing. The problem of finding the x-values for a given y is not so easy anymore, once the function is not monotonically increasing as in this case. So you still need to know roughly where to expect the values to be.
import numpy as np
import scipy.interpolate
import scipy.optimize
x=np.array([0,1,2,3,4])
y=np.array([5,3,40,20,1])
#if the independent variable is monotonically increasing
print np.interp(1.3, x, y)
# if not, as in the case of finding x(y) here,
# we need to find the zeros of an interpolating function
y0 = 30.
initial_guess = 1.5 #for the first zero,
#initial_guess = 3.0 # for the secon zero
f = scipy.interpolate.interp1d(x,y,kind="linear")
fmin = lambda x: np.abs(f(x)-y0)
s = scipy.optimize.fmin(fmin, initial_guess, disp=False)
print s
I use python 3.
print(numpy.interp(1.3, x, y))
Y = 30
eps = 1e-6
j = 0
for i, ((x0, x1), (y0, y1)) in enumerate(zip(zip(x[:-1], x[1:]), zip(y[:-1], y[1:]))):
dy = y1 - y0
if abs(dy) < eps:
if y0 == Y:
print('There are infinite number of solutions')
else:
t = (Y - y0)/dy
if 0 < t < 1:
sol = x0 + (x1 - x0)*t
print('solution #{}: {}'.format(j, sol))
j += 1

plotting with meshgrid and imshow

Imshow and meshgrid are not working the way I thought. I have some function defined for a given (x,y) point in 2D that returns a scalar f(x,y). I want to visualize the function f using imshow.
x = np.linspace(0,4)
y = np.linspace(0,1)
X,Y = np.meshgrid(x,y)
Z = np.zeros((50,50))
for i in range(50):
for j in range(50):
Z[i,j] = f(X[i,j],Y[i,j])
fig = plt.figure()
plt.imshow(Z,extent=[0,4,1,0])
plt.show()
This works as expected except in the extent I think it should be [0,4,0,1]... Am I defining the Z[i,j] to each (x,y) pair incorrectly? An explanation for how this works would be great! Thanks!
As far as I am aware, the imshow is normally used to display an image. The extent is then used to define how large it should be, say you might want to give an image as the background of the plot.
Instead I think you will find it more intuitive to use pcolor, a demo can be found here. It works much the same as imshow so you can just supply Z. However, you can also give it the X and Y arrays. This way you can really check if your supplying the values correctly:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,4)
y = np.linspace(0,1)
def f(x, y):
return y * np.sin(x)
X, Y = np.meshgrid(x,y)
Z = np.zeros((50,50))
for i in range(50):
for j in range(50):
Z[i,j] = f(X[i,j],Y[i,j])
plt.pcolor(X, Y, Z)
plt.show()
I have added a function to show it works. Note that if your function is able to handle numpy arrays you can replace the initialisation of Z and the nested for loops with
X, Y = np.meshgrid(x,y)
Z = f(X, Y)
This is cleaner and will be faster to compute.

How to drop connecting lines where the function is discontinuous

I'm plotting some functions that have several discontinuities. Each function is given as a list. I want to connect points with lines only where the function is continuous.
Here is a simplified example of what plot is doing.
x=linspace(0,1,100)
y=zeros(100)
y[x<0.5] = x[x<0.5]
y[x>=0.5] = 1 + x[x>=0.5]
plot(x, y, '-o')
There is a discontinuity at x=0.5, but plot connects all points with lines regardless.
My functions are different of course. They typically have several discontinuities in different places. The criterion for the discontinuity is simple. Say, if the function jumps by more than 0.5, I assume it is discontinuous at that point.
Is there an option in plot to tell it to drop the connecting lines between the points where the function is discontinuous? I recall being able to do that easily with gnuplot.
use nan to break the line into multiple segments:
import numpy as np
from pylab import *
x=linspace(0,1,100)
y=zeros(100)
y[x<0.5] = x[x<0.5]
y[x>=0.5] = 1 + x[x>=0.5]
pos = np.where(np.abs(np.diff(y)) >= 0.5)[0]
x[pos] = np.nan
y[pos] = np.nan
plot(x, y, '-o')
Edit:
to insert nan at discontinuities:
pos = np.where(np.abs(np.diff(y)) >= 0.5)[0]+1
x = np.insert(x, pos, np.nan)
y = np.insert(y, pos, np.nan)
Here is my suggestion for plotting tan(x):
import matplotlib.pyplot as plt
from math import *
x_lim = 3*pi/2
y_lim = 5
n = 1000
X = []
Y = []
Z = []
for i in range(0,2*n):
x = -x_lim + i*x_lim/n
y = tan(x)
if y<y_lim and y>-y_lim:
X.append(x)
Y.append(y)
else:
if len(X)>0 and len(Y)>0:
Z.append([X,Y])
del X,Y
X = []
Y = []
for i in range(0, len(Z)):
plt.plot(Z[i][0],Z[i][1])
plt.grid(True)
plt.show()