Dynamically add subplots in matplotlib with more than one column

前端 未结 3 1241
囚心锁ツ
囚心锁ツ 2020-12-30 13:26

How can I dynamically add a new plot to bunch of subplots if I\'m using more than one column to display my subplots? This answers this question for one column, but I cant se

相关标签:
3条回答
  • 2020-12-30 13:33

    Here's the solution I ended up with. It lets you reference subplots by name, and adds a new subplot if that name has not been used yet, repositioning all previous subplots in the process.

    Usage:

    set_named_subplot('plot-a')  # Create a new plot
    plt.plot(np.sin(np.linspace(0, 10, 100)))  # Plot a curve
    
    set_named_subplot('plot-b')  # Create a new plot
    plt.imshow(np.random.randn(10, 10))   # Draw image
    
    set_named_subplot('plot-a')   # Set the first plot as the current one
    plt.plot(np.cos(np.linspace(0, 10, 100)))  # Plot another curve in the first plot
    
    plt.show()  # Will show two plots
    

    The code:

    import matplotlib.pyplot as plt
    import numpy as np
    
    
    def add_subplot(fig = None, layout = 'grid'):
        """
        Add a subplot, and adjust the positions of the other subplots appropriately.
        Lifted from this answer: http://stackoverflow.com/a/29962074/851699
    
        :param fig: The figure, or None to select current figure
        :param layout: 'h' for horizontal layout, 'v' for vertical layout, 'g' for approximately-square grid
        :return: A new axes object
        """
        if fig is None:
            fig = plt.gcf()
        n = len(fig.axes)
        n_rows, n_cols = (1, n+1) if layout in ('h', 'horizontal') else (n+1, 1) if layout in ('v', 'vertical') else \
            vector_length_to_tile_dims(n+1) if layout in ('g', 'grid') else bad_value(layout)
        for i in range(n):
            fig.axes[i].change_geometry(n_rows, n_cols, i+1)
        ax = fig.add_subplot(n_rows, n_cols, n+1)
        return ax
    
    
    _subplots = {}
    
    
    def set_named_subplot(name, fig=None, layout='grid'):
        """
        Set the current axes.  If "name" has been defined, just return that axes, otherwise make a new one.
    
        :param name: The name of the subplot
        :param fig: The figure, or None to select current figure
        :param layout: 'h' for horizontal layout, 'v' for vertical layout, 'g' for approximately-square grid
        :return: An axes object
        """
        if name in _subplots:
            plt.subplot(_subplots[name])
        else:
            _subplots[name] = add_subplot(fig=fig, layout=layout)
        return _subplots[name]
    
    
    def vector_length_to_tile_dims(vector_length):
        """
        You have vector_length tiles to put in a 2-D grid.  Find the size
        of the grid that best matches the desired aspect ratio.
    
        TODO: Actually do this with aspect ratio
    
        :param vector_length:
        :param desired_aspect_ratio:
        :return: n_rows, n_cols
        """
        n_cols = np.ceil(np.sqrt(vector_length))
        n_rows = np.ceil(vector_length/n_cols)
        grid_shape = int(n_rows), int(n_cols)
        return grid_shape
    
    
    def bad_value(value, explanation = None):
        """
        :param value: Raise ValueError.  Useful when doing conditional assignment.
        e.g.
        dutch_hand = 'links' if eng_hand=='left' else 'rechts' if eng_hand=='right' else bad_value(eng_hand)
        """
        raise ValueError('Bad Value: %s%s' % (value, ': '+explanation if explanation is not None else ''))
    
    0 讨论(0)
  • 2020-12-30 13:49

    Assuming at least 1 dimension of your grid and the total number of plots is known, I would use the gridspec module and a little bit of math.

    import math
    
    import matplotlib.pyplot as plt
    from matplotlib import gridspec
    
    def do_plot(ax):
        ax.plot([1,2,3], [4,5,6], 'k.')
    
    
    N = 11
    cols = 3
    rows = int(math.ceil(N / cols))
    
    gs = gridspec.GridSpec(rows, cols)
    fig = plt.figure()
    for n in range(N):
        ax = fig.add_subplot(gs[n])
        do_plot(ax)
    
    fig.tight_layout()
    

    0 讨论(0)
  • 2020-12-30 13:51
    from math import ceil
    from PyQt5 import QtWidgets, QtCore
    from matplotlib.gridspec import GridSpec
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
    
    
    class MplCanvas(FigureCanvas):
        """
        Frontend class. This is the FigureCanvas as well as plotting functionality.
        Plotting use pyqt5.
        """
    
        def __init__(self, parent=None):
            self.figure = Figure()
    
            gs = GridSpec(1,1)
    
            self.figure.add_subplot(gs[0])
            self.axes = self.figure.axes
    
            super().__init__(self.figure)
    
            self.canvas = self.figure.canvas
            self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
            self.updateGeometry()
            self.setParent(parent)
    
        def add(self, cols=2):
            N = len(self.axes) + 1
            rows = int(ceil(N / cols))
            grid = GridSpec(rows, cols)
    
            for gs, ax in zip(grid, self.axes):
                ax.set_position(gs.get_position(self.figure))
    
            self.figure.add_subplot(grid[N-1])
            self.axes = self.figure.axes
            self.canvas.draw()
    

    Was doing some PyQt5 work, but the add method shows how to dynamically add a new subplot. The set_position method of Axes is used to change the old position to new position. Then you add a new subplot with the new position.

    0 讨论(0)
提交回复
热议问题