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()
Related
I am a beginner in Python. I'm trying to plot a circle using matplotlib that has tangent to Z axis. I know how to draw a sphere in 3D but don't know how to draw a circle/ring in 3D plot. Can someone help me with the code? Thanks in advance!
You need the usual imports, plus the 3D toolkit
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
You need a 3D enabled axes object
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
You need a circle, contained in the plane y-z
theta = np.linspace(0, 2 * np.pi, 201)
y = 10*np.cos(theta)
z = 10*np.sin(theta)
now we can plot the original circle and, as an example, a number of circles rotated about the z-axis and whose centers are also placed at a fixed distance (equal to the c ircles'radius) from the z-axis, so that they are tangent to it
for i in range(18):
phi = i*np.pi/9
ax.plot(y*np.sin(phi)+10*np.sin(phi),
y*np.cos(phi)+10*np.cos(phi), z)
eventually we place a vertical axis and a legend
ax.plot((0,0),(0,0), (-10,10), '-k', label='z-axis')
ax.legend()
It's time to see what we got
plt.show()
mpl_toolkits.mplot3d.art3d
https://matplotlib.org/3.2.1/gallery/mplot3d/pathpatch3d.html was mentioned
in a comment, the example can be minimized to:
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import mpl_toolkits.mplot3d.art3d as art3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Draw a circle on the x=0 'wall'
p = Circle((5, 5), 3)
ax.add_patch(p)
art3d.pathpatch_2d_to_3d(p, z=0, zdir="x")
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
plt.show()
which gives:
This is a bit nicer than https://stackoverflow.com/a/56871467/895245 as it uses a higher level Circle object directly, instead of requiring you to explicitly plot the lines.
Unfortunately, 3D support in matplotlib is a bit limited as mentioned in the documentation itself, and you have to do some extra work to plot on planes not parallel to the main coordinate plane: How can matplotlib 2D patches be transformed to 3D with arbitrary normals?
Tested on matplotlib==3.2.2.
I have seen many examples of using annotate arrows in Matplotlib that have a single color specified. I was wondering if it is possible to instead set the color according to a colormap, so that the whole range of colors from a specified colormap is displayed on a single arrow. I know that it is possible to set the color of an arrow to a single color from a colormap, but I want to have a single arrow displaying all of the colors of a given colormap.
A simple example of using an annotate arrow is shown below. In the documentation, I have not found any method for specifying a colormap. If I naively specify a colormap, I get an error from an invalid RGBA argument.
import matplotlib.pyplot as plt
RdPu = plt.get_cmap('RdPu')
ax = plt.subplot(111)
ax.annotate("Test", xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
size=20, arrowprops=dict(color=RdPu),
)
plt.show()
Ok, let's produce The Rainbow Arrow. ;-)
There is of course no built-in way to colorize an arrow with a color gradient. Instead one needs to build the arrow manually. I can think of two options. (1) Create a color gradient and clip it with the circonference path of an arrow. (2) Produce a LineCollection with a colorgradient and then add an arrow head to it.
The following is the second option:
import matplotlib.pyplot as plt
import matplotlib.transforms
import matplotlib.path
import numpy as np
from matplotlib.collections import LineCollection
def rainbowarrow(ax, start, end, cmap="viridis", n=50,lw=3):
cmap = plt.get_cmap(cmap,n)
# Arrow shaft: LineCollection
x = np.linspace(start[0],end[0],n)
y = np.linspace(start[1],end[1],n)
points = np.array([x,y]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)
lc = LineCollection(segments, cmap=cmap, linewidth=lw)
lc.set_array(np.linspace(0,1,n))
ax.add_collection(lc)
# Arrow head: Triangle
tricoords = [(0,-0.4),(0.5,0),(0,0.4),(0,-0.4)]
angle = np.arctan2(end[1]-start[1],end[0]-start[0])
rot = matplotlib.transforms.Affine2D().rotate(angle)
tricoords2 = rot.transform(tricoords)
tri = matplotlib.path.Path(tricoords2, closed=True)
ax.scatter(end[0],end[1], c=1, s=(2*lw)**2, marker=tri, cmap=cmap,vmin=0)
ax.autoscale_view()
fig,ax = plt.subplots()
ax.axis([0,5,0,4])
ax.set_aspect("equal")
rainbowarrow(ax, (3,3), (2,2.5), cmap="viridis", n=100,lw=3)
rainbowarrow(ax, (1,1), (1.5,1.5), cmap="jet", n=50,lw=7)
rainbowarrow(ax, (4,1.3), (2.7,1.0), cmap="RdYlBu", n=23,lw=5)
plt.show()
The following is the old solution, caused by a misunderstanding
An annotation arrow is a single arrow. Hence you would need to draw any number of arrows individually. In order for each arrow to then obtain a color, you may use the arrowprops=dict(color="<some color>") argument.
To get colors from a colormap, you can call the colormap with a value. Here the length of the arrow can be taken as the quantity to encode as color.
import matplotlib.pyplot as plt
import numpy as np
RdPu = plt.get_cmap('RdPu')
ax = plt.subplot(111)
ax.axis([-6,2,-4.5,3.2])
ax.set_aspect("equal")
X = np.linspace(0,1,17, endpoint=False)
Xt =np.sin(2.5*X+3)
Yt = 3*np.cos(2.6*X+3.4)
Xh = np.linspace(-0.5,-5,17)
Yh = -1.3*Xh-5
#Distance
D = np.sqrt((Xh-Xt)**2+(Yh-Yt)**2)
norm = plt.Normalize(D.min(), D.max())
for xt, yt, xh, yh, d in zip(Xt,Yt,Xh,Yh,D):
ax.annotate("Test", xy=(xh,yh), xycoords='data',
xytext=(xt,yt), textcoords='data',
size=10, arrowprops=dict(color=RdPu(norm(d))))
plt.show()
I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.
Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.
I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.
This screen shot shows the plot generated with filled area extending beyond the set axis limits.
How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
How do I add a legend for those plots onto the figure?
Code as follows:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
x,y = genfromtxt("data.dat",unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()
ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)
plt.show()
I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
#x,y = genfromtxt("data.dat",unpack=True)
# Generated some random data
w = 3
x,y = np.arange(100), np.random.randint(0,100+w,100)
y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
z = np.zeros(x.shape)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
ax.plot(x,z,y,label="line plot")
ax.legend()
ax.set_ylim(-1,1)
plt.show()
The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:
I am making a polar scatter plot for a college project with matplotlib and I can't find out how to add a label to the radial axis. Here is my code ( I left out the data because it was read out of a csv)
import matplotlib.pyplot as plt
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
plt.show()
(My plot looks like this. I can't seem to find any straight forward method to do it. Has anyone dealt with this before and have any suggestions?
I don't know of a built in way to do it, but you could use ax.text to make your own. You can get the position of the radial tick labels using ax.get_rlabel_position(), and the mid point of the radial axis using ax.get_rmax()/2.
For example, here's your code (with some random data):
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
label_position=ax.get_rlabel_position()
ax.text(np.radians(label_position+10),ax.get_rmax()/2.,'My label',
rotation=label_position,ha='center',va='center')
plt.show()
And here's the output:
I'd be interested to see if there's a more elegant solution, but hopefully this helps you.
from pylab import *
N = 150
r = 2*rand(N)
theta = 2*pi*rand(N)
area = 200*r**2*rand(N)
colors = theta
ax = subplot(111, polar=True)
c = scatter(theta, r, c=colors, s=area, cmap=cm.hsv)
c.set_alpha(0.75)
ax.set_ylabel('Radius', rotation=45, size=11)
show()
A slightly different method from #tom. This uses directly the plt.legend option.
Example:
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii,label='Supernova')
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(loc='lower right', scatterpoints=1)
plt.show()
You can change lower right to upper right or even to best to leave the alignment of the legend to matplotlib.
I'm trying to make a centered bubble chart in matplotlib / python similar to this.
Some people have called it a "bottom aligned bubble chart", So far, I've basically found a way to do a concentric circle scatter plot.
%matplotlib inline
import matplotlib.pyplot as plt
s = [ 50000.,10478.2, 4733.4,3185.3,2484.7,2310.9]
x = [1]*len(s)
y = [0]*len(s);
plt.scatter(x,y,s=s);
plt.show()
Any ideas on how to line up the bottom edges of these concentric cirlces?
I would interact with matplotlib artists directly. I would also set the radius -- and therefore center -- of each circle the square root of the populations.
This is because, for a circle, A ~ r^2, so you'll heavily distort the size differences if r ~ population.
So all that said:
%matplotlib inline
import numpy
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import seaborn
seaborn.set(style='white')
populations = numpy.sqrt([50000., 10478.2, 4733.4, 3185.3, 2484.7, 2310.9])
cp = seaborn.color_palette('Blues_r', n_colors=len(populations))
fig, ax = plt.subplots()
for n, p in enumerate(populations):
circle = plt.Circle((1, p), radius=p, facecolor=cp[n])
ax.add_artist(circle)
ax.set_xlim(-max(populations), max(populations))
ax.set_ylim(0, 2 * max(populations))
ax.set_aspect('equal')
plt.show()
Gives me this: