How to disable the Matplotlib navigation toolbar in a particular axis? - python

I have a figure with different plots on several axes. Some of those axes do not play well with some of the navigation toolbar actions. In particular, the shortcuts to go back to the home view and the ones to go to the previous and next views.
Is there a way to disable those shortcuts only for those axes? For example, in one of the two in the figure from the example below.
import matplotlib.pyplot as plt
# Example data for two plots
x1 = [1, 2, 3, 4]
y1 = [10, 20, 25, 30]
x2 = [2, 3, 4, 5]
y2 = [5, 15, 20, 25]
# Create figure and axes objects
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
# Plot data on the first axis
ax1.plot(x1, y1)
ax1.set_title("First Plot")
# Plot data on the second axis
ax2.plot(x2, y2)
ax2.set_title("Second Plot")
# Show plot
plt.show()
Edit 1:
The following method will successfully disable the pan and zoom tools from the GUI toolbox in the target axis.
ax2.set_navigate(False)
However, the home, forward, and back buttons remain active. Is there a trick to disable also those buttons in the target axis?

It worked for me when I did as below:
import matplotlib
matplotlib.rcParams['toolbar'] = 'None'
plt = matplotlib.pyplot
# Example data for two plots
x1 = [1, 2, 3, 4]
y1 = [10, 20, 25, 30]
x2 = [2, 3, 4, 5]
y2 = [5, 15, 20, 25]
# Create figure and axes objects
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
# Plot data on the first axis
ax1.plot(x1, y1)
ax1.set_title("First Plot")
# Plot data on the second axis
ax2.plot(x2, y2)
ax2.set_title("Second Plot")
# Show plot
plt.show()
Source.

As I am not sure to fully understand which button you want to enable and disable, I provide here a code to customize your navigation toolbar individually across the different axes. It is using some matplotlib backend tools and you can change the line inside the class definition so it fits with what you want:
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
# Create a custom toolbar that only includes the pan and zoom tools
class CustomToolbar(NavigationToolbar):
toolitems = [t for t in NavigationToolbar.toolitems if
t[0] in ('Pan', 'Zoom')]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
ax1.plot(x1, y1)
ax1.set_title("First Plot")
ax2.plot(x2, y2)
ax2.set_title("Second Plot")
# Set the custom toolbar for the second axis
ax2_navigation_toolbar = CustomToolbar(ax2.figure.canvas, ax2)
ax2.navigation_toolbar = ax2_navigation_toolbar
# Show plot
plt.show()
Hope this helps.

You can try to use ax2.get_xaxis().set_visible(False)

Related

Change the font size of matplotlib.pyplot Axis after its been set

How to change the font size of the X-Axis or Y-Axis on a matplotlib.pyplot plot in Python.
I'm want to change the fon size of the text that has already set and I don't want to re-enter the text. Is it possible to get the axis object and set its font size ?
To change the font size of the x-axis tick labels or y-axis tick labels in a Matplotlib plot, you can use the tick_params function and set the labelsize parameter.
Here is an example code:
import matplotlib.pyplot as plt
# Generate some data to plot
x = [1, 2, 3, 4, 5]
y = [1, 4, 6, 8, 10]
# Create a figure and a subplot
fig, ax = plt.subplots()
# Plot the data
ax.plot(x, y)
# Set the font size of the x-axis and y-axis tick labels
ax.tick_params(axis='x', labelsize=20)
ax.tick_params(axis='y', labelsize=20)
# Show the plot
plt.show()
Also if you didn't stored the fig, ax objects you can reach them with plt.gca()
Here is an example code:
import matplotlib.pyplot as plt
# Generate some data to plot
x = [1, 2, 3, 4, 5]
y = [1, 4, 6, 8, 10]
# Create a figure and a subplot
plt.figure()
# Plot the data
plt.plot(x, y)
# Set the font size of the x-axis and y-axis tick labels
plt.gca().xaxis.label.set_size(20)
plt.gca().yaxis.label.set_size(20)
# Show the plot
plt.show()

invert_xaxis gives an error when using matplotlib plt.barh

I am trying to plot 2 way bar charts. I want to invert the x-axis of x1 so that 0 is in the middle of both. I keep getting the error:
AttributeError: 'BarContainer' object has no attribute 'invert_xaxis'
Here is my code:
import matplotlib.pyplot as plt
y = ['F','M','H']
x1 = [8, 4, 3]
x2 = [2, 4, 7]
fig, axes = plt.subplots(ncols=2, sharey=True)
axes[0] = plt.barh(y, x1, align='center', color='b')
axes[1] = plt.barh(y, x2, align='center', color='r')
axes[0].invert_xaxis()
plt.show()
The problem is that you are assigning the plots to the two axis objects instead of using them to plot. The correct way is to directly use the axis objects to plot the barh. Then the things will work as expected.
import matplotlib.pyplot as plt
y = ['F','M','H']
x1 = [8, 4, 3]
x2 = [2, 4, 7]
fig, axes = plt.subplots(ncols=2, sharey=True)
axes[0].barh(y, x1, align='center', color='b') # <---- Changed here
axes[1].barh(y, x2, align='center', color='r') # <---- Changed here
axes[0].invert_xaxis()
plt.show()

