Skip to content

colorbar might shrink plots if used with twinx #8823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
PrometheusPi opened this issue Jun 29, 2017 · 7 comments
Closed

colorbar might shrink plots if used with twinx #8823

PrometheusPi opened this issue Jun 29, 2017 · 7 comments
Milestone

Comments

@PrometheusPi
Copy link

Bug report

Changing plot alignment for different placing of colorbar() in twinx plot

If one combines a pcolormesh plot/hist2d plot with a standard plot by using twinx, the graph from the plot moves to wrong x values if the colorbar is called after the plot call. If it is called before, the alignment is fine.

I am not sure if this is a bug or just wrong usage.
However, one of the resulting graphs has a wrong extent that might lead to misinterpretations.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt

data = np.random.random((64, 128)) * 2.0
x = np.arange(128)
y = np.arange(64)
plt.pcolormesh(x, y, data)

#plt.colorbar() --> here it works fine
plt.twinx()
plt.colorbar()
# --> here it gives wrong x values for the orange plot

plt.plot(x, np.sin(x/10), color="orange", lw=4)

plt.show()

Actual outcome

misaligned_twinx_wrong

Next to the confusing layout the plot shows actually a wrong graph:
the orange plot just goes to around x=100 but should go to x=128.
(The axis of the colorbar is correct and shows the range of data.)

Expected outcome

misaligned_twinx_correct

If colorbar is called before twinx, the orange graph has the correct extent.

I am not sure if this is a bug or a wrong usage of matplotlib. In any case the wrong extent of the (in this case) orange plot is a dangerous behavior that might lead to misinterpretations of the data.

Matplotlib version

  • Operating System: Ubuntu 14.04.1
  • Matplotlib Version: 2.0.0rc1
  • Python Version: 3.4.3
  • Jupyter Version: 1.0.0 (I am using %matplotlib inline)
  • Other Libraries: numpy 1.12.0b1

I installed matplotlib via pip.

@efiring
Copy link
Member

efiring commented Jun 29, 2017

I would call it a shortcoming of the twinx implementation: it makes a second Axes at the same position as the first, but it has no mechanism for ensuring that if the position of either is subsequently changed, the position of the other is also changed to match it. Ideally, the position of both Axes would be the same object so that it would not be necessary to use callbacks to keep them synchronized. There are several attributes related to position, so I'm not sure whether this would be a simple change, a difficult one, or a complete can of worms.

@tacaswell tacaswell added this to the 2.2 (next next feature release) milestone Jun 30, 2017
@jklymak
Copy link
Member

jklymak commented Dec 11, 2017

#9082 addresses this (though you can't use the pyplot interface)

data = np.random.random((64, 128)) * 2.0
x = np.arange(128)
y = np.arange(64)
fig, ax = plt.subplots(constrained_layout=True)
pcm = ax.pcolormesh(x, y, data)

#plt.colorbar() --> here it works fine
ax2 = ax.twinx()
fig.colorbar(pcm)
# --> here it gives wrong x values for the orange plot

ax2.plot(x, np.sin(x/10), color="orange", lw=4)

plt.show()

figure_1

@PrometheusPi
Copy link
Author

@jklymak Thanks for posting this. I will cherry pick your pull request.

@efiring
Copy link
Member

efiring commented Jan 21, 2018

This is also addressed by #10033.

@efiring
Copy link
Member

efiring commented Jan 26, 2018

Closed by #10033.

@efiring efiring closed this as completed Jan 26, 2018
@QuLogic QuLogic modified the milestones: needs sorting, v2.2.0 Feb 12, 2018
@bactone
Copy link

bactone commented Nov 24, 2021

while I got the same problem, here is my code: if a twinx exist, then if you remove the colorbar, and the axe shrink, even if you apply tight layout, it won't recover.

import sys
import numpy as np
from PyQt5 import QtWidgets

import matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt


matplotlib.use("Qt5Agg")


class MplCanvas(FigureCanvasQTAgg):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig = plt.figure(figsize=(width, height), dpi=dpi)
        self.axe = self.fig.add_subplot(1, 1, 1, label='good')
        super().__init__(self.fig)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        v_layout = QtWidgets.QVBoxLayout()

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.axe = self.canvas.axe
        self.figure = self.canvas.fig
        
        # draw a scatter plot on self.axe
        np.random.seed(19680801)
        rx, ry = 3., 1.
        area = rx * ry * np.pi
        theta = np.arange(0, 2 * np.pi + 0.01, 0.1)
        verts = np.column_stack([rx / area * np.cos(theta), ry / area * np.sin(theta)])
        x, y, s, c = np.random.rand(4, 30)
        s *= 10 ** 2
        self.image = self.axe.scatter(x, y, s=s, c=c, marker=verts)

        # add a twinx: it fails the tight layout*********************
        x = np.arange(0, 1, 0.01)
        y = np.random.randn(len(x))
        self.ax2 = self.axe.twinx()
        self.ax2.plot(x, y, lw=2, linestyle='-', label='signal')
        # ***********************************************************

        toolbar = NavigationToolbar(self.canvas, self)
        v_layout.addWidget(toolbar)

        v_layout.addWidget(self.canvas)
        label = QtWidgets.QLabel('Show Color Bar')
        self.checkBox_colorBar = QtWidgets.QCheckBox(self)
        h_layout = QtWidgets.QHBoxLayout(self)
        h_layout.addWidget(label)
        h_layout.addWidget(self.checkBox_colorBar)

        v_layout.addLayout(h_layout)

        widget = QtWidgets.QWidget()
        widget.setLayout(v_layout)
        self.setCentralWidget(widget)

        self.checkBox_colorBar.toggled.connect(self.showColorBar)

    def showColorBar(self):

        color_bar = self.image.colorbar  # None if no color bar

        if self.checkBox_colorBar.isChecked():
            if color_bar is None:
                self.figure.colorbar(self.image)
        else:
            if color_bar is not None:
                color_bar.remove()
                del color_bar

        self.canvas.draw()


if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)

    window = MainWindow()
    mw = window
    mw.show()
    app.exec_()

it gives out the following result if toggle the show color bar check box a few times.
image
image
image
image

@jklymak
Copy link
Member

jklymak commented Nov 24, 2021

Please open a new bug report including all relevant version info. However I do not think removing a Colorbar will magically reallocate its space using tight_layout. It may work with constrained_layout

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants