Plotting axis ticks & labels outside the axes when the spine is moved - python

Following on from this question and some similar situations here, and here, how does one place the x- and y-axis tick marks and labels at the outside of the plot? There is one hack here, that attempts to address the problem since as #whatitis notes, you cannot know the pad in advance in all situations (e.g. ax.get_xaxis().set_tick_params(pad=5)); so it seems there must be a native API approach, but none of the attempts (commented) in the following MWE work
import numpy as np
import matplotlib.pyplot as plt
#data generation
x = np.arange(-10,20,0.2)
y = 1.0/(1.0+np.exp(-x)) # numpy does the calculation elementwise for you
fig, ax = plt.subplots()
ax.plot(x,y)
# Eliminate upper and right axes
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Show ticks on the left and lower axes only (and let them protrude in both directions)
ax.xaxis.set_tick_params(bottom='on', top=False, direction='inout')
ax.yaxis.set_tick_params(left='on', right=False, direction='inout')
# Make spines pass through zero of the other axis
ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')
ax.set_ylim(-0.4,1.0)
ax.set_ylabel(r'y-axis label')
ax.set_xlabel(r'x-axis label')
#
# How to place labels and ticks on the outside of the axes area
#
# Attempt 1 - has no effect
# ax.xaxis.tick_bottom()
# ax.yaxis.tick_left()
# Attempt 2 - has no effect - is this a bug? Why isn't 'top' & 'bottom' where it should be?
ax.yaxis.set_label_position('left')
ax.xaxis.set_label_position('bottom')
plt.show()

Related

Matplotlib - duplicated axes - prevent gridlines from covering data - set right ylabel

I want to duplicate axes so that I can express an exponent in terms of its doubling time.
I think I am doing things right, but I have two problems
no label on the right hand side of the chart and
y-axis gridlines that are plotted above the data that I cannot shift to the bottom, nor remove.
Example code follows:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
MARGINS = 0.02
data = pd.Series(np.arange(0.05, 1.0, 0.1))
# preliminaries
plt.style.use('ggplot')
fig, ax = plt.subplots()
ax.figure.set_size_inches((8, 4))
ax.set_ylabel('$k$') # This works
ax.margins(MARGINS)
ax.set_axisbelow(True)
# duplicate the axes
axr = ax.twinx().twiny()
axr.margins(MARGINS)
axr.set_ylabel('Doubling time') # This does not work
# No x-ticks at the top
axr.xaxis.set_ticks([])
axr.xaxis.set_ticklabels([])
# plot the data
ax.plot(data.index, data)
# label right-hand y-axis
locations = ax.get_yticks()
new_labels = [f'{np.log(2)/x:,.2f}' if x != 0 else '∞' for x in locations ]
axr.yaxis.set_ticks(locations)
axr.yaxis.set_ticklabels(new_labels)
axr.set_axisbelow(True) # this does not work
# match the left and right ylim settings
axr.set_ylim(ax.get_ylim())
axr.set_xlim(ax.get_xlim())
# remove the grid
axr.grid(False, which='both')
axr.yaxis.grid(False, which='both') # this does not work
# finish-up
ax.set_title('Chart')
fig.tight_layout(pad=1.1)
plt.show()
plt.close('all')
Desired output:
Similar chart to above but with:
a right hand side y-axis label
no y-axis gridlines over the data line (but keep the horizontal gridlines under the dataline)
Change the order of twiny and twinx:
axr = ax.twiny().twinx()

matplotlib: axis tick label alignment not enforced with multiple subplots

