I have one question about the grid lines matplotlib.
I am not sure if this is possible to do or not.
I am plotting the following graph as shown in the image.
I won't give the entire code, since it is involving reading of files.
However the important part of code is here -
X, Y = np.meshgrid(smallX, smallY)
Z = np.zeros((len(X),len(X[0])))
plt.contourf(X, Y, Z, levels, cmap=cm.gray_r, zorder = 1)
plt.colorbar()
...
# Set Border width zero
[i.set_linewidth(0) for i in ax.spines.itervalues()]
gridLineWidth=0.1
ax.set_axisbelow(False)
gridlines = ax.get_xgridlines()+ax.get_ygridlines()
#ax.set_axisbelow(True)
plt.setp(gridlines, 'zorder', 5)
ax.yaxis.grid(True, linewidth=gridLineWidth, linestyle='-', color='0.6')
ax.xaxis.grid(False)
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
Now, my questions is like this -
If I put the grid lines below the contour, they disappear since they are below it.
If I put the grid line above the contour, they looks like what they are looking now.
However, what I would like to have is the grid lines should be visible, but should be below the black portion of the contour. I am not sure if that is possible.
Thank You !
In addition to specifying the z-order of the contours and the gridlines, you could also try masking the zero values of your contoured data.
Here's a small example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
y = np.arange(-2*np.pi, 2*np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) - np.cos(Y)
Z = np.ma.masked_less(Z, 0) # you use mask_equal(yourData, yourMagicValue)
fig, ax = plt.subplots()
ax.contourf(Z, zorder=5, cmap=plt.cm.coolwarm)
ax.xaxis.grid(True, zorder=0)
ax.yaxis.grid(True, zorder=0)
And the output:
Related
I've been toying around with this problem and am close to what I want but missing that extra line or two.
Basically, I'd like to plot a single line whose color changes given the value of a third array. Lurking around I have found this works well (albeit pretty slowly) and represents the problem
import numpy as np
import matplotlib.pyplot as plt
c = np.arange(1,100)
x = np.arange(1,100)
y = np.arange(1,100)
cm = plt.get_cmap('hsv')
fig = plt.figure(figsize=(5,5))
ax1 = plt.subplot(111)
no_points = len(c)
ax1.set_color_cycle([cm(1.*i/(no_points-1))
for i in range(no_points-1)])
for i in range(no_points-1):
bar = ax1.plot(x[i:i+2],y[i:i+2])
plt.show()
Which gives me this:
I'd like to be able to include a colorbar along with this plot. So far I haven't been able to crack it just yet. Potentially there will be other lines included with different x,y's but the same c, so I was thinking that a Normalize object would be the right path.
Bigger picture is that this plot is part of a 2x2 sub plot grid. I am already making space for the color bar axes object with matplotlib.colorbar.make_axes(ax4), where ax4 with the 4th subplot.
Take a look at the multicolored_line example in the Matplotlib gallery and dpsanders' colorline notebook:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.collections as mcoll
def multicolored_lines():
"""
http://nbviewer.ipython.org/github/dpsanders/matplotlib-examples/blob/master/colorline.ipynb
http://matplotlib.org/examples/pylab_examples/multicolored_line.html
"""
x = np.linspace(0, 4. * np.pi, 100)
y = np.sin(x)
fig, ax = plt.subplots()
lc = colorline(x, y, cmap='hsv')
plt.colorbar(lc)
plt.xlim(x.min(), x.max())
plt.ylim(-1.0, 1.0)
plt.show()
def colorline(
x, y, z=None, cmap='copper', norm=plt.Normalize(0.0, 1.0),
linewidth=3, alpha=1.0):
"""
http://nbviewer.ipython.org/github/dpsanders/matplotlib-examples/blob/master/colorline.ipynb
http://matplotlib.org/examples/pylab_examples/multicolored_line.html
Plot a colored line with coordinates x and y
Optionally specify colors in the array z
Optionally specify a colormap, a norm function and a line width
"""
# Default colors equally spaced on [0,1]:
if z is None:
z = np.linspace(0.0, 1.0, len(x))
# Special case if a single number:
# to check for numerical input -- this is a hack
if not hasattr(z, "__iter__"):
z = np.array([z])
z = np.asarray(z)
segments = make_segments(x, y)
lc = mcoll.LineCollection(segments, array=z, cmap=cmap, norm=norm,
linewidth=linewidth, alpha=alpha)
ax = plt.gca()
ax.add_collection(lc)
return lc
def make_segments(x, y):
"""
Create list of line segments from x and y coordinates, in the correct format
for LineCollection: an array of the form numlines x (points per line) x 2 (x
and y) array
"""
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
return segments
multicolored_lines()
Note that calling plt.plot hundreds of times tends to kill performance.
Using a LineCollection to build multi-colored line segments is much much faster.
I got a problem when I was plotting a 3d figure using matplotlib of python. Using the following python function, I got this figure:
Here X, Y are meshed grids and Z and Z_ are functions of X and Y. C stands for surface color.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
def plot(X, Y, Z, Z_, C):
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(C),
linewidth=0, antialiased=False, shade=False)
surf_ = ax.plot_surface(
X, Y, Z_, rstride=1, cstride=1,
facecolors=cm.jet(C),
linewidth=0, antialiased=False, shade=False)
ax.view_init(elev=7,azim=45)
plt.show()
But now I want to cut this figure horizontally and only the part whose z is between -1 and 2 remain.
What I want, plotted with gnuplot, is this:
I have tried ax.set_zlim3d and ax.set_zlim, but neither of them give me the desired figure. Does anybody know how to do it using python?
Nice conical intersections you have there:)
What you're trying to do should be achieved by setting the Z data you want to ignore to NaN. Using graphene's tight binding band structure as an example:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generate dummy data (graphene tight binding band structure)
kvec = np.linspace(-np.pi,np.pi,101)
kx,ky = np.meshgrid(kvec,kvec)
E = np.sqrt(1+4*np.cos(3*kx/2)*np.cos(np.sqrt(3)/2*ky) + 4*np.cos(np.sqrt(3)/2*ky)**2)
# plot full dataset
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(kx,ky,E,cmap='viridis',vmin=-E.max(),vmax=E.max(),rstride=1,cstride=1)
ax.plot_surface(kx,ky,-E,cmap='viridis',vmin=-E.max(),vmax=E.max(),rstride=1,cstride=1)
# focus on Dirac cones
Elim = 1 #threshold
E[E>Elim] = np.nan
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.plot_surface(kx2,ky2,E2,cmap='viridis',vmin=-Elim,vmax=Elim)
#ax.plot_surface(kx2,ky2,-E2,cmap='viridis',vmin=-Elim,vmax=Elim)
ax.plot_surface(kx,ky,E,cmap='viridis',rstride=1,cstride=1,vmin=-Elim,vmax=Elim)
ax.plot_surface(kx,ky,-E,cmap='viridis',rstride=1,cstride=1,vmin=-Elim,vmax=Elim)
plt.show()
The results look like this:
Unfortunately, there are problems with the rendering of the second case: the apparent depth order of the data is messed up in the latter case: cones in the background are rendered in front of the front ones (this is much clearer in an interactive plot). The problem is that there are more holes than actual data, and the data is not connected, which confuses the renderer of plot_surface. Matplotlib has a 2d renderer, so 3d visualization is a bit of a hack. This means that for complex overlapping surfaces you'll more often than not get rendering artifacts (in particular, two simply connected surfaces are either fully behind or fully in front of one another).
We can get around the rendering bug by doing a bit more work: keeping the data in a single surface by not using nans, but instead colouring the the surface to be invisible where it doesn't interest us. Since the surface we're plotting now includes the entire original surface, we have to set the zlim manually in order to focus on our region of interest. For the above example:
from matplotlib.cm import get_cmap
# create a color mapping manually
Elim = 1 #threshold
cmap = get_cmap('viridis')
colors_top = cmap((E + Elim)/2/Elim) # listed colormap that maps E from [-Elim, Elim] to [0.0, 1.0] for color mapping
colors_bott = cmap((-E + Elim)/2/Elim) # same for -E branch
colors_top[E > Elim, -1] = 0 # set outlying faces to be invisible (100% transparent)
colors_bott[-E < -Elim, -1] = 0
# in nature you would instead have something like this:
#zmin,zmax = -1,1 # where to cut the _single_ input surface (x,y,z)
#cmap = get_cmap('viridis')
#colors = cmap((z - zmin)/(zmax - zmin))
#colors[(z < zmin) | (z > zmax), -1] = 0
# then plot_surface(x, y, z, facecolors=colors, ...)
# or for your specific case where you have X, Y, Z and C:
#colors = get_cmap('viridis')(C)
#colors[(z < zmin) | (z > zmax), -1] = 0
# then plot_surface(x, y, z, facecolors=colors, ...)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# pass the mapped colours as the facecolors keyword arg
s1 = ax.plot_surface(kx, ky, E, facecolors=colors_top, rstride=1, cstride=1)
s2 = ax.plot_surface(kx, ky, -E, facecolors=colors_bott, rstride=1, cstride=1)
# but now we need to manually hide the invisible part of the surface:
ax.set_zlim(-Elim, Elim)
plt.show()
Here's the output:
Note that it looks a bit different from the earlier figures because 3 years have passed in between and the current version of matplotlib (3.0.2) has very different (and much prettier) default styles. In particular, edges are now transparent in surface plots. But the main point is that the rendering bug is gone, which is evident if you start rotating the surface around in an interactive plot.
I am creating a histogram for my data. Interestingly, when I plot my raw data and their histogram together on one plot, they are a "y-flipped" version of each other as follows:
I failed to find out the reason and fix it. My code snippet is as follows:
import math as mt
import numpy as np
import matplotlib.pylab as plt
x = np.random.randn(50)
y = np.random.randn(50)
w = np.random.randn(50)
leftBound, rightBound, topBound, bottomBound = min(x), max(x), max(y), min(y)
# parameters for histogram
x_edges = np.linspace(int(mt.floor(leftBound)), int(mt.ceil(rightBound)), int(mt.ceil(rightBound))-int(mt.floor(leftBound))+1)
y_edges = np.linspace(int(mt.floor(bottomBound)), int(mt.ceil(topBound)), int(mt.ceil(topBound))-int(mt.floor(bottomBound))+1)
# construct the histogram
wcounts = np.histogram2d(x, y, bins=(x_edges, y_edges), normed=False, weights=w)[0]
# wcounts is a 2D array, with each element representing the weighted count in a bins
# show histogram
extent = x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
axes.set_xlabel('x (m)')
axes.set_ylabel('y (m)')
histogram = axes.imshow(np.transpose(wcounts), extent=extent, alpha=1, vmin=0.5, vmax=5, cmap=cm.binary) # alpha controls the transparency
fig.colorbar(histogram)
# show data
axes.plot(x, y, color = '#99ffff')
Since the data here are generated randomly for demonstration, I don't think it helps much, if the problem is with that particular data set. But anyway, if it is something wrong with the code, it still helps.
By default, axes.imshow(z) places array element z[0,0] in the top left corner of the axes (or the extent in this case). You probably want to either add the origin="bottom" argument to your imshow() call or pass a flipped data array, i.e., z[:,::-1].
In matplotlib, I'm looking to create an inset color bar to show the scale of my contour plot, but when I create the contour using contour, the color bar has white stripes running through it, whereas when I use contourf, the colorbar has the proper "smooth" appearance:
How can I get that nice smooth colorbar from the filled contour on my normal contour plot? I'd also be OK with a filled contour where the zero-level can be set to white, I imagine.
Here is code to generate this example:
from numpy import linspace, outer, exp
from matplotlib.pyplot import figure, gca, clf, subplots_adjust, subplot
from matplotlib.pyplot import contour, contourf, colorbar, xlim, ylim, title
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
# Make some data to plot - 2D gaussians
x = linspace(0, 5, 100)
y = linspace(0, 5, 100)
g1 = exp(-((x-0.75)/0.2)**2)
g2 = exp(-((y-4.25)/0.1)**2)
g3 = exp(-((x-3.5)/0.15)**2)
g4 = exp(-((y-1.75)/0.05)**2)
z = outer(g1, g2) + outer(g3, g4)
figure(1, figsize=(13,6.5))
clf()
# Create a contour and a contourf
for ii in range(0, 2):
subplot(1, 2, ii+1)
if ii == 0:
ca = contour(x, y, z, 125)
title('Contour')
else:
ca = contourf(x, y, z, 125)
title('Filled Contour')
xlim(0, 5)
ylim(0, 5)
# Make the axis labels
yt = text(-0.35, 2.55, 'y (units)', rotation='vertical', size=14);
xt = text(2.45, -0.4, 'x (units)', rotation='horizontal', size=14)
# Add color bar
ains = inset_axes(gca(), width='5%', height='60%', loc=2)
colorbar(ca, cax=ains, orientation='vertical', ticks=[round(xx*10.0)/10.0 for xx in linspace(0, 1)])
if ii ==1:
ains.tick_params(axis='y', colors='#CCCCCC')
subplots_adjust(left=0.05, bottom=0.09, right=0.98, top=0.94, wspace=0.12, hspace=0.2)
show()
Edit: I realize now that at the lower resolution, the white striping behavior is hard to distinguish from some light transparency. Here's an example with only 30 contour lines which makes the problem more obvious:
Edit 2: While I am still interested in figuring out how to do this in the general general case (like if there's negative values), in my specific case, I have determined that I can effectively create something that looks like what I want by simply setting the levels of a filled contour to start above the zero-level:
ca = contourf(x, y, z, levels=linspace(0.05, 1, 125))
Which basically looks like what I want:
A simple hack is to set the thickness of the lines in the colorbar to some higher value.
E.g. storing the colorbar object as cb and adding the following lines to your example
for line in cb.lines:
line.set_linewidth(3)
gives
I am creating a histogram for my data. Interestingly, when I plot my raw data and their histogram together on one plot, they are a "y-flipped" version of each other as follows:
I failed to find out the reason and fix it. My code snippet is as follows:
import math as mt
import numpy as np
import matplotlib.pylab as plt
x = np.random.randn(50)
y = np.random.randn(50)
w = np.random.randn(50)
leftBound, rightBound, topBound, bottomBound = min(x), max(x), max(y), min(y)
# parameters for histogram
x_edges = np.linspace(int(mt.floor(leftBound)), int(mt.ceil(rightBound)), int(mt.ceil(rightBound))-int(mt.floor(leftBound))+1)
y_edges = np.linspace(int(mt.floor(bottomBound)), int(mt.ceil(topBound)), int(mt.ceil(topBound))-int(mt.floor(bottomBound))+1)
# construct the histogram
wcounts = np.histogram2d(x, y, bins=(x_edges, y_edges), normed=False, weights=w)[0]
# wcounts is a 2D array, with each element representing the weighted count in a bins
# show histogram
extent = x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
axes.set_xlabel('x (m)')
axes.set_ylabel('y (m)')
histogram = axes.imshow(np.transpose(wcounts), extent=extent, alpha=1, vmin=0.5, vmax=5, cmap=cm.binary) # alpha controls the transparency
fig.colorbar(histogram)
# show data
axes.plot(x, y, color = '#99ffff')
Since the data here are generated randomly for demonstration, I don't think it helps much, if the problem is with that particular data set. But anyway, if it is something wrong with the code, it still helps.
By default, axes.imshow(z) places array element z[0,0] in the top left corner of the axes (or the extent in this case). You probably want to either add the origin="bottom" argument to your imshow() call or pass a flipped data array, i.e., z[:,::-1].