Visibility for twinx grid lines

When creating overlaid bar charts with two different height scales using Axes.twinx(), I cannot set visible the vertical grid lines of the 'twin' axis set. The horizontal lines work fine though. Any thoughts on how to resolve this?
Below is some example code that illustrates what I want to do and what I cannot do. As seen, the vertical grid lines are hidden by the red bars of ax2, whereas I want the grid lines to be visible through all bars.
# Create figure and figure layout
ax1 = plt.subplot()
ax2 = ax1.twinx()
# Example data
x = [0, 1, 2, 3, 4, 5]
h1 = [55, 63, 70, 84, 73, 93]
h2 = [4, 5, 4, 7, 4, 3]
# Plot bars
h1_bars = ax1.bar(x, h1, width=0.6, color='darkblue')
h2_bars = ax2.bar(x, h2, width=0.6, color='darkred')
# Set y limits and grid visibility
for ax, ylim in zip([ax1, ax2], [100, 10]):
ax.set_ylim(0, ylim)
ax.grid(True)
The error comes about because the vertical grid lines of ax2 are not set visible. This can be tested by setting ax1.grid(False), in which case there are only horizontal grid lines.
I have tried all combinations of ax1.xaxis.grid(True), ax1.yaxis.grid(True), ax2.xaxis.grid(True) and ax2.yaxis.grid(True) without any luck. Any help on this matter deeply appreciated!
You may revert the role of ax1 and ax2, such that the blue bars are on ax2 and the red ones on ax1. Then you need to put the twin axes in the background and tick the respective y axes on the other side of the plot.
import matplotlib.pyplot as plt
# Create figure and figure layout
ax1 = plt.subplot()
ax2 = ax1.twinx()
# Example data
x = [0, 1, 2, 3, 4, 5]
h1 = [55, 63, 70, 84, 73, 93]
h2 = [4, 5, 4, 7, 4, 3]
# Plot bars
h1_bars = ax2.bar(x, h1, width=0.6, color='darkblue')
h2_bars = ax1.bar(x, h2, width=0.6, color='darkred')
# Set y limits and grid visibility
for ax, ylim in zip([ax1, ax2], [10, 100]):
ax.set_ylim(0, ylim)
ax.grid(True)
ax1.set_zorder(1)
ax1.patch.set_alpha(0)
ax2.set_zorder(0)
ax1.yaxis.tick_right()
ax2.yaxis.tick_left()
plt.show()

Remove matplotlib text plot border

How to remove matplotlib text border, while making the text be in the first plane, in front of the plotted line?
import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [1, 2, 3]
plt.plot(x, y)
plt.text(2.85, 2.9, 'label', bbox={'facecolor':'white', 'alpha':1, 'pad':10})
plt.show()
Are you asking how to make the text more visible without adding the box behind it? If so, have a look at the last couple of examples.
Controlling the drawing order
The text is already in front of the line, it's just hard to distinguish the two. However, in general, the order of the elements is controlled by the zorder kwarg.
To demonstrate this, I'll change the colors and size of the font in your example to make things a touch more clear:
import matplotlib.pyplot as plt
x = [1, 2, 3]
y =[1, 2, 3]
fig, ax = plt.subplots()
ax.plot(x, y, linewidth=10, color='yellow')
ax.text(2, 2, 'label', ha='center', size=72)
# For the moment, hide everything else...
ax.axis('off')
fig.tight_layout()
plt.show()
If we decrease the z-order of the text below that of the line or increase the zorder of the line above that of the text, the line will be in front. By default, most plotted data types have a zorder of 1, while annotations such as text have a zorder of 3, if I recall correctly. It's just the relative values of zorder that matter, though. In other words, it doesn't matter whether we do ax.text(..., zorder=0) or ax.plot(..., zorder=4), we'll get the same result.
import matplotlib.pyplot as plt
x = [1, 2, 3]
y =[1, 2, 3]
fig, ax = plt.subplots()
ax.plot(x, y, linewidth=10, color='yellow')
ax.text(2, 2, 'label', ha='center', size=72, zorder=0)
# For the moment, hide everything else...
ax.axis('off')
fig.tight_layout()
plt.show()
A more subtle box for clearer labels
However, what you're probably wanting to accomplish is a cleaner way to display the label and the line together.
In that case, you have several different options.
Let's go back to your original example. You can display the box, behind the text, but remove the edge color on the box. So, if you add 'edgecolor':'none' to the dict in the bbox kwarg, you'll get something similar to this:
import matplotlib.pyplot as plt
x = [1, 2, 3]
y =[1, 2, 3]
plt.plot(x, y)
plt.text(2.85, 2.9, 'label',
bbox={'facecolor':'white', 'edgecolor':'none', 'pad':10})
plt.show()
Or as an example of what it would look like using the earlier code snippet with a yellow line:
Using a stroke effect for clear labels
However, this doesn't look as nice if we have more than just a simple line. Therefore, you might also want to consider using a stroke path effect:
import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
x = [1, 2, 3]
y =[1, 2, 3]
fig, ax = plt.subplots()
ax.plot(x, y, linewidth=10, color='yellow')
ax.text(2, 2, 'label', ha='center', size=72,
path_effects=[pe.withStroke(linewidth=10, foreground='w')])
# For the moment, hide everything else...
ax.axis('off')
fig.tight_layout()
fig.set(facecolor='white')
plt.show()

