Setting physically square subplots in Matplotlib 3 with shared axes - python

I am trying to create a similar plot to what is done by seaborn, but in native matplotlib.
I am plotting every Series in a data frame against every other series in a matrix of plots.
So far I've plotted it, marked the outer axes, and set the axes to be shared along columns and row (as this works with the data the best).
The final step I am failing to manage is to make all the plots physically square in dimension. The following code:
#scatter matrix
def plot_scatter_matrix(data):
dim = len(data.columns.values)
fig, axs = newfigure(dim, dim, sharex='col', sharey='row', figsize=(10,10))
fig.tight_layout()
for row, iname in enumerate(data.columns.values):
for col, jname in enumerate(data.columns.values):
axs[row,col].scatter(data[jname], data[iname])
if col == 0:
axs[row,col].set_ylabel(iname)
if row == len(data.columns.values)-1:
axs[row,col].set_xlabel(jname)
return fig, axs
fig, axs = plot_scatter_matrix(ndata)
plt.show()
achieves this (only top half pictured):
I have attempted to use axs[row,col].set_aspect(1.0, adjustable='box', share=True) after the call to scatter() however it simply resulted in this:
As you can see, some managed to become physically square but they are all different sizes.
Having looked extensively through documentation and other questions I am stumped. Doesn't make it easier when other methods for this sort of thing have been deprecated over past versions.

If some axes become square by using set_aspect(1.0) (or the equivalent set_aspect("equal")) that's more or less coincidence and would only happen when the diffence of axis limits is actually equal; e.g. when the data ranges for x and y are the same.
Of course you could share all axes, not just column- or row-wise. That would ensure all axes to be of equal shape - but not necessarily square.
The requirement for square axes is that the aspect is the quotient of the x- and y range.
ax.set_aspect(np.diff(ax.get_xlim())/np.diff(ax.get_ylim()))
Also see: How to make sure that both x and y axes of plot are of equal sizes?
Another option is to restrict the space the subplots have via the subplot parameters as shown in this answer to python interplay between axis('square') and set_xlim.

Related

What does the 'single' mean here?

Here is the instruction:
a string "fig_type", which is one of the two values: "single" or "subplots".
The input argument "fig_type" determines how to draw the plots:
if "fig_type" is "single", you should produce one set of axes, draw all the plots together in the same axes, and differentiate them e.g. by line or marker colour or style.
if "fig_type" is "subplots", you should produce 𝑟
r
different sets of axes (in the same figure), so that each plot is drawn in a different subplot. Choose how to set up your subplots so that all plots are sufficiently large and readable.
Then I write a code like that, I'm not quite sure if that's how it should be written, and I'm not sure what single means in this context.
if fig_type =='single':
fig, ax = plt.plot()
else:
fig, ax = plt.subplots()

Plot two datasets at same position based on their index

I'm trying to plot two datasets (called Height and Temperature) on different y axes.
Both datasets have the same length.
Both datasets are linked together by a third dataset, RH.
I have tried to use matplotlib to plot the data using twiny() but I am struggling to align both datasets together on the same plot.
Here is the plot I want to align.
The horizontal black line on the figure is defined as the 0°C degree line that was found from Height and was used to test if both datasets, when plotted, would be aligned. They do not. There is a noticable difference between the black line and the 0°C tick from Temperature.
Rather than the two y axes changing independently from each other I would like to plot each index from Height and Temperature at the same y position on the plot.
Here is the code that I used to create the plot:
#Define number of subplots sharing y axis
f, ax1 = plt.subplots()
ax1.minorticks_on()
ax1.grid(which='major',axis='both',c='grey')
#Set axis parameters
ax1.set_ylabel('Height $(km)$')
ax1.set_ylim([np.nanmin(Height), np.nanmax(Height)])
#Plot RH
ax1.plot(RH, Height, label='Original', lw=0.5)
ax1.set_xlabel('RH $(\%)$')
ax2 = ax1.twinx()
ax2.plot(RH, Temperature, label='Original', lw=0.5, c='black')
ax2.set_ylabel('Temperature ($^\circ$C)')
ax2.set_ylim([np.nanmin(Temperature), np.nanmax(Temperature)])
Any help on this would be amazing. Thanks.
Maybe the atmosphere is wrong. :)
It sounds like you are trying to align the two y axes at particular values. Why are you doing this? The relationship of Height vs. Temperature is non-linear, so I think you are setting the stage for a confusing graph. Any particular line you plot can only be interpreted against one vertical axis.
If needed, I think you will be forced to "do some math" on the limits of the y axes. This link may be helpful:
align scales

How can I change value on Right axis? (python3/matplotlab)

