I have a trajectory of some robot, together with some arrows describing parameters of the robot. Just like so:
Robot Trajectory with annotation arrows
(because of my lack of reputation, I cannot add the picture directly)
The question is: How can I display the annotation arrows together with the lines in the legend?
I use annotation arrows in order to draw the arrows as per this answer in a loop to draw arrows for every point.
Here is the code for one of my annotations:
an = ax.annotate('', xy=xy_tuple, xytext=xy_texttuple, label=labelString, arrowprops=dict(color=arrowcolor, arrowstyle=aStyle))
And just as a reference, I use the plotting function like so:
# plot Local x-y axis
fig, ax1 = plt.subplots()
ln1 = ax1.plot(x, y, '-o', label='Location of the last 48h')
ax1.set_ylabel('Local North (m)')
ax1.set_xlabel('Local East (m)')
ax1.grid()
fig.gca().set_aspect('equal', adjustable='box')
lns = ln1
labs = [l.get_label() for l in lns]
ax1.legend(lns, labs, loc='best')
So how can I add the annotate labels (given by labelString) to my legend?
As pointed out by #ImportanceOfBeingErnest, this is an open issue which was found with matplotlib. The developers wanted to fix this issue, but according to this discussion on the matplotlib developerboard, an solution was implemented but did not pass to the final version and is now closed (although it is a pretty cool solution imho).
Give an line and two annotations, my solution now looks like so. Of course this is not nearly as nice:
ax1.legend([ln1, an1.arrow_patch, an2.arrow_patch], (ln1.get_label(), an1.get_label(), an2.get_label()))
Resulting in the following example
Related
I'm trying to plot a two-dimensional array in matplotlib using imshow(), and overlay it with a scatterplot on a second y axis.
oneDim = np.array([0.5,1,2.5,3.7])
twoDim = np.random.rand(8,4)
plt.figure()
ax1 = plt.gca()
ax1.imshow(twoDim, cmap='Purples', interpolation='nearest')
ax1.set_xticks(np.arange(0,twoDim.shape[1],1))
ax1.set_yticks(np.arange(0,twoDim.shape[0],1))
ax1.set_yticklabels(np.arange(0,twoDim.shape[0],1))
ax1.grid()
#This is the line that causes problems
ax2 = ax1.twinx()
#That's not really part of the problem (it seems)
oneDimX = oneDim.shape[0]
oneDimY = 4
ax2.plot(np.arange(0,oneDimX,1),oneDim)
ax2.set_yticks(np.arange(0,oneDimY+1,1))
ax2.set_yticklabels(np.arange(0,oneDimY+1,1))
If I only run everything up to the last line, I get my array fully visualised:
However, if I add a second y axis (ax2=ax1.twinx()) as preparation for the scatterplot, it changes to this incomplete rendering:
What's the problem? I've left a few lines in the code above describing the addition of the scatterplot, although it doesn't seem to be part of the issue.
Following the GitHub discussion which Thomas Kuehn has pointed at, the issue has been fixed few days ago. In the absence of a readily available built, here's a fix using the aspect='auto' property. In order to get nice regular boxes, I adjusted the figure x/y using the array dimensions. The axis autoscale feature has been used to remove some additional white border.
oneDim = np.array([0.5,1,2.5,3.7])
twoDim = np.random.rand(8,4)
plt.figure(figsize=(twoDim.shape[1]/2,twoDim.shape[0]/2))
ax1 = plt.gca()
ax1.imshow(twoDim, cmap='Purples', interpolation='nearest', aspect='auto')
ax1.set_xticks(np.arange(0,twoDim.shape[1],1))
ax1.set_yticks(np.arange(0,twoDim.shape[0],1))
ax1.set_yticklabels(np.arange(0,twoDim.shape[0],1))
ax1.grid()
ax2 = ax1.twinx()
#Required to remove some white border
ax1.autoscale(False)
ax2.autoscale(False)
Result:
I am preparing 3d plots with matplotlib and I am having a really weird behaviour with multiple datasets. I have two datasets that describe basically two shells in 3d: one inner shell and one outer shell. To plot them in 3d I do:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(outer_z[:n], outer_x[:n], outer_y[:n], c='black', marker='.', lw=0)
ax.scatter(inner_z[:n], inner_x[:n], inner_y[:n], c='red', marker='.', lw=0)
ax.set_xlabel("Z")
ax.set_ylabel("X")
ax.set_zlabel("Y")
ax.set_xlim([-5,5])
ax.set_ylim([5,-5])
ax.set_zlim([-5,5])
(the order of the axes are just for perspective purposes). When I save the figure, however, I don't get two shells:
I get one layer over the other, with the points that are clearly in the back appearing in front. You can see on the pictures that some points of the outer shell that should be behind the inner shell are plotted in front of the inner shell. This is really annoying, because it does not pursue the "plot in 3d" purpose. Does any one have an idea on why is this happening and how could this be solved?
Many thanks!
I know that this isn't a solution to your problem, but perhaps an explanation for why it's behaving the way it is.
This has to do with the fact that Matplotlib does not actually have a 3D engine. Mplot3D takes your points and projects them to what it would look like on a 2D plot (for each object), and then Matplotlib draws each object one at a time; Matplotlib is a 2D drawing framework and Mplot3D is kind of a little hack to get some 3D functionality working without needing to write an full-blown 3D engine for Matplotlib.
This means the order in which you draw your different plots (in this case your red and black dots) matters, and if you draw your black dots after your red dots, they will appear to be in front of the red dots, regardless of their position.
Let me illustrate this with another example.
theta = np.linspace(0, 2*np.pi, 100, endpoint=True)
helix_x = np.cos(3*theta)
helix_y = np.sin(3*theta)
helix_z = theta
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
line_x = np.zeros(100)
line_y = np.zeros(100)
ax.plot(line_x, line_y, theta, lw='3', color='r')
ax.plot(helix_x, helix_y, helix_z, lw='2', color='k')
ax.set_xlabel("Z")
ax.set_ylabel("X")
ax.set_zlabel("Y")
ax.set_xlim([-1.5,1.5])
ax.set_ylim([-1.5,1.5])
ax.set_zlim([0,2*np.pi])
This gives:
But from the top view you can see that the line is inside the helix:
However if you swap the order in which you plot these lines:
ax.plot(line_x, line_y, theta, lw='3', color='r')
ax.plot(helix_x, helix_y, helix_z, lw='2', color='k')
You then see the line drawn after the helix:
Ultimately this means that you will have to manually determine which points will be in front of the other points. Then you can use the zorder argument to determine which objects will be in front of the others. But you would have to do this for each perspective (angle, elevation). In this case you would probably have to break up the inside line into "infront_of_helix" and "behind_helix" parts and then draw them in front and behind the helix respectively.
I hope someone comes along with more elaboration on the matter though, as I'm interested in the topic myself. I know that mplot3d has some elementary methods for making sure the front points show first, I believe, when it's using the shading algorithms but I'm not exactly sure.
thanks you so much for your explanation :) I thought it could be something like that indeed. But I forgot to say in my question that the same thing happened no matter the order of the ax.scatter commands, what is pretty weird. I found out before reading your answer that that does not happen with the ax.plot command. Therefore, I replaced:
ax.scatter(outer_z[:n], outer_x[:n], outer_y[:n], c='black', marker='.', lw=0)
ax.scatter(inner_z[:n], inner_x[:n], inner_y[:n], c='red', marker='.', lw=0)
by
ax.plot(outer_z[:n], outer_x[:n], outer_y[:n], '.', markersize=1, color='black')
ax.plot(inner_z[:n], inner_x[:n], inner_y[:n], '.', markersize=1, color='red')
And I got the following picture:
which works for me. I know, however, that if I change the point of view I will have the red shell appearing on top of the black one. One problem I found later was that the .plot function does not have vmin and vmax arguments (as the .scatter one), which makes it harder to define the color as a gradient starting in vmin and vmax...
This question already has answers here:
Is there a way to make a discontinuous axis in Matplotlib?
(7 answers)
Closed 5 years ago.
Best way to describe what I want to achieve is using my own image:
Now I have a lot of dead space in the spectra plot, especially between 5200 and 6300. My question is quite simple, how would I add in a nice little // break that looks something similar to this (image lifted from the net):
I'm using this setup for my plots:
nullfmt = pyplot.NullFormatter()
fig = pyplot.figure(figsize=(16,6))
gridspec_layout1= gridspec.GridSpec(2,1)
gridspec_layout1.update(left=0.05, right=0.97, hspace=0, wspace=0.018)
pyplot_top = fig.add_subplot(gridspec_layout1[0])
pyplot_bottom = fig.add_subplot(gridspec_layout1[1])
pyplot_top.xaxis.set_major_formatter(nullfmt)
I'm quite certain it is achievable with gridpsec but an advanced tutorial cover exactly how this is achieved would be greatly appreciated.
Apologies also if this question has been dealt with previously on stackoverflow but I have looked extensively for the correct procedure for gridSpec but found nothing as yet.
I have managed to go as far as this, pretty much there:
However, my break lines are not as steep as I would like them...how do I change them? (I have made use of the example answer below)
You could adapt the matplotlib example for a break in the x-axis directly:
"""
Broken axis example, where the x-axis will have a portion cut out.
"""
import matplotlib.pylab as plt
import numpy as np
x = np.linspace(0,10,100)
x[75:] = np.linspace(40,42.5,25)
y = np.sin(x)
f,(ax,ax2) = plt.subplots(1,2,sharey=True, facecolor='w')
# plot the same data on both axes
ax.plot(x, y)
ax2.plot(x, y)
ax.set_xlim(0,7.5)
ax2.set_xlim(40,42.5)
# hide the spines between ax and ax2
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.yaxis.tick_left()
ax.tick_params(labelright='off')
ax2.yaxis.tick_right()
# This looks pretty good, and was fairly painless, but you can get that
# cut-out diagonal lines look with just a bit more work. The important
# thing to know here is that in axes coordinates, which are always
# between 0-1, spine endpoints are at these locations (0,0), (0,1),
# (1,0), and (1,1). Thus, we just need to put the diagonals in the
# appropriate corners of each of our axes, and so long as we use the
# right transform and disable clipping.
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((1-d,1+d), (-d,+d), **kwargs)
ax.plot((1-d,1+d),(1-d,1+d), **kwargs)
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
ax2.plot((-d,+d), (1-d,1+d), **kwargs)
ax2.plot((-d,+d), (-d,+d), **kwargs)
# What's cool about this is that now if we vary the distance between
# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(),
# the diagonal lines will move accordingly, and stay right at the tips
# of the spines they are 'breaking'
plt.show()
For your purposes, just plot your data twice (once on each axis, ax and ax2 and set your xlims appropriately. The "break lines" should move to match the new break because they are plotted in relative axis coordinates rather than data coordinates.
The break lines are just unclipped plot lines drawn between a pair of points. E.g. ax.plot((1-d,1+d), (-d,+d), **kwargs) plots the break line between point (1-d,-d) and (1+d,+d) on the first axis: this is the bottom righthand one. If you want to change the graident, change these values appropriately. For example, to make this one steeper, try ax.plot((1-d/2,1+d/2), (-d,+d), **kwargs)
The solution provided by xnx is a good start, but there is a remaining issue that the scales of the x-axes are different between the plots. This is not a problem if the range in the left plot and the range in the right plot are the same, but if they are unequal, subplot will still give the two plots equal width, so the x-axis scale will be different between the two plots (as is the case with xnx's example). I made a package, brokenaxes to deal with this.
I am developing some code to produce an arbitrary number of 2D plots (maps and simple contour plots) on a figure. The matplotlib subplots routine works great for this. In the simplified example below, everything works as it should. However, in my real application - which uses the exact same commands for subplots, contourf and colorbar, only that these are dispersed across several routines - the labels on the colorbars are not showing up (the color patches seem to be ok though). Even after hours of reading documentation and searching the web, I don't even have a clue where I could start looking for what the problem is. If I have my colorbar instance (cbar), I should be able to find out if the ticklabel position makes sense, if the ticklabels are set to visible, if my font settings make sense, etc.... But how do I actually check these properties? Has anyone encountered similar problems already? (and even better: found a solution?) Oh yes: if I manually create a new figure and axes in the actual plotting routine (where the contourf command is issued), then it will work again. But that means losing all control over the figure layout etc. Could it be that I am not passing my axes instance correctly? Here is what I do:
fig, ax = plt.subplots(nrows, ncols)
row, col = getCurrent(...)
plotMap(x, y, data, ax=ax[row,col], ...)
Then, inside plotMap:
c = ax.contourf(x, y, data, ...)
ax.figure.colorbar(c, ax=ax, orientation="horizontal", shrink=0.8)
As said above, the example below with simplified plots and artificial data works fine:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0.,360.,5.)*np.pi/180.
y = np.arange(0.,360.,5.)*np.pi/180.
data = np.zeros((y.size, x.size))
for i in range(x.size):
data[:,i] = np.sin(x[i]**2*y**2)
fig, ax = plt.subplots(2,1)
contour = ax[0].contourf(x, y, data)
cbar = ax[0].figure.colorbar(contour, ax=ax[0], orientation='horizontal', shrink=0.8)
contour = ax[1].contourf(x, y, data, levels=[0.01,0.05,0.1,0.05])
cbar = ax[1].figure.colorbar(contour, ax=ax[1], orientation='horizontal', shrink=0.8)
plt.show()
Thanks for any help!
Addition after some further poking around:
for t in cbar.ax.get_xticklabels():
print t.get_position(), t.get_text(), t.get_visible()
shows me the correct text and visible=True, but all positions are (0.,0.). Could this be a problem?
BTW: axis labels are also missing sometimes... and I am using matplotlib version 1.1.1 with python 2.7.3 on windows.
OK - I could track it down: matplotlib is working as it should!
The error was embedded in a utility routine that adds some finishing touches to each page (=figure) once the given number of plot panels has been produced. In this routine I wanted to hide empty plot panels (i.e. on the last page) and I did this with
ax = fig.axes
for i in range(axCurrent, len(ax)):
ax[i].set_axis_off()
However, axCurrent was already reset to zero when the program entered this routine for any page but the last, hence the axes were switched off for all axes in figure. Adding
if axCurrent > 0:
before the for i... solves the problem.
Sorry if I stole anyone's time. Thanks anyway to everyone who was considering to help!
My objective is to draw a graph with 4 quadrants and plot points in the same. And also, how can I divide a quadrant into several sectors? How can I do the same in matplotlib: a graph/plot with 4 quadrants. With x axis (1-9) and y-axis(1-9)?
From the question, it sounds like you want a single graph with several delineated regions with a specific xy range. This is pretty straightforward to do. You can always just draw lines on the plot to delineate the regions of interest. Here is a quick example based on your stated objectives:
import matplotlib.pyplot as plt
plt.figure()
# Set x-axis range
plt.xlim((1,9))
# Set y-axis range
plt.ylim((1,9))
# Draw lines to split quadrants
plt.plot([5,5],[1,9], linewidth=4, color='red' )
plt.plot([1,9],[5,5], linewidth=4, color='red' )
plt.title('Quadrant plot')
# Draw some sub-regions in upper left quadrant
plt.plot([3,3],[5,9], linewidth=2, color='blue')
plt.plot([1,5],[7,7], linewidth=2, color='blue')
plt.show()
I would take a look at the AxesGrid toolkit:
http://matplotlib.sourceforge.net/mpl_toolkits/axes_grid/index.html
Perhaps the middle image at the top of this page is something along the lines of what you are looking for. There are examples on the following page in the API documentation that should be a good starting point:
http://matplotlib.sourceforge.net/mpl_toolkits/axes_grid/users/overview.html
Without an example of what you want to do exactly it is difficult to give you the best advice.
you need subplot see this example:
http://matplotlib.sourceforge.net/examples/pylab_examples/subplot_toolbar.html