Pyplot: change size of tick lines on axis - python

Consider this reproducable example:
import numpy as np
import matplotlib.pyplot as plt
labels = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21']
fig, axes = plt.subplots(nrows=3, ncols=3,figsize=(6, 3.5),dpi=300)
plt.subplots_adjust(wspace=0.025, hspace=0.2)
for ax in [a for b in axes for a in b]:
ax.imshow(np.random.randint(2, size=(22,42)))
ax.set_xticks([0,6,12,18,24,30,36,41])
ax.tick_params(axis='x', which='major', labelsize=3)
ax.set_yticks([])
ax.set_aspect('equal')
for ax in [item for sublist in axes for item in sublist][0::3]:
ax.set_yticks(range(len(labels)))
ax.set_yticklabels(labels,fontsize=3)
fig.savefig("example.png",bbox_inches='tight')
My issue is that even though I changed the tick font size, the lines of each tick remain the same. This looks ugly and wastes a lot of space, especially on the X axes. Any ideas how to get those lines smaller, thus that the xlabels are closer to the axis?
PS tight_layout() does not help.

Make
ax.tick_params(axis='x', which='major', labelsize=3, length=2)
and, right after,
ax.tick_params(axis='y', which='major', labelsize=3, length=2)
This will make the ticks smaller, but won't get the labels any closer to the chart.
PS: length=1 will make the ticks even smaller, obviously.

You could play with the width parameter:
ax.tick_params(axis='x', which='major', labelsize=3, width=0.1)

Instead of (line 4):
fig, axes = plt.subplots(nrows=3, ncols=3,figsize=(6, 3.5),dpi=300)
You can manipulate figsize. Example:
fig, axes = plt.subplots(nrows=3, ncols=3,figsize=(3, 2),dpi=300).
Lets's compare both outputs:
The numbers on axes are bigger than old one.

Related

How to include the outside legend into the generated file?

I am plotting many lines on several axes, so I have a several fairly busy plots, thus I need to place the legend outside of the figure:
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
fig.tight_layout()
axes[0].legend(loc="upper left", bbox_to_anchor=(1,0,1,1))
which produces something like
However, when I save the figure using fig.savefig("test.png"), I get this:
note the missing legend.
How do I save the figure so that the legend is included?
One option is to tell tight_layout() not to use the full width. That leaves enough room for your legend. I'm not sure if there's a way measure the width of your legend in code, but I experimentally found this fits your legend:
import matplotlib.pyplot as plt
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
fig.tight_layout(rect=(0, 0, 0.84, 1))
axes[0].legend(loc="upper left", bbox_to_anchor=(1,0,1,1))
fig.savefig("test.png")
After some experimentation, though, it seems like simplifying the call to legend() tells tight_layout() about the legend and to leave room for it. Now, making the names longer automatically makes the plots smaller so that everything fits.
There was a problem with tight_layout() leaving gaps between subplots, because the legend was taller than the subplot. We put a single entry in the legend, call tight_layout(), then put all the entries in the legend. The legend extends below the bottom of the first subplot, but that's what we want.
If the names are different lengths, you'd have to do some more trickery to use the longest name instead of the first name or split all the entries across all the subplots before calling tight_layout().
import matplotlib.pyplot as plt
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
# Create a legend with only one entry, so tight_layout doesn't stretch down.
handles, labels = axes[0].get_legend_handles_labels()
axes[0].legend(handles[:1], labels[:1], bbox_to_anchor=(1, 1))
fig.tight_layout()
# Use all the entries without worrying about expanding below the subplot.
axes[0].legend(handles, labels, bbox_to_anchor=(1, 1))
fig.savefig("test.png")
Use plt.subplots_adjust and you can customize the space around the figure. Here I just used plt.subplots_adjust(right=0.8) but you can adjust all the settings (including top, bottom, left, hspace, wspace)
import numpy as np
nrows = 4
fig = plt.figure(figsize=(6, 2*nrows))
axes = fig.subplots(nrows=nrows, ncols=1)
names = [f"name-{n}" for n in range(10)]
for ax in axes:
for n in names:
ax.plot(np.arange(10),np.random.normal(size=10),label=n)
fig.tight_layout()
axes[0].legend(loc="upper left", bbox_to_anchor=(1,0,1,1))
fig.subplots_adjust(right=0.80)
fig.savefig("test.png")
Saved image:

How can axes be despined in Matplotlib?

I am trying to make the below grid of plots a little bit cleaner. I don't want the tick marks on the left side and the bottom to overlap. I have tried to despine the axes by trying the below code, but it doesn't seem to work. Anyone have any suggestions?
fig, ax = plt.subplots(figsize=(15,10))
cols = ['x6', 'x7', 'x16', 'x17']
subset = df[cols]
normed_df = (subset-subset.min())/(subset.max()-subset.min())
style.use('seaborn-darkgrid')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
for sp in range(4):
ax = fig.add_subplot(2,2, sp+1)
ax.hist(normed_df[cols[sp]], density=True)
normed_df[cols[sp]].plot.kde(ax=ax)
ax.tick_params(bottom="off", top="off", left="off", right="off")
After running the above code, I am getting the following plots, however, the ticks are still overlapping.
either do what #Arne suggested:
fig, ax = plt.subplots(rows, cols) #makes a grid of subplots
or make your first two lines this:
fig, ax = plt.subplots(figsize=(15,10))
ax.axis('off')
this will remove the axis around the entire subplot before adding your additional subplots
When you call plt.subplots() without specifying a grid, it creates those axes across the whole figure whose tick marks and labels interfere with your subplot tick labels in the final plot. So change your first line of code to this:
fig, ax = plt.subplots(2, 2, figsize=(15,10))

