I am working some meteorological data to plot contour lines on a basemap. The full working example code I have done earlier is here How to remove/omit smaller contour lines using matplotlib. All works fine and I don’t complain with the contour plot. However there is a special case that I have to hide all contour lines over a specific region (irregular lat & lon) on a Basemap.
The only possible solution I can think of is to draw a ploygon lines over a desired region and fill with the color of same as Basemap. After lot of search I found this link How to draw rectangles on a Basemap (code below)
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
def draw_screen_poly( lats, lons, m):
x, y = m( lons, lats )
xy = zip(x,y)
poly = Polygon( xy, facecolor='red', alpha=0.4 )
plt.gca().add_patch(poly)
lats = [ -30, 30, 30, -30 ]
lons = [ -50, -50, 50, 50 ]
m = Basemap(projection='sinu',lon_0=0)
m.drawcoastlines()
m.drawmapboundary()
draw_screen_poly( lats, lons, m )
plt.show()
It seems to work partially. However, I want to draw a region which is irregular.
Any solution is appreciated.
Edit: 1
I have understood where the problem is. It seems that any colour (facecolor) filled within the polygon region does not make it hide anything below. Always it is transparent only, irrespective of alpha value used or not. To illustrate the problem, I have cropped the image which has all three regions ie. contour, basemap region and polygon region. Polygon region is filled with red colour but as you can see, the contour lines are always visible. The particular line I have used in the above code is :-
poly = Polygon(xy, facecolor='red', edgecolor='b')
Therefore the problem is not with the code above. It seem the problem with the polygon fill. But still no solution for this issue. The resulting image (cropped image) is below (See my 2nd edit below the attached image):-
Edit 2:
Taking clue from this http://matplotlib.1069221.n5.nabble.com/Clipping-a-plot-inside-a-polygon-td41950.html which has the similar requirement of mine, I am able to remove some the data. However, the removed data is only from outside of polygon region instead of within. Here is the code I have taken clue from:-
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import RegularPolygon
data = np.arange(100).reshape(10, 10)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.contourf(data)
poly = RegularPolygon([ 0.5, 0.5], 6, 0.4, fc='none',
ec='k', transform=ax.transAxes)
for artist in ax.get_children():
artist.set_clip_path(poly)
Now my question is that what command is used for removing the data within the polygon region?
Didn't noticed there was a claim on this so I might just give the solution already proposed here. You can tinker with the zorder to hide stuff behind your polygon:
import matplotlib
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
# Create a simple contour plot with labels using default colors. The
# inline argument to clabel will control whether the labels are draw
# over the line segments of the contour, removing the lines beneath
# the label
fig = plt.figure()
ax = fig.add_subplot(111)
CS = plt.contour(X, Y, Z,zorder=3)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
rect1 = matplotlib.patches.Rectangle((0,0), 2, 1, color='white',zorder=5)
ax.add_patch(rect1)
plt.show()
, the result is:
Related
I want to draw a figure in matplotib where the axis are displayed within the plot itself not on the side
I have tried the following code from here:
import math
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
a = []
for item in x:
a.append(1/(1+math.exp(-item)))
return a
x = np.arange(-10., 10., 0.2)
sig = sigmoid(x)
plt.plot(x,sig)
plt.show()
The above code displays the figure like this:
What I would like to draw is something as follows (image from Wikipedia)
This question describes a similar problem, but it draws a reference line in the middle but no axis.
One way to do it is using spines:
import math
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
a = []
for item in x:
a.append(1/(1+math.exp(-item)))
return a
x = np.arange(-10., 10., 0.2)
sig = sigmoid(x)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Move left y-axis and bottom x-axis to centre, passing through (0,0)
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
# Eliminate upper and right axes
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
# Show ticks in the left and lower axes only
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
plt.plot(x,sig)
plt.show()
shows:
Basically, I want to comment on the accepted answer (but my rep doesn't allow that).
The use of
ax.spines['bottom'].set_position('center')
draws the x-axes such that it intersect the y-axes in its center. In case of asymmetric ylim this means that x-axis passes NOT through y=0. Jblasco's answer has this drawback, the intersect is at y=0.5 (the center between ymin=0.0 and ymax=1.0)
However, the reference plot of the original question has axes that intersect each other at 0.0 (which is somehow conventional or at least common).
To achieve this behaviour,
ax.spines['bottom'].set_position('zero')
has to be used.
See the following example, where 'zero' makes the axes intersect at 0.0 despite asymmetrically ranges in both x and y.
import numpy as np
import matplotlib.pyplot as plt
#data generation
x = np.arange(-10,20,0.2)
y = 1.0/(1.0+np.exp(-x)) # nunpy does the calculation elementwise for you
fig, [ax0, ax1] = plt.subplots(ncols=2, figsize=(8,4))
# Eliminate upper and right axes
ax0.spines['top'].set_visible(False)
ax0.spines['right'].set_visible(False)
# Show ticks on the left and lower axes only
ax0.xaxis.set_tick_params(bottom='on', top='off')
ax0.yaxis.set_tick_params(left='on', right='off')
# Move remaining spines to the center
ax0.set_title('center')
ax0.spines['bottom'].set_position('center') # spine for xaxis
# - will pass through the center of the y-values (which is 0)
ax0.spines['left'].set_position('center') # spine for yaxis
# - will pass through the center of the x-values (which is 5)
ax0.plot(x,y)
# Eliminate upper and right axes
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
# Show ticks on the left and lower axes only (and let them protrude in both directions)
ax1.xaxis.set_tick_params(bottom='on', top='off', direction='inout')
ax1.yaxis.set_tick_params(left='on', right='off', direction='inout')
# Make spines pass through zero of the other axis
ax1.set_title('zero')
ax1.spines['bottom'].set_position('zero')
ax1.spines['left'].set_position('zero')
ax1.set_ylim(-0.4,1.0)
# No ticklabels at zero
ax1.set_xticks([-10,-5,5,10,15,20])
ax1.set_yticks([-0.4,-0.2,0.2,0.4,0.6,0.8,1.0])
ax1.plot(x,y)
plt.show()
Final remark: If ax.spines['bottom'].set_position('zero') is used but zerois not within the plotted y-range, then the axes is shown at the boundary of the plot closer to zero.
The title of this question is how to draw the spine in the middle and the accepted answer does exactly that but what you guys draw is the sigmoid function and that one passes through y=0.5. So I think what you want is the spine centered according to your data. Matplotlib offers the spine position data for that (see documentation)
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
sigmoid = np.vectorize(sigmoid) #vectorize function
values=np.linspace(-10, 10) #generate values between -10 and 10
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
#spine placement data centered
ax.spines['left'].set_position(('data', 0.0))
ax.spines['bottom'].set_position(('data', 0.0))
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
plt.plot(values, sigmoid(values))
plt.show()
Looks like this (Github):
You can simply add:
plt.axhline()
plt.axvline()
It's not fixed to the center, but it does the job very easily.
Working example:
import matplotlib.pyplot as plt
import numpy as np
def f(x):
return np.sin(x) / (x/100)
delte = 100
Xs = np.arange(-delte, +delte +1, step=0.01)
Ys = np.array([f(x) for x in Xs])
plt.axhline(color='black', lw=0.5)
plt.axvline(color='black', lw=0.5)
plt.plot(Xs, Ys)
plt.show()
If you use matplotlib >= 3.4.2, you can use Pandas syntax and do it in only one line:
plt.gca().spines[:].set_position('center')
You might find it cleaner to do it in 3 lines:
ax = plt.gca()
ax.spines[['top', 'right']].set_visible(False)
ax.spines[['left', 'bottom']].set_position('center')
See documentation here.
Check your matplotlib version with pip freeze and update it with pip install -U matplotlib.
According to latest MPL Documentation:
ax = plt.axes()
ax.spines.left.set_position('zero')
ax.spines.bottom.set_position('zero')
I'm trying to create a custom color bar for a matplotlib PolyCollection. Everything seems ok until I attempt to plot a masked array. The color bar no longer shows the correct colors even though the plot does. Is there a different procedure for plotting masked arrays?
I'm using matplotlib 1.4.0 and numpy 1.8.
Here's my plotting code:
import numpy
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
vertices = numpy.load('vertices.npy')
array = numpy.load('array.npy')
# Take 2d slice out of 3D array
slice_ = array[:, :, 0:1].flatten(order='F')
fig, ax = plt.subplots()
poly = PolyCollection(vertices, array=slice_, edgecolors='black', linewidth=.25)
cm = mpl.colors.ListedColormap([(1.0, 0.0, 0.0), (.2, .5, .2)])
poly.set_cmap(cm)
bounds = [.1, .4, .6]
norm = mpl.colors.BoundaryNorm(bounds, cm.N)
fig.colorbar(poly, ax=ax, orientation='vertical', boundaries=bounds, norm=norm)
ax.add_collection(poly, autolim=True)
ax.autoscale_view()
plt.show()
Here's what the plot looks like:
However, when I plot a masked array with the following change before the slicing:
array = numpy.ma.array(array, mask=array > .5)
I get a color bar that now shows only a single color. Even though both colors are (correctly) still shown in the plot.
Is there some trick to keeping a colobar consistent when plotting a masked array? I know I can use cm.set_bad to change the color of masked values, but that's not quite what I'm looking for. I want the color bar to show up the same between these two plots since both colors and the color bar itself should remain unchanged.
Pass the BoundaryNorm to the PolyCollection, poly. Otherwise, poly.norm gets set to a matplotlib.colors.Normalize instance by default:
In [119]: poly.norm
Out[119]: <matplotlib.colors.Normalize at 0x7faac4dc8210>
I have not stepped through the source code sufficiently to explain exactly what is happening in the code you posted, but I speculate that the interaction of this Normalize instance and the BoundaryNorm make the range of values seen by the fig.colorbar different than what you expected.
In any case, if you pass norm=norm to PolyCollection, then the result looks correct:
import numpy
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.collections as mcoll
import matplotlib.colors as mcolors
numpy.random.seed(4)
N, M = 3, 3
vertices = numpy.random.random((N, M, 2))
array = numpy.random.random((1, N, 2))
# vertices = numpy.load('vertices.npy')
# array = numpy.load('array.npy')
array = numpy.ma.array(array, mask=array > .5)
# Take 2d slice out of 3D array
slice_ = array[:, :, 0:1].flatten(order='F')
fig, ax = plt.subplots()
bounds = [.1, .4, .6]
cm = mpl.colors.ListedColormap([(1.0, 0.0, 0.0), (.2, .5, .2)])
norm = mpl.colors.BoundaryNorm(bounds, cm.N)
poly = mcoll.PolyCollection(
vertices,
array=slice_,
edgecolors='black', linewidth=.25, norm=norm)
poly.set_cmap(cm)
fig.colorbar(poly, ax=ax, orientation='vertical')
ax.add_collection(poly, autolim=True)
ax.autoscale_view()
plt.show()
How would I plot a curve (in 3d perhaps) with something to show the direction that it's going. For example, to show that a circular plane curve is going clockwise or counterclockwise.
A curve like the one here,
http://mathworld.wolfram.com/CauchyIntegralFormula.html
I am not sure even if there is a comparable function right now, so I don't have an example to show you.
Thanks for reading.
Edit: I search quite a bit on this, don't think you can do this on gnuplot either.
Interesting question. I have no time for more than a quick and dirty hack, so here we go (liberally inspired from the code in mpl streamplot)
import matplotlib.lines as mlines
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
def add_arrow_to_line2D(
axes, line, arrow_locs=[0.2, 0.4, 0.6, 0.8],
arrowstyle='-|>', arrowsize=1, transform=None):
"""
Add arrows to a matplotlib.lines.Line2D at selected locations.
Parameters:
-----------
axes:
line: list of 1 Line2D obbject as returned by plot command
arrow_locs: list of locations where to insert arrows, % of total length
arrowstyle: style of the arrow
arrowsize: size of the arrow
transform: a matplotlib transform instance, default to data coordinates
Returns:
--------
arrows: list of arrows
"""
if (not(isinstance(line, list)) or not(isinstance(line[0],
mlines.Line2D))):
raise ValueError("expected a matplotlib.lines.Line2D object")
x, y = line[0].get_xdata(), line[0].get_ydata()
arrow_kw = dict(arrowstyle=arrowstyle, mutation_scale=10 * arrowsize)
if transform is None:
transform = axes.transData
arrows = []
for loc in arrow_locs:
s = np.cumsum(np.sqrt(np.diff(x) ** 2 + np.diff(y) ** 2))
n = np.searchsorted(s, s[-1] * loc)
arrow_tail = (x[n], y[n])
arrow_head = (np.mean(x[n:n + 2]), np.mean(y[n:n + 2]))
p = mpatches.FancyArrowPatch(
arrow_tail, arrow_head, transform=transform,
**arrow_kw)
axes.add_patch(p)
arrows.append(p)
return arrows
fig, ax = plt.subplots(1, 1)
t = np.linspace(0., 4*np.pi, 100.)
line = ax.plot(np.log(t+1)*np.cos(t), np.log(t+1)*np.sin(t),"-")
add_arrow_to_line2D(ax, line, arrow_locs=[0.1, 0.2, 0.3, 0.4, 0.6, 0.8, 0.99],
arrowsize=1.5)
ax.axis("equal")
ax.set_xlim([-4., 4.])
ax.set_ylim([-4., 4.])
plt.show()
I want to show how two values overlap each other in x and y axes. In my case these are some observation data in form of time series, but I believe that this is not relevant.
I would like to achieve something like this:
http://druid.if.uj.edu.pl/~pawel/rect3001.png
Is it possible in matplotlib?
Here's a good example. I adapted it slightly from the gallery.
import numpy as np
import matplotlib.cm as cm
from matplotlib.pyplot import figure, show, rc
# force square figure and square axes looks better for polar, IMO
fig = figure(figsize=(8,8))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=False)
N = 20
theta = np.arange(0.0, 2*np.pi, 2*np.pi/N)
radii = 10*np.random.rand(N)
width = np.pi/4*np.random.rand(N)
bars = ax.bar(theta, radii, width=width, bottom=0.0)
for r,bar in zip(radii, bars):
bar.set_facecolor( cm.jet(r/10.))
bar.set_alpha(0.5)
show()
I am quite used to working with matlab and now trying to make the shift matplotlib and numpy. Is there a way in matplotlib that an image you are plotting occupies the whole figure window.
import numpy as np
import matplotlib.pyplot as plt
# get image im as nparray
# ........
plt.figure()
plt.imshow(im)
plt.set_cmap('hot')
plt.savefig("frame.png")
I want the image to maintain its aspect ratio and scale to the size of the figure ... so when I do savefig it exactly the same size as the input figure, and it is completely covered by the image.
Thanks.
I did this using the following snippet.
#!/usr/bin/env python
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from pylab import *
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2-Z1 # difference of Gaussians
ax = Axes(plt.gcf(),[0,0,1,1],yticks=[],xticks=[],frame_on=False)
plt.gcf().delaxes(plt.gca())
plt.gcf().add_axes(ax)
im = plt.imshow(Z, cmap=cm.gray)
plt.show()
Note the grey border on the sides is related to the aspect rario of the Axes which is altered by setting aspect='equal', or aspect='auto' or your ratio.
Also as mentioned by Zhenya in the comments Similar StackOverflow Question
mentions the parameters to savefig of bbox_inches='tight' and pad_inches=-1 or pad_inches=0
You can use a function like the one below.
It calculates the needed size for the figure (in inches) according to the resolution in dpi you want.
import numpy as np
import matplotlib.pyplot as plt
def plot_im(image, dpi=80):
px,py = im.shape # depending of your matplotlib.rc you may
have to use py,px instead
#px,py = im[:,:,0].shape # if image has a (x,y,z) shape
size = (py/np.float(dpi), px/np.float(dpi)) # note the np.float()
fig = plt.figure(figsize=size, dpi=dpi)
ax = fig.add_axes([0, 0, 1, 1])
# Customize the axis
# remove top and right spines
ax.spines['right'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
# turn off ticks
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.imshow(im)
plt.show()
Here's a minimal object-oriented solution:
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([0, 0, 1, 1], frameon=False, xticks=[], yticks=[])
Testing it out with
ax.imshow([[0]])
fig.savefig('test.png')
saves out a uniform purple block.
edit: As #duhaime points out below, this requires the figure to have the same aspect as the axes.
If you'd like the axes to resize to the figure, add aspect='auto' to imshow.
If you'd like the figure to resize to be resized to the axes, add
from matplotlib import tight_bbox
bbox = fig.get_tightbbox(fig.canvas.get_renderer())
tight_bbox.adjust_bbox(fig, bbox, fig.canvas.fixed_dpi)
after the imshow call. This is the important bit of matplotlib's tight_layout functionality which is implicitly called by things like Jupyter's renderer.