Related
When drawing a contour plot with Python/Matplotlib, the default behaviour (for 1 color) is that negative values are dashed. This is a desired feature for me. However, if I set the color of the lines, all of them are drawn solid. I would like to combine the dashed negatives and custom colors.
How can I plot colored lines, and keep the negative-dahsed style?
Below, I copy (modifying a bit), an example from this tutorial: https://www.oreilly.com/library/view/python-data-science/9781491912126/ch04.html
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
# Default: 1 color, negatives are dashed
plt.contour(X, Y, Z, colors='black')
plt.show()
# Set colormap: all lines are solid
plt.contour(X, Y, Z, cmap='RdBu')
plt.show()
# Set individual colors: all solid lines
plt.contour(X, Y, Z, colors=['b','b','b','r','r','r','r','r'])
plt.show()
Defalut: negatives are dashed.
Set colors via colormap: all have become solid.
Set individual colors: all solid again. I would like the blue lines here to be dashed, automatically, since they are negative values.
Unfortunately, the feature of different linestyles for negative values is not exposed to the user. It is bound to whether or not a single color is used for the lines. This toggles a property monochrome, which in turn decides whether or not to change the linestyle.
A quick hack is hence to set the monochrome attribute to True and reset the linesstyles.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
cntr = plt.contour(X, Y, Z, cmap='RdBu')
cntr.monochrome = True
for col, ls in zip(cntr.collections, cntr._process_linestyles()):
col.set_linestyle(ls)
plt.show()
Since this uses a private ._process_linestyles() attribute, it would not be recommended to use it in production code; but rather use #WarrenWeckesser's answer or the option below.
Here I would like to point to the option to set the linestyles a priori, depending on the levels:
import matplotlib.pyplot as plt
import matplotlib.ticker
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
loc = matplotlib.ticker.MaxNLocator(7)
lvls = loc.tick_values(Z.min(), Z.max())
cntr = plt.contour(X, Y, Z, levels=lvls, cmap='RdBu',
linestyles=np.where(lvls >= 0, "-", "--"))
plt.show()
Here's one way you can do it. (A matplotlib guru might be able to recommend an easier way.)
This example uses a colormap to set the colors, and then changes the linestyles after the contour plot is created. A dashed line is used for negative contours, a dotted line for 0, and a solid line for positive.
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
# Contour with a specified colormap.
result = plt.contour(X, Y, Z, cmap='RdBu')
# Override the linestyles based on the levels.
for line, lvl in zip(result.collections, result.levels):
if lvl < 0:
line.set_linestyle('--')
elif lvl == 0:
line.set_linestyle(':')
else:
# Optional; this is the default.
line.set_linestyle('-')
# Optional--this makes the 0 contour more visible with the
# chosen colormap.
ax = plt.gca()
ax.set_facecolor('#d0d0d0')
plt.show()
Here's the result:
The below code is what I'm playing around with at the minute:
x = np.linspace(0,30,1000)
y = np.linspace(0,30,1000)
X,Y = np.meshgrid(x,y)
def f(x,y):
return x**2 + y**2
Z = f(X,Y)
plt.contour(X, Y, Z, colors='black');
I want this plot to display some forbidden region, say when f(x,y) < 9;
I want this shaded in and added to the plot.
How exactly would I do this?
I've tried using plt.contourf but I can't quite get it working.
I think you can do it this way using contourf, use contourf to fill with a solid color red then mask the region you want to display with your contour chart:
x = np.linspace(0,30,1000)
y = np.linspace(0,30,1000)
X,Y = np.meshgrid(x,y)
def f(x,y):
return x**2 + y**2
Z = f(X,Y)
d = np.ma.array(Z, mask=Z>9)
plt.contour(X, Y, Z, colors='black')
plt.contourf(X, Y, d, colors='red');
Output:
I'm trying to plot this "F" vector function in R3. Together with the Sphere centered at origin of radius Pi/2.
I do not understand why my vectors and my sphere are not at the same position.
I do not understand mlab.axes "axes visibility", I just would like to display the usual x,y,z axes.
Sorry if it is obvious to some, but I went through the standard documentation, few examples and google searches for a few hours, and I'm still clueless.
My current Mayavi Result and Code:
import numpy as np
from mayavi import mlab
# Functions
def h(t):
return np.exp(-1/t) * (1/np.cos(t))
def F(x, y, z):
norm = np.linalg.norm([x, y, z])
h_norm = (h(norm)/norm)
return [x*h_norm, y*h_norm, z*h_norm]
# Vectors
x, y, z = np.meshgrid(np.arange(-2, 2, 0.5),
np.arange(-2, 2, 0.5),
np.arange(-2, 2, 0.5))
u, v, w = F(x, y, z)
src = mlab.pipeline.vector_field(u, v, w)
mlab.pipeline.vectors(src, mask_points=20, scale_factor=.5)
# Ball
dphi, dtheta = np.pi/250.0, np.pi/250.0
[phi, theta] = np.mgrid[0:2*np.pi:dphi,
0:np.pi:dtheta]
r = np.pi / 2
x2 = r*np.sin(theta)*np.cos(phi)
y2 = r*np.sin(theta)*np.sin(phi)
z2 = r*np.cos(theta)
s = mlab.mesh(x2, y2, z2)
#mlab.axes(x_axis_visibility=True, y_axis_visibility=True)
# View it all.
mlab.outline()
mlab.show()
I'm trying to understand and adapt the following code:
import numpy as np
def f(x,y):
return (1 - x / 2 + x**5 + y**3) * np.exp(-x**2 -y**2)
n = 256
x = np.linspace(-3, 3, n)
y = np.linspace(-3, 3, n)
X,Y = np.meshgrid(x, y)
pl.axes([0.025, 0.025, 0.95, 0.95])
pl.contourf(X, Y, f(X, Y), 8, alpha=.75, cmap=pl.cm.hot)
C = pl.contour(X, Y, f(X, Y), 8, colors='black', linewidth=.5)
pl.clabel(C, inline=1, fontsize=10)
pl.xticks(())
pl.yticks(())
pl.show()
Here we have a set of points (x,y) and a value for each point, computed with f(x,y)
Now, I have a set of computational results in the form of x;y;output in a txt output file that I read with csv module for example. The point is that I'm not understanding the data types here, probably the meshgrid. Let's say that each point is a key key in a dictionary FH_DICT so as FH_DICT[key] will play the role of f(x,y) in the code above. But I don't know how to implement it, as the output value for each point it is not easy to express as a mathematical function.
Thanks for your time.
So i have a meshgrid (matrices X and Y) together with scalar data (matrix Z), and i need to visualize this. Preferably some 2D image with colors at the points showing the value of Z there.
I've done some research but haven't found anything which does exactly what i want.
pyplot.imshow(Z) has a good look, but it doesn't take my X and Y matrices, so the axes are wrong and it is unable to handle non-linearly spaced points given by X and Y.
pyplot.pcolor(X,Y,Z) makes colored squares with colors corresponding to the data at one of its corners, so it kind of misrepresents the data (it should show the data in its center or something). In addition it ignores two of the edges from the data matrix.
I pretty sure there must exist some better way somewhere in Matplotlib, but the documentation makes it hard to get an overview. So i'm asking if someone else knows of a better way. Bonus if it allows me to refresh the matrix Z to make an animation.
This looks nice, but it's inefficient:
from pylab import *
origin = 'lower'
delta = 0.025
x = y = arange(-3.0, 3.01, delta)
X, Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)
nr, nc = Z.shape
CS = contourf(
X, Y, Z,
levels = linspace(Z.min(), Z.max(), len(x)),
ls = '-',
cmap=cm.bone,
origin=origin)
CS1 = contour(
CS,
levels = linspace(Z.min(), Z.max(), len(x)),
ls = '-',
cmap=cm.bone,
origin=origin)
show()
It it were me, I'd re-interpolate (using scipy.interpolate) the data to a regular grid and use imshow(), setting the extents to fix the axes.
Edit (per comment):
Animating a contour plot can be accomplished like this, but, like I said, the above is inefficient just plain abuse of the contour plot function. The most efficient way to do what you want is to employ SciPy. Do you have that installed?
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab
import time
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
def animate():
origin = 'lower'
delta = 0.025
x = y = arange(-3.0, 3.01, delta)
X, Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)
CS1 = ax.contourf(
X, Y, Z,
levels = linspace(Z.min(), Z.max(), 10),
cmap=cm.bone,
origin=origin)
for i in range(10):
tempCS1 = contourf(
X, Y, Z,
levels = linspace(Z.min(), Z.max(), 10),
cmap=cm.bone,
origin=origin)
del tempCS1
fig.canvas.draw()
time.sleep(0.1)
Z += x/10
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()
If your meshgrid has uniform spacing, you could continue to use pcolor, but just shift X and Y for the purposes of centering the data at the particular values rather than at the corners.
You could also use a scatter plot to explicitly place points of some size at the exact X and Y points and then set the color to Z:
x = numpy.arange(10)
y = numpy.arange(10)
X,Y = numpy.meshgrid(x,y)
Z = numpy.arange(100).reshape((10,10))
scatter(X,Y,c=Z,marker='s',s=1500)
#I picked a marker size that basically overlapped the symbols at the edges
axis('equal')
or:
pcolor(X+0.5,Y+0.5,Z)
axis('equal')
or as Paul suggested, using one of the contour functions
In case anyone comes across this article looking for what I was looking for, I took the above example and modified it to use imshow with an input stack of frames, instead of generating and using contours on the fly. Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.
def animate_frames(frames):
nBins = frames.shape[0]
frame = frames[0]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
for k in range(nBins):
frame = frames[k]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
del tempCS1
fig.canvas.draw()
#time.sleep(1e-2) #unnecessary, but useful
fig.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)
I also found a much simpler way to go about this whole process, albeit less robust:
fig = plt.figure()
for k in range(nBins):
plt.clf()
plt.imshow(frames[k],cmap=plt.cm.gray)
fig.canvas.draw()
time.sleep(1e-6) #unnecessary, but useful
Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg
Thank you for the help with everything.
The following function creates boxes of half the size at the boundary (as shown in the attached picture).
import matplotlib.pyplot as plt
import numpy as np
from scipy.ndimage.filters import convolve
def pcolor_all(X, Y, C, **kwargs):
X = np.concatenate([X[0:1,:], X], axis=0)
X = np.concatenate([X[:,0:1], X], axis=1)
Y = np.concatenate([Y[0:1,:], Y], axis=0)
Y = np.concatenate([Y[:,0:1], Y], axis=1)
X = convolve(X, [[1,1],[1,1]])/4
Y = convolve(Y, [[1,1],[1,1]])/4
plt.pcolor(X, Y, C, **kwargs)
X, Y = np.meshgrid(
[-1,-0.5,0,0.5,1],
[-2,-1,0,1,2])
C = X**2-Y**2
plt.figure(figsize=(4,4))
pcolor_all(X, Y, C, cmap='gray')
plt.savefig('plot.png')