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.
Related
I have a plot of values w/ errorbars with a categorical variable on the X-axis, made using the errorbar method. I have followed the instructions here to create a discontinuity in the X-axis (I'm only showing points with values > or < some absolute threshold).
So far so good, with one slight issue - the axis break ends up being exactly on a data point on each side, which makes it harder to read on the axis, and also, the datapoints themselves end up split in two, which is kind of awkward.
I would like the axis break to instead be between datapoints, something like:
----(last value before break)--/ /--(first value after break)----
Is there a way to do this?
The breakpoints are determined with set_xlim, and I'm not sure if there's a way to do move them off the datapoints with a categorical x-axis...
sort_res_h = sort_res_a[sort_res_a > threshold]
sort_res_l = sort_res_a[sort_res_a < -threshold]
ax0.errorbar(sort_res_a.index, sort_res_a, yerr=chg_dpm_err, fmt='o')
ax1.errorbar(sort_res_a.index, sort_res_a, yerr=chg_dpm_err, fmt='o')
ax0.set_xlim(xmax=sort_res_h.index[-1])
ax1.set_xlim(xmin=sort_res_l.index[0])
I am going to share what I have been able to complete so far, although it is not a flawless solution. But maybe this will help you in any case. I would propose that you set your xaxis limits using the ticks positions, instead of the data. The problem is that if you take ticks as provided by default by matplotlib, sometimes there are more ticks than those that you see (for example there could be ticks before the minimum value in the xaxis or after the maximum). As in your case you seem to be setting the ticks, I think this should work (Replace the way you set your axes limits by this. I only provide the code for the ax0):
# First we get current ticks positions
ax0_xticks_positions = ax0.get_xticks()
# then we get the distance between two ticks
ax0_ticks_distance = ax0_xticks_positions[1] - ax0_xticks_positions[0]
# percentage of tick separation that we want the axis to extend beyond/before the last/first tick
percentage_add = 0.5
# set the xlim to the last tick position plus a percentage of tick distance
ax0.set_xlim(xmax = ax0_xticks_positions[-1] + percentage_add*ax0_ticks_distance)
You can play around with percentage_add until you find a value that suits you. For ax1 you would have to use the first tick:
ax1.set_xlim(xmin = ax1_xticks_positions[0] - percentage_add*ax0_ticks_distance)
The problem of this solution, is if the tick you use to fix the limit is outside the current axis limits. That would make the breakpoint go further than you would expect. To solve that, you can compare the tick position (for example ax0_xticks_positions[-1]) to the applicable axis limit (following the example ax0.get_xlim()[-1]). If the tick position is larger than the limit, you would have to use the second to last tick ax0_xticks_positions[-2]
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.
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
I created a matplotlib plot that has 2 y-axes. The y-axes have different scales, but I want the ticks and grid to be aligned. I am pulling the data from excel files, so there is no way to know the max limits beforehand. I have tried the following code.
# creates double-y axis
ax2 = ax1.twinx()
locs = ax1.yaxis.get_ticklocs()
ax2.set_yticks(locs)
The problem now is that the ticks on ax2 do not have labels anymore. Can anyone give me a good way to align ticks with different scales?
Aligning the tick locations of two different scales would mean to give up on the nice automatic tick locator and set the ticks to the same positions on the secondary axes as on the original one.
The idea is to establish a relation between the two axes scales using a function and set the ticks of the second axes at the positions of those of the first.
import matplotlib.pyplot as plt
import matplotlib.ticker
fig, ax = plt.subplots()
# creates double-y axis
ax2 = ax.twinx()
ax.plot(range(5), [1,2,3,4,5])
ax2.plot(range(6), [13,17,14,13,16,12])
ax.grid()
l = ax.get_ylim()
l2 = ax2.get_ylim()
f = lambda x : l2[0]+(x-l[0])/(l[1]-l[0])*(l2[1]-l2[0])
ticks = f(ax.get_yticks())
ax2.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(ticks))
plt.show()
Note that this is a solution for the general case and it might result in totally unreadable labels depeding on the use case. If you happen to have more a priori information on the axes range, better solutions may be possible.
Also see this question for a case where automatic tick locations of the first axes is sacrificed for an easier setting of the secondary axes tick locations.
To anyone who's wondering (and for my future reference), the lambda function f in ImportanceofBeingErnest's answer maps the input left tick to a corresponding right tick through:
RHS tick = Bottom RHS tick + (% of LHS range traversed * RHS range)
Refer to this question on tick formatting to truncate decimal places:
from matplotlib.ticker import FormatStrFormatter
ax2.yaxis.set_major_formatter(FormatStrFormatter('%.2f')) # ax2 is the RHS y-axis
I am trying to plot a graph using Matplotlib using the following code:
fig, axs = plt.subplots()
axs.set_xlim([1,5])
axs.grid()
axs.errorbar(plot1_dataerr[1],range(len(plot1_dataerr[1])),xerr = plot1_dataerr[2], fmt = 'k o')
axs.yaxis.set_ticks(np.arange(len(plot1_dataerr[1])))
axs.set_yticklabels(plot1_dataerr[0])
The variable plot1_dataerr contains the labels for the data as its 0th element, the actual means as the 1st element and the half-length of the error bars as the second element. When I run this code (along with the exact data) I get the following:
However as you can see some of the ticks on the y-axis are cut off, they should all start with 'vegetable based side dishes'. Does anyone know what I should change so that everything fits. I don't mind if some of the labels need to occupy 2 lines.
Thanks in advance!
You probably need to increase the left margin. For automatic adjustment, use
fig.tight_layout()
Else, start with
fig.subplots_adjust(left=0.4)
and decrease the value until you are happy with the result.