When I'm plotting a single curve, I use
plt.tick_params(axis = 'y', which = 'both', labelright = True)
which shows the same value on the right axis as the left Y-axis.
Is there a way to change the value on the right axis?
As far as I've searched up to now, most threads are about a shared X-axis. But mine is a single curve, NOT shared axis curves.
I would like to show the percentage of the Y axis value to a base value on the right axis.
I appreciate your suggestions!
Even though you call it left axis and right axis, it really is one single axis. You may decide on which side of the plot to label it through the use of labelright and labelleft arguments of tick_params, which will just determine whether to show the labels or not.
In case you want to show something different on both sides of the plot you need a second axes. An easy method is to use a twinx axes. For how to do this see
Adding a second y-axis related to the first y-axis.
fig, ax = plt.subplots()
ax2 = ax.twinx()
Now it depends on how you want the two y axes to link to each other. You may share the y axes, or you may set the same limits, or you may calculate the limits of the one depending on the other.

Time series plotted with imshow

I tried to make the title as clear as possible although I am not sure it is completely limpid.
I have three series of data (number of events along time). I would like to do a subplots were the three time series are represented. You will find attached the best I could come up with. The last time series is significantly shorter and that's why it is not visible on here.
I'm also adding the corresponding code so you can maybe understand better why I'm trying to do and advice me on the proper/smart way to do so.
import numpy as np
import matplotlib.pyplot as plt
x=np.genfromtxt('nbr_lig_bound1.dat')
x1=np.genfromtxt('nbr_lig_bound2.dat')
x2=np.genfromtxt('nbr_lig_bound3.dat')
# doing so because imshow requieres a 2D array
# best way I found and probably not the proper way to get it done
x=np.expand_dims(x, axis=0)
x=np.vstack((x,x))
x1=np.expand_dims(x1, axis=0)
x1=np.vstack((x1,x1))
x2=np.expand_dims(x2, axis=0)
x2=np.vstack((x2,x2))
# hoping that this would compensate for sharex shrinking my X range to
# the shortest array
ax[0].set_xlim(1,24)
ax[1].set_xlim(1,24)
ax[2].set_xlim(1,24)
fig, ax = plt.subplots(nrows=3, ncols=1, figsize=(6,6), sharex=True)
fig.subplots_adjust(hspace=0.001) # this seem to have no effect
p1=ax[0].imshow(x1[:,::10000], cmap='autumn_r')
p2=ax[1].imshow(x2[:,::10000], cmap='autumn_r')
p3=ax[2].imshow(x[:,::10000], cmap='autumn')
Here is what I could reach so far:
and here is a scheme of what I wish to have since I could not find it on the web. In short, I would like to remove the blank spaces around the plotted data in the two upper graphs. And as a more general question I would like to know if imshow is the best way of obtaining such plot (cf intended results below).
Using fig.subplots_adjust(hspace=0) sets the vertical (height) space between subplots to zero but doesn't adjust the vertical space within each subplot. By default, plt.imshow has a default aspect ratio (rc image.aspect) usually set such that pixels are squares so that you can accurately recreate images. To change this use aspect='auto' and adjust the ylim of your axes accordingly.
For example:
# you don't need all the `expand_dims` and `vstack`ing. Use `reshape`
x0 = np.linspace(5, 0, 25).reshape(1, -1)
x1 = x0**6
x2 = x0**2
fig, axes = plt.subplots(3, 1, sharex=True)
fig.subplots_adjust(hspace=0)
for ax, x in zip(axes, (x0, x1, x2)):
ax.imshow(x, cmap='autumn_r', aspect='auto')
ax.set_ylim(-0.5, 0.5) # alternatively pass extent=[0, 1, 0, 24] to imshow
ax.set_xticks([]) # remove all xticks
ax.set_yticks([]) # remove all yticks
plt.show()
yields
To add a colorbar, I recommend looking at this answer which uses fig.add_axes() or looking at the documentation for AxesDivider (which I personally like better).

grids of graphs in matplotlib

Using the AXIS notation for matplotlib has allowed me to manually plot a grid of 2x2 or 3x3 or whatever size grid (if I know what size grid I want beforehand.)
However, how do you determine what size grid is needed automatically. Like what if you don't know how many unique values are in a column that you want to graph?
I am thinking there must be a way of doing this in a loop and figuring out based on the number of unique values in the column this is how big the graph needs to be.
Example
When I plot this for some reason it doesn't show month_name on the x axis (as in Jan, Feb, Marc etc)
avg_all_account.plot(legend=False,subplots=True,x='month_date',figsize=(10,20))
plt.xlabel('month')
plt.ylabel('number of proposals')
Yet when I plot subplots on a figure and specify x axis paremeter x='month_name' The month name appears on the plot here:
f = plt.figure()
f.set_figheight(8)
f.set_figwidth(8)
f.sharex=True
f.sharey=True
#graph1 = f.add_subplot(2,2,1)
avg_all_account.ix[0:,['month_date','number_open_proposals_all']].plot(ax=f.add_subplot(331),legend=False,subplots=True,x='month_date',y='number_open_proposals_all',title='open proposals')
plt.xlabel('month')
plt.ylabel('number of proposals')
Thus because the subplot method worked and showed the month_name on the x axis, and my x and y axis labels: I wanted to know how would I work out how many subplots I would need without first calculating it, then writing out each line and hard coding the subplot position?

Categories