set_markersize not working for right side axis

I'm messing around with some plot styles and ran into a curiosity. I have a plot with twinx() to produce ticks on the right-hand side as well as the left. I want to stagger some ticks, some going farther out that others.
I can add padding to any tick on any axes and push out the text via ax.yaxis.get_major_ticks()[1].set_pad(), but when I try to lengthen the tick via ax.yaxis.get_major_ticks()[1].tick1line.set_markersize(), it works for all axes EXCEPT the right side. Any insight?
Please see the code below. I've tried switching up the axis (ax1, ax2) and index.
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
t = np.linspace(0,5)
x = np.exp(-t)*np.sin(2*t)
fig, ax1 = plt.subplots()
ax1.plot(t, x, alpha=0.0)
ax2 = ax1.twinx()
ax2.plot(t, x, alpha=1.0)
ax1.set_xticks([0,1,2])
ax1.set_yticks([0.1, 0.2])
ax2.set_yticks([0.3, 0.4, 0.5])
ax2.set_xticks([1,2,3])
ax1.grid(True, color='lightgray')
ax2.grid(True, color='lightgray')
for a in [ax1, ax2]:
a.spines["top"].set_visible(False)
a.spines["right"].set_visible(False)
a.spines["left"].set_visible(False)
a.spines["bottom"].set_visible(False)
ax1.set_axisbelow(True)
ax2.set_axisbelow(True)
ax1.xaxis.get_major_ticks()[1].set_pad(15) #
ax1.xaxis.get_major_ticks()[1].tick1line.set_markersize(15)
ax1.yaxis.get_major_ticks()[1].set_pad(15) #
ax1.yaxis.get_major_ticks()[1].tick1line.set_markersize(15)
ax2.yaxis.get_major_ticks()[1].set_pad(15) #
ax2.yaxis.get_major_ticks()[1].tick1line.set_markersize(15)
plt.savefig('fig.pdf')
plt.show()
You need to use tick2line instead of tick1line, since that's the one referring to the top/right axis, according to the documentation.
Change ax2.yaxis.get_major_ticks()[1].tick1line.set_markersize(15) for:
ax2.yaxis.get_major_ticks()[1].tick2line.set_markersize(15)
Result:

Matplotlib won't show minor ticks when using subplots

I have several subplots and want to adjust the axis ticks settings by ax.tick_params. Everything works fine, however, the minor ticks are not shown. Here is a code example
import matplotlib.pyplot as plt
x = np.linspace(0,1,100)
y = x*x
f, (ax1,ax2) = plt.subplots(2, 1)
ax1.tick_params(axis="both", direction="in", which="both", right=False, top=True)
ax2.tick_params(axis="both", direction="in", which="both", right=True, top=False)
ax1.plot(x,y)
ax2.plot(x,-y)
plt.show()
I assumed that which=both would give me the minor ticks. However I need to add an additional
plt.minorticks_on()
which makes them visible but only in ax2.
How do I fix this?
With pyplot the danger is that you loose track of which one the current axes is that a command like plt.minorticks_on() operates on. Hence it would be beneficial to use the respective methods of the axes you're working with:
ax1.minorticks_on()
ax2.minorticks_on()
plt would work on the current axis which is ax2 in your case. One way is to first enable them using the way you did and then specify the number of minor ticks using AutoMinorLocator
from matplotlib.ticker import AutoMinorLocator
ax1.tick_params(axis="both", direction="in", which="both", right=False, top=True)
ax2.tick_params(axis="both", direction="in", which="both", right=True, top=False)
ax1.plot(x,y)
ax2.plot(x,-y)
for ax in [ax1, ax2]:
ax.xaxis.set_minor_locator(AutoMinorLocator(4))
ax.yaxis.set_minor_locator(AutoMinorLocator(4))

Increasing tick size by using axes in matplotlib

I am trying to create two axes within one figure
fig, ax = plt.subplots(2,figsize=(20,16))
This is my first figure:
ax[0].scatter(x,y, color="brown", alpha=0.4, s=200)
ax[0].plot(x,lof, color="brown", alpha=0.4)
for the first axes I want to make the x_ticks and y_ticks bigger how can I go about this?
You can use tick_params:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2,figsize=(6, 4))
ax[0].scatter([1,2,3],[1,2,3], color="brown", alpha=0.4, s=200)
ax[0].tick_params(width=2, length=4)
ax[1].tick_params(width=3, length=6)
ax[1].plot([1,2,3],[1,2,3], color="brown", alpha=0.4)
With it you can change all appearance properties of it. Here are the docs:
https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.tick_params.html
One way is to iterate over the major x- and y-ticks of the desired subplot (ax[0] here) and changing their font size.
Minimal representative answer
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2,figsize=(8, 4))
ax[0].scatter([1,2,3],[1,2,3], color="brown", alpha=0.4, s=200)
ax[1].plot([1,2,3],[1,2,3], color="brown", alpha=0.4)
for tick in ax[0].xaxis.get_major_ticks():
tick.label.set_fontsize(16)
for tick in ax[0].yaxis.get_major_ticks():
tick.label.set_fontsize(16)
plt.tight_layout()
plt.show()
If you don't need to differentiate between the X and Y axes, or major and minor ticks, use tick_params:
tick_size = 14
ax.tick_params(size=tick_size)
If you want to change the size of the tick labels, then you want this:
label_size = 14
ax.tick_params(labelsize=label_size)

Categories

Resources