I've come across a number of posts such as this and this talking about how to align tick labels to avoid overlaps. In fact, the second link is basically exactly what I want to do, which is move the first and last tick (on both X and Y axis) into the plot area. Unfortunately, I'm encountering some odd behavior that I'm hoping someone can explain to me.
The code below generates 3 figures (also shown below). The first is a figure with 1 subplot, and everything works as intended. All tick labels are center-justified except the first and last on each axis, which is properly adjusted to be within the plot area.
Figure 2 has 2 subplots vertically stacked. In this plot the horizontal axis has tick labels properly justified, but on the vertical axis all the positive labels (0-10) have been justified "bottom" when only the last label (10) should have been justified "top". All others should be justified "center" still.
Figure 3 is similar to Figure 2, only with horizontally stacked subplots. In this case, it's the tick labels on the positive horizontal axis that are not correctly justified, with all labels justified "left" when only the final label should be justified "right".
Any clue why on a figure with multiple subplots the tick label justification is not being set correctly? I've made multiple versions of these plots, including embedded in a Tkinter window (my actual application) and I get the exact same result every time.
EDIT: I've added a screenshot of the plots from my actual application, which is a GUI made using Tkinter. The overall window size is 1024x768 and the plots are made using 2 figures, one for the top plot (XY) and one with two subplots for the bottom plots (XZ and YZ). This screenshot is without any resizing of the window.
import matplotlib.pyplot as plt
import matplotlib
def stylize_plot(ax=None, fig=None):
if ax is None:
ax = plt.gca()
if fig is None:
fig = plt.gcf()
ax.axis([-10, 10, -10, 10])
ax.grid(True)
fig.set_tight_layout(True)
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Appears to be shifting all tick labels on positive horizontal axis for Figure with 2 subplots
xTick_objects = ax.xaxis.get_major_ticks()
xTick_objects[0].label1.set_horizontalalignment('left') # left align first tick
xTick_objects[-1].label1.set_horizontalalignment('right') # right align last tick
yTick_objects = ax.yaxis.get_major_ticks()
yTick_objects[0].label1.set_verticalalignment('bottom') # bottom align first tick
yTick_objects[-1].label1.set_verticalalignment('top') # top align last tick
ax.xaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%0.1f'))
ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%0.1f'))
if __name__ == '__main__':
fig = plt.figure()
ax = fig.add_subplot(111)
stylize_plot(ax=ax, fig=fig)
fig2 = plt.figure()
ax2 = fig2.add_subplot(211)
ax3 = fig2.add_subplot(212)
stylize_plot(ax=ax2, fig=fig2)
stylize_plot(ax=ax3, fig=fig2)
fig3 = plt.figure()
ax4 = fig3.add_subplot(121)
ax5 = fig3.add_subplot(122)
stylize_plot(ax=ax4, fig=fig3)
stylize_plot(ax=ax5, fig=fig3)
plt.show()

matplotlib: align y-ticks in twinx [duplicate]

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

adjust matplotlib subplot spacing after tight_layout

I would like to minimize white space in my figure. I have a row of sub plots where four plots share their y-axis and the last plot has a separate axis.
There are no ylabels or ticklabels for the shared axis middle panels.
tight_layout creates a lot of white space between the the middle plots as if leaving space for tick labels and ylabels but I would rather stretch the sub plots. Is this possible?
import matplotlib.gridspec as gridspec
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
fig = plt.figure()
gs = gridspec.GridSpec(1, 5, width_ratios=[4,1,4,1,2])
ax = fig.add_subplot(gs[0])
axes = [ax] + [fig.add_subplot(gs[i], sharey=ax) for i in range(1, 4)]
axes[0].plot(np.random.randint(0,100,100))
barlist=axes[1].bar([1,2],[1,20])
axes[2].plot(np.random.randint(0,100,100))
barlist=axes[3].bar([1,2],[1,20])
axes[0].set_ylabel('data')
axes.append(fig.add_subplot(gs[4]))
axes[4].plot(np.random.randint(0,5,100))
axes[4].set_ylabel('other data')
for ax in axes[1:4]:
plt.setp(ax.get_yticklabels(), visible=False)
sns.despine();
plt.tight_layout(pad=0, w_pad=0, h_pad=0);
Setting w_pad = 0 is not changing the default settings of tight_layout. You need to set something like w_pad = -2. Which produces the following figure:
You could go further, to say -3 but then you would start to get some overlap with your last plot.
Another way could be to remove plt.tight_layout() and set the boundaries yourself using
plt.subplots_adjust(left=0.065, right=0.97, top=0.96, bottom=0.065, wspace=0.14)
Though this can be a bit of a trial and error process.
Edit
A nice looking graph can be achieved by moving the ticks and the labels of the last plot to the right hand side. This answer shows you can do this by using:
ax.yaxis.tick_right()
ax.yaxis.set_label_position("right")
So for your example:
axes[4].yaxis.tick_right()
axes[4].yaxis.set_label_position("right")
In addition, you need to remove sns.despine(). Finally, there is now no need to set w_pad = -2, just use plt.tight_layout(pad=0, w_pad=0, h_pad=0)
Using this creates the following figure:

Hiding axis text in matplotlib plots

