I am trying to plot a circle and a rectangle on the same graph with matplotlib.
Instead, I get an empty plot. What should I do?
Here is my code:
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.axes()
circle = plt.Circle((0, 0), radius=0.75, fc='y')
plt.axis('scaled')
rectangle = plt.Rectangle((10, 10), 100, 100, fc='r')
plt.gca().add_patch(rectangle)
Your code is mostly working. The only issue is
plt.axis('scaled')
Double check your axis limits - this line only works with normal plot objects, not patches, so if you remove this line you should see the rectangle (though you also forgot to add the circle in your pasted code), as long you you update the axis limits (I used plt.axis([-1, 120, -1, 120]) below to achieve this).
A full working listing is:
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.axes()
circle = plt.Circle((0, 0), radius=0.75, fc='y')
plt.gca().add_patch(circle)
rectangle = plt.Rectangle((10, 10), 100, 100, fc='r')
plt.gca().add_patch(rectangle)
plt.axis([-1, 120, -1, 120])
Alternatively, plt.autoscale also works to set the data limits as suggested by tom.
you need to set the axes limits. You can do this with plt.autoscale(), or plt.xlim and plt.ylim. You also need to add the circle patch. Add these lines at the end of your script:
plt.gca().add_patch(circle)
plt.autoscale()
Related
I wish to plot things on top of an image I insert into my figure. I'm not sure how to do that. Here is a simple example where I do my best to place scattered points in the foreground of mario: I specify the order with zorder and call the scatter command last. However, mario is in the foreground and the scattered points are in the background.
How can I make the scattered points appear in front of Mario?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
# load up mario
vortexRF = plt.imread('./mario.png')
imagebox = OffsetImage(vortexRF, zoom=0.08, zorder=1)
# initiate plot
fig, ax = plt.subplots()
# place mario in plot
ab = AnnotationBbox(imagebox, (0, 0), frameon=False)
cbar_ax = fig.add_axes([0.7, .42, 0.1, 0.1])
cbar_ax.add_artist(ab)
cbar_ax.axis('off')
# add scatter plot
NPoints = 1000
ax.scatter(np.random.random(NPoints), np.random.normal(0, 1, NPoints), s=3, c='purple', zorder=2)
# comment that mario should be in the background
ax.set_title("we want the purple dots to be in front of Mario")
# save figure. Mario is behind the scattered points :(
plt.savefig('marioExample')
cbar_ax = fig.add_axes(..., zorder=-1) arranges the z-order between axes. And ax.set_facecolor('none') makes the background of the scatter plot fully transparent (the default is opaque white, hiding everything behind it).
Note that everything that uses an ax is combined into one layer. An ax is either completely in front or completely to the back of another ax. Inside each ax, the elements can have their own z-orders.
To avoid copy-right issues, and to create a standalone example, the code below uses Ada Lovelace's image that comes with matplotlib.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import matplotlib.cbook as cbook
np.random.seed(1234)
# load up Ada's image
with cbook.get_sample_data('ada.png') as image_file:
vortexRF = plt.imread(image_file)
imagebox = OffsetImage(vortexRF, zoom=0.2)
# initiate plot
fig, ax = plt.subplots()
# place Ada in plot
ab = AnnotationBbox(imagebox, (0, 0), frameon=False)
cbar_ax = fig.add_axes([0.6, .42, 0.3, 0.3], zorder=-1)
cbar_ax.add_artist(ab)
cbar_ax.axis('off')
# add scatter plot
ax.scatter(np.random.normal(np.tile(np.random.uniform(0, 1, 5), 1000), .1),
np.random.normal(np.tile(np.random.uniform(0, 1, 5), 1000), .1),
c=np.tile(['fuchsia', 'gold', 'coral', 'deepskyblue', 'chartreuse'], 1000),
s=3, alpha=0.2)
# comment that Ada should be in the background
ax.set_title("we want the dots to be in front of Ada")
# make the background of the scatter plot fully transparent
ax.set_facecolor('none')
plt.show()
PS: Note that you can also add the image on the same ax as the scatter using imshow with an extent. The extent is default expressed in the same data coordinates as the plot in the order (x0, x1, y0, y1). This makes things somewhat simpler. The method using fig.add_axes, however, nicely keeps the original aspect ratio of the image.
ax.imshow(vortexRF, extent=[0.0, 0.4, 0.7, 1.1])
I'd like to create a map using cartopy that draws gridlines that are clipped by the continents so that they only appear over the ocean and not over land. Whether they're actually clipped or whether I just control the drawing order so that my data is drawn over them is not important to me; either way would work.
I'm in Python 3, using cartopy 0.17 and matplotlib 3.1.1.
from cartopy.crs import PlateCarree
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes((0, 0, 1, 1), projection=PlateCarree())
ax.gridlines()
ax.coastlines()
I've tried calling gridlines before and coastlines and vice versa but either way the gridlines end up being drawn over the continents, so clearly the order in which these things are drawn is determined in a different way.
I've also tried this plotting some data using ax.contourf and I get the same thing: the gridlines appear over the opaque colors created by the data. So it's not just the fact that coastlines only draws an outline.
Is this the result you want?
from cartopy.crs import PlateCarree
import cartopy
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes((0, 0, 1, 1), projection=PlateCarree())
ax.add_feature(cartopy.feature.LAND, facecolor='white', zorder=1)
ax.add_feature(cartopy.feature.COASTLINE)
ax.gridlines(zorder=0)
I have to place a circle in a specific spot in an image. The problem is that the image is plotted in an semi-log scale, which distorts the circle unless I use some specific transform. However, when I do that, the circle changes position depending if I save the image as A PDF or PNG. Here's a MWE:
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse, Circle
import numpy as np
from matplotlib.text import OffsetFrom
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(11,5), squeeze=False,
gridspec_kw = {'width_ratios':[3, 1]}, subplot_kw=dict(aspect="auto"))
x=np.logspace(-2,1)
y=np.linspace(.5,0,x.size)
ax=axes[0,0]
ax.semilogx(x, y)
circ = Circle((.5, .5), .1, transform="none", facecolor="none", edgecolor="k")
ax.add_patch(circ)
ax.set(xlim=(1e-2, 1e1), ylim=(0, .6))
fig.savefig("circle.png")
And here are the two outputs depending on how I save the image:
I have also tried using transform=ax.transAxes and, while it preserves the location of the circle, it's not a circle anymore after the semilog transformation.
Any ideas?
I think this is a known issue. The problem is that pdf is always saved with a dpi of 72, while png will take the figure dpi into account.
However, instead of creating a circle directly in the figure or axes, I would recommend playing around with the Annotation BBox tools.
You may create an AnnotationBBox with a DrawingArea inside. The DrawingArea may contain the circle. The coordinates of the DrawingArea are points.
The AnnotationBbox can be placed anywhere on the axes or figure and its position may be specified in some other coordinate system like axes coordinates or data coordinates.
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse, Circle
import numpy as np
from matplotlib.offsetbox import DrawingArea, AnnotationBbox
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(11,5), squeeze=False,
gridspec_kw = {'width_ratios':[3, 1]}, subplot_kw=dict(aspect="auto"))
x=np.logspace(-2,1)
y=np.linspace(.5,0,x.size)
ax=axes[0,0]
ax.semilogx(x, y)
##################
# Axes Coordinates
# Create circle of 10 points radius
da = DrawingArea(1,1, 0, 0)
p = Circle((0, 0), 10)
da.add_artist(p)
# Place box in the middle ((.5,.5)) of the axes.
# Add circle inside drawing area to box
ab = AnnotationBbox(da, (.5,.5), xycoords='axes fraction',
box_alignment=(0, 0), frameon=False, pad=0.0)
ax.add_artist(ab)
###################
# Data Coordinates
# Create circle of 10 points radius
da = DrawingArea(1,1, 0, 0)
p = Circle((0, 0), 10, color="crimson")
da.add_artist(p)
# Place box at (0.1,0.3) in data coordinates.
# Add circle inside drawing area to box
ab = AnnotationBbox(da, (0.1,0.3), xycoords='data',
box_alignment=(0, 0), frameon=False, pad=0.0)
ax.add_artist(ab)
ax.set(xlim=(1e-2, 1e1), ylim=(0, .6))
fig.savefig("circle.png")
fig.savefig("circle.pdf")
plt.show()
The resulting pdf and png will now be identical
I want to draw boxes on an image opened from an array in matplotlib. One way I have found to draw boxes is by using add_patch, but I can't find the way to use it on an image loaded from an array.
This code
arr = np.random.rand(400,400)
fig = plt.imshow(arr)
fig.add_patch(patches.Rectangle((100, 100), 100, 100, fill=False))
produces the error: AttributeError: 'AxesImage' object has no attribute 'add_patch'
You have to add your patch to the matplotlib Axes :
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
arr = np.random.rand(400,400)
fig,ax = plt.subplots(1)
ax.imshow(arr)
rect = patches.Rectangle((100, 100), 100, 100, fill=False)
ax.add_patch(rect)
plt.show()
I need to make a plot (with errorbars) with ellipses as markers. After some searching I came up with Ellipse in matplotlib.patches. Then I could draw the error bars with plt.errorbar. But the problem is that even though I give the error bar command first, the error bars are always drawn in the foreground and the ellipses are drawn on the background, no matter what order I give in the program.
Does any one know of a better way to create an ellipse as a marker (each point will have a different eccentricity) with error bars? Or at least guide me in how to put the error bars in the background?
Here is a minimal example of what I have so far:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from matplotlib.patches import Ellipse
PlotFileName="test.pdf"
pdf = PdfPages(PlotFileName)
fig=plt.figure(1)
ax1=fig.add_subplot(111)
plt.xlim([1,4])
plt.ylim([2,8])
ax1.errorbar([2.5], [5], yerr=[1], fmt="o", color="black", ms=0.1)
ax1.add_artist(Ellipse((2.5, 5), 1, 1, facecolor="green", edgecolor="black"))
pdf.savefig(fig)
pdf.close()
plt.close()
and here is how it looks:
I want the error bar to go in the background of the ellipse.
Thanks in advance...
Use the zorder specifier for both your plot commands.
From the documentation: "Set the zorder for the artist. Artists with lower zorder values are drawn first."
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
fig=plt.figure(1)
ax1=fig.add_subplot(111)
plt.xlim([0,5])
plt.ylim([0,10])
ax1.errorbar([2.5], [5], yerr=[1], fmt="o", color="black", ms=0.1, zorder=1)
ax1.add_artist(Ellipse((2.5, 5), 1, 1, facecolor="green", edgecolor="black",zorder=2))
plt.show()
exit(0)
It seems to me using Path is a more straightforward approach: the Path instance is treated exactly as a normal marker, hence just use the very same interface. Please have a look at the example below, but also reference matplotlib documentation on this topic.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.path as mpath
# Create mock data.
theta = np.linspace(0, 2.*np.pi, 30)
signa = np.sin(theta)
u_theta = np.random.normal(0., scale=0.15, size=signa.size)
u_signa = np.random.normal(0., scale=0.15, size=signa.size)
theta += u_theta
signa += u_signa
# Define the ellipse marker.
circle = mpath.Path.unit_circle()
verts = np.copy(circle.vertices)
verts[:, 0] *= 1.618
ellipt_marker = mpath.Path(verts, circle.codes)
# Done, basically.[![Plotting example][1]][1]
plt.errorbar(theta, signa, xerr=u_theta, yerr=u_signa,
marker=ellipt_marker, linestyle='', capsize=5,
ms=20, mfc='w', c='r', mec='g')
plt.xlabel('Autology', fontsize=35)
plt.ylabel('Eterology', fontsize=35)
plt.show()