Positioning figure and table in figure - matplotlib

I'm trying to put both a plot and a table in a figure, but I want some whitespace to separate the two. How do I position the table/plot at arbitrary positions? What I have now is the table of values showing up IMMEDIATELY under the x-axis (so that it's actually colliding into my axis labels...)
I don't know matplotlib at all...The documentation is not written very well either...

To position something in a figure you have to use the function set_position([left, bottom, width, height]). Example:
matplotlib.pyplot.axes().set_position([0.15, 0.20, 0.80, 0.70])

Related

Matplotlib's Figure and Axes explanation

I am really pretty new to matplotlib, though I know that it can be very powerful.
I've been reading number of tutorials and examples and it's a real hassle to understand how does matplotlib's Figure and Axes work. I am illustrating, what I am trying to understand, with the attached figure.
I know how to create a figure instance of certain size in inches. However, what bothers me is how can I create subplots and then axes, within each subplot, with relative coordinates (bottom=0,left=0,top=1,right=1) as illustrated.
So, for example I want to create a "parent" plot area (say (6in,10in)). Then, I want to create two subplot areas, each with size (3in,3in), with 1in space from the top, 2in space between the two vertical subplot areas and 1in from bottom. Then, 1in space on the left and 2in space on the write. In the same time, I would like to be able to get the coordinates of the subplot areas with respect to the main plot area.
Then, inside the first subplot area, I'd like to create 2 axis instances, with Axis 1, having coordinates with respect to Subplot Area1 (0.1,0.7,0.7,0.2) and Axes 2 (0.1,0.2,0.7,0.5). And then of course I'd like to be able to plot on these axes e.g., ax1.plot()....
If you could provide a sample code to achieve that, then I can study it.
Your help will be very much appreciated!
a subplot and an Axes object are really the same thing. There is not really a "subplot" as you describe it in matplotlib. You can just create your three Axes objects using gridspec without the need to put them in your "subplots".
There are a few different ways to create Axes instances within your figure.
fig.add_axes will create an Axes instance at the position given to it (you give it [left,bottom,width,height] in figure coordinates (i.e. 0,0 is bottom left, 1,1 is top right).
fig.add_subplot will also create an Axes instance. In this case, rather than giving it a rectangle to be created in, you give it the number of rows and columns of subplots you would like, and then the plot_number, where plot_number starts at 1, increments across rows first and has a maximum of nrows * ncols.
For example, to create the top-left Axes in a grid of 2 row and 2 columns, you could do the following:
fig.add_subplot(2,2,1)
or the shorthand
fig.add_subplot(221)
There are some more customisable ways to create Axes as well, for example gridspec and subplot2grid which allow for easy creation of many subplots of different shapes and sizes.

How do create a scale for a second axis without unnecessary (or redundant) plotting?

I have a plot in which I have already plotted all my data and a "twined" axis, on which I'd like to use another scale, in this case dates. I also have a list of all the dates corresponding to each element of my data, and want to add an a scale for the dates to the twined axis.
For example, I have
ax2 = ax1.twinx()
and lists x_temporal_data, y_day_offsets, y_dates, all of the same length, and have already plotted the relationship between the first two with
ax1.plot(x_temporal_data, y_day_offsets)
and I just want to have a scale on ax2 for the dates in y_dates, since y_day_offsets and y_dates are "synonyms" for the same time information.
Is there a way to do this without "plotting" something I don't need to display (since all my data is already plotted). For example, I can get the dates to appear perfectly on ax2 with
ax2.plot(len(y_dates)*[some_random_out_of_xrange_value], y_dates)
but that seems like a hack: plotting nothing to "calibrate" the second axis.
Is there a better, more idiomatic way of accomplishing this?
Simply set the scale on the second y-axis to your liking with:
ax2.set_ylim([min(y_dates), max(y_dates)])

Matplotlib tick labels

Is there a way to render the tick labels just right inside the axes, i.e, something like the direction property there is on the ticks themself?
Right now I'm setting the x property to a positive value on the ticklabels to draw them inside of the axis, i.e.,
ax2.set_yticklabels(['0', '2500', '5000', '7500'], minor=False, x=0.05)
But this doesn't really work on resizable plots, as the 0.05 figure is absolute (and too big on big plots).
Any ideas?
I'm assuming that ax2 is constructed as ax2 = ax.twinx(), which is to say that it is on the right side of the axes.
You could do something like the following:
ax2.set_yticklabels(['0', '2500', '5000', '7500'], minor=False, horizontalalignment='right')
for tick in ax2.yaxis.get_major_ticks():
tick.set_pad(-8)
If you want the left side axis on the inside too, then you'd simply switch the horizontal alignment to 'left' and change the pad from -8 to -25.
The two numbers might not be exact and could depend on other matplotlib settings you might have (e.g. length of major ticks) so you may want to increase or decrease those values slightly.

How can I get and set the position of a draggable legend in matplotlib

I'm trying to get and set the position of a draggable legend in matplotlib. My application consists of an interactive GUI, which has a redraw/plot function that should perform the follow steps:
save the position of the current legend.
clear the current axes and perform various plotting operations, which may or may add labels to their plots.
build a new draggable legend (ax.legend().draggable()) and restore the old position of the legend.
In between these steps the user is free to drag the legend around, and the goal is to persist the legend position when the plots are redrawn.
My first approach was to use oldpos = legend.get_bbox_to_anchor() and legend.set_bbox_to_anchor(oldpos) in steps 1 and 3. However this causes to move the legend completely off the visible area.
Note that I have to use ax.legend() and cannot use fig.legend(lines, labels), since step 2 is completely decoupled, i.e., I don't know anything about lines and labels in step 3. According to answers to the question How to position and align a matplotlib figure legend? there seems to be a difference between these two possibilities regarding axes or figure coordinates. Obviously my problem calls for figure coordinates, but I haven't fully understood how to convert the bbox to a "bbox in figure coordinates".
The even more severe problem I just realized is that apparently legend.get_bbox_to_anchor() always seems to return the same values irrespective of the drag position. So maybe the anchor can only be (ab-)used to manipulate the position of static legends? Is there another/proper way to save and restore the position of a draggable legend?
By looking at the implementation of Legend I found out that there is an undocumented property _loc, which exactly does what I want. My solution now looks astonishingly simple:
oldLegPos = ax.get_legend()._loc
# perform all plotting operations...
legend = ax.legend().draggable()
legend._loc = oldLegPos
It looks like _loc automatically stores figure coordinates, since I do not have to convert the coordinates in any way (eg. when the plotting operations completely change the axes ranges/coordinates).

How to change text of y-axes on a matplotlib generated picture

The page is
"http://matplotlib.sourceforge.net/examples/pylab_examples/histogram_demo_extended.html"
Let's look at the y-axis, the numbers there do not make any sense, could we change it to something else that is meaningful?
Except the cumulative distribution plot, and the last one, the rest of the y-axes data show normalized histogram values with normed=1 keyword set (i.e., the are underneath the histogram equals to 1 as in the definition of a probability density function (PDF))
You can use yticks(), see this example.