I'm trying to plot a figure without tickmarks or numbers on either of the axes (I use axes in the traditional sense, not the matplotlib nomenclature!). An issue I have come across is where matplotlib adjusts the x(y)ticklabels by subtracting a value N, then adds N at the end of the axis.
This may be vague, but the following simplified example highlights the issue, with '6.18' being the offending value of N:
import matplotlib.pyplot as plt
import random
prefix = 6.18
rx = [prefix+(0.001*random.random()) for i in arange(100)]
ry = [prefix+(0.001*random.random()) for i in arange(100)]
plt.plot(rx,ry,'ko')
frame1 = plt.gca()
for xlabel_i in frame1.axes.get_xticklabels():
xlabel_i.set_visible(False)
xlabel_i.set_fontsize(0.0)
for xlabel_i in frame1.axes.get_yticklabels():
xlabel_i.set_fontsize(0.0)
xlabel_i.set_visible(False)
for tick in frame1.axes.get_xticklines():
tick.set_visible(False)
for tick in frame1.axes.get_yticklines():
tick.set_visible(False)
plt.show()
The three things I would like to know are:
How to turn off this behaviour in the first place (although in most cases it is useful, it is not always!) I have looked through matplotlib.axis.XAxis and cannot find anything appropriate
How can I make N disappear (i.e. X.set_visible(False))
Is there a better way to do the above anyway? My final plot would be 4x4 subplots in a figure, if that is relevant.
Instead of hiding each element, you can hide the whole axis:
frame1.axes.get_xaxis().set_visible(False)
frame1.axes.get_yaxis().set_visible(False)
Or, you can set the ticks to an empty list:
frame1.axes.get_xaxis().set_ticks([])
frame1.axes.get_yaxis().set_ticks([])
In this second option, you can still use plt.xlabel() and plt.ylabel() to add labels to the axes.
If you want to hide just the axis text keeping the grid lines:
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
Doing set_visible(False) or set_ticks([]) will also hide the grid lines.
If you are like me and don't always retrieve the axes, ax, when plotting the figure, then a simple solution would be to do
plt.xticks([])
plt.yticks([])
I've colour coded this figure to ease the process.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
You can have full control over the figure using these commands, to complete the answer I've add also the control over the spines:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# X AXIS -BORDER
ax.spines['bottom'].set_visible(False)
# BLUE
ax.set_xticklabels([])
# RED
ax.set_xticks([])
# RED AND BLUE TOGETHER
ax.axes.get_xaxis().set_visible(False)
# Y AXIS -BORDER
ax.spines['left'].set_visible(False)
# YELLOW
ax.set_yticklabels([])
# GREEN
ax.set_yticks([])
# YELLOW AND GREEN TOGHETHER
ax.axes.get_yaxis().set_visible(False)
I was not actually able to render an image without borders or axis data based on any of the code snippets here (even the one accepted at the answer). After digging through some API documentation, I landed on this code to render my image
plt.axis('off')
plt.tick_params(axis='both', left=False, top=False, right=False, bottom=False, labelleft=False, labeltop=False, labelright=False, labelbottom=False)
plt.savefig('foo.png', dpi=100, bbox_inches='tight', pad_inches=0.0)
I used the tick_params call to basically shut down any extra information that might be rendered and I have a perfect graph in my output file.
Somewhat of an old thread but, this seems to be a faster method using the latest version of matplotlib:
set the major formatter for the x-axis
ax.xaxis.set_major_formatter(plt.NullFormatter())
One trick could be setting the color of tick labels as white to hide it!
plt.xticks(color='w')
plt.yticks(color='w')
or to be more generalized (#Armin Okić), you can set it as "None".
When using the object oriented API, the Axes object has two useful methods for removing the axis text, set_xticklabels() and set_xticks().
Say you create a plot using
fig, ax = plt.subplots(1)
ax.plot(x, y)
If you simply want to remove the tick labels, you could use
ax.set_xticklabels([])
or to remove the ticks completely, you could use
ax.set_xticks([])
These methods are useful for specifying exactly where you want the ticks and how you want them labeled. Passing an empty list results in no ticks, or no labels, respectively.
You could simply set xlabel to None, straight in your axis. Below an working example using seaborn
from matplotlib import pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set(xlabel=None)
plt.show()
Just do this in case you have subplots
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
ax[0].set_yticklabels([]) # x-axis
ax[0].set_xticklabels([]) # y-axis

Categories

Resources