Matplotlib different size subplots

I need to add two subplots to a figure. One subplot needs to be about three times as wide as the second (same height). I accomplished this using GridSpec and the colspan argument but I would like to do this using figure so I can save to PDF. I can adjust the first figure using the figsize argument in the constructor, but how do I change the size of the second plot?
As of matplotlib 3.6.0, width_ratios and height_ratios can now be passed directly as keyword arguments to plt.subplots and subplot_mosaic, as per What's new in Matplotlib 3.6.0 (Sep 15, 2022).
f, (a0, a1) = plt.subplots(1, 2, width_ratios=[3, 1])
f, (a0, a1, a2) = plt.subplots(3, 1, height_ratios=[1, 1, 3])
Another way is to use the subplots function and pass the width ratio with gridspec_kw
matplotlib Tutorial: Customizing Figure Layouts Using GridSpec and Other Functions
matplotlib.gridspec.GridSpec has available gridspect_kw options
import numpy as np
import matplotlib.pyplot as plt
# generate some data
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# plot it
f, (a0, a1) = plt.subplots(1, 2, gridspec_kw={'width_ratios': [3, 1]})
a0.plot(x, y)
a1.plot(y, x)
f.tight_layout()
f.savefig('grid_figure.pdf')
Because the question is canonical, here is an example with vertical subplots.
# plot it
f, (a0, a1, a2) = plt.subplots(3, 1, gridspec_kw={'height_ratios': [1, 1, 3]})
a0.plot(x, y)
a1.plot(x, y)
a2.plot(x, y)
f.tight_layout()
You can use gridspec and figure:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
# generate some data
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# plot it
fig = plt.figure(figsize=(8, 6))
gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1])
ax0 = plt.subplot(gs[0])
ax0.plot(x, y)
ax1 = plt.subplot(gs[1])
ax1.plot(y, x)
plt.tight_layout()
plt.savefig('grid_figure.pdf')
I used pyplot's axes object to manually adjust the sizes without using GridSpec:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# definitions for the axes
left, width = 0.07, 0.65
bottom, height = 0.1, .8
bottom_h = left_h = left+width+0.02
rect_cones = [left, bottom, width, height]
rect_box = [left_h, bottom, 0.17, height]
fig = plt.figure()
cones = plt.axes(rect_cones)
box = plt.axes(rect_box)
cones.plot(x, y)
box.plot(y, x)
plt.show()
Probably the simplest way is using subplot2grid, described in Customizing Location of Subplot Using GridSpec.
ax = plt.subplot2grid((2, 2), (0, 0))
is equal to
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(2, 2)
ax = plt.subplot(gs[0, 0])
so bmu's example becomes:
import numpy as np
import matplotlib.pyplot as plt
# generate some data
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# plot it
fig = plt.figure(figsize=(8, 6))
ax0 = plt.subplot2grid((1, 3), (0, 0), colspan=2)
ax0.plot(x, y)
ax1 = plt.subplot2grid((1, 3), (0, 2))
ax1.plot(y, x)
plt.tight_layout()
plt.savefig('grid_figure.pdf')
In a simple way, different size sub plotting can also be done without gridspec:
plt.figure(figsize=(12, 6))
ax1 = plt.subplot(2,3,1)
ax2 = plt.subplot(2,3,2)
ax3 = plt.subplot(2,3,3)
ax4 = plt.subplot(2,1,2)
axes = [ax1, ax2, ax3, ax4]
A nice way of doing this was added in matplotlib 3.3.0, subplot_mosaic.
You can make a nice layout using an "ASCII art" style.
For example
fig, axes = plt.subplot_mosaic("ABC;DDD")
will give you three axes on the top row and one spanning the full width on the bottom row like below
A nice thing about this method is that the axes returned from the function is a dictionary with the names you define, making it easier to keep track of what is what e.g.
axes["A"].plot([1, 2, 3], [1, 2, 3])
You can also pass a list of lists to subplot_mosaic if you want to use longer names
fig, axes = plt.subplot_mosaic(
[["top left", "top centre", "top right"],
["bottom row", "bottom row", "bottom row"]]
)
axes["top left"].plot([1, 2, 3], [1, 2, 3])
will produce the same figure

Categories

Resources