Agregue de forma dinámica subplots en matplotlib con más de una columna

¿Cómo puedo agregar dinámicamente una nueva ttwig a un grupo de subplots si estoy usando más de una columna para mostrar mis subplots? Esto responde a esta pregunta para una columna, pero parece que no puedo modificar las respuestas para que se agreguen dinámicamente a un subplot con x columnas

Modifiqué la respuesta de Sadarthrion e intenté lo siguiente. Aquí, por un ejemplo, hice number_of_subplots=11 y num_cols = 3 .

 import matplotlib.pyplot as plt def plotSubplots(number_of_subplots,num_cols): # Start with one fig = plt.figure() ax = fig.add_subplot(111) ax.plot([1,2,3]) for j in range(number_of_subplots): if j > 0: # Now later you get a new subplot; change the geometry of the existing n = len(fig.axes) for i in range(n): fig.axes[i].change_geometry(n+1, num_cols, i+1) # Add the new ax = fig.add_subplot(n+1, 1, n+1) ax.plot([4,5,6]) plt.show() plotSubplots(11,3) 

introduzca la descripción de la imagen aquí

Como puedes ver, esto no me da lo que quiero. La primera plot ocupa todas las columnas y las plots adicionales son más pequeñas de lo que deberían ser

EDITAR:

('2.7.6 | 64-bit | (default, Sep 15 2014, 17:36:35) [MSC v.1500 64 bit (AMD64)]'

También tengo matplotlib versión 1.4.3:

 import matplotlib as mpl print mpl.__version__ 1.4.3 

Probé la respuesta de Paul a continuación y recibí el siguiente mensaje de error:

 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 = 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() --------------------------------------------------------------------------- TypeError Traceback (most recent call last)  in () 15 fig = plt.figure() 16 for n in range(N): ---> 17 ax = fig.add_subplot(gs[n]) 18 do_plot(ax) 19 C:\Users\user\AppData\Local\Enthought\Canopy\User\lib\site-packages\matplotlib\figure.pyc in add_subplot(self, *args, **kwargs) 962 self._axstack.remove(ax) 963 --> 964 a = subplot_class_factory(projection_class)(self, *args, **kwargs) 965 966 self._axstack.add(key, a) C:\Users\user\AppData\Local\Enthought\Canopy\User\lib\site-packages\matplotlib\axes\_subplots.pyc in __init__(self, fig, *args, **kwargs) 73 raise ValueError('Illegal argument(s) to subplot: %s' % (args,)) 74 ---> 75 self.update_params() 76 77 # _axes_class is set in the subplot_class_factory C:\Users\user\AppData\Local\Enthought\Canopy\User\lib\site-packages\matplotlib\axes\_subplots.pyc in update_params(self) 113 self.figbox, self.rowNum, self.colNum, self.numRows, self.numCols = 114 self.get_subplotspec().get_position(self.figure, --> 115 return_all=True) 116 117 def is_first_col(self): C:\Users\user\AppData\Local\Enthought\Canopy\User\lib\site-packages\matplotlib\gridspec.pyc in get_position(self, fig, return_all) 423 424 figBottoms, figTops, figLefts, figRights = --> 425 gridspec.get_grid_positions(fig) 426 427 C:\Users\user\AppData\Local\Enthought\Canopy\User\lib\site-packages\matplotlib\gridspec.pyc in get_grid_positions(self, fig) 103 cellHeights = [netHeight*r/tr for r in self._row_height_ratios] 104 else: --> 105 cellHeights = [cellH] * nrows 106 107 sepHeights = [0] + ([sepH] * (nrows-1)) TypeError: can't multiply sequence by non-int of type 'float' 

Suponiendo que se conozca al menos una dimensión de su cuadrícula y el número total de plots, usaría el módulo gridspec y un poco de matemáticas.

 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() 

introduzca la descripción de la imagen aquí

Aquí está la solución con la que terminé. Le permite hacer referencia a las subplots por nombre y agrega una nueva subplot si ese nombre aún no se ha utilizado, reposicionando todas las subplots anteriores en el proceso.

Uso:

 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 

El código:

 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. eg 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 '')) 
 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() 

Estaba haciendo algún trabajo de PyQt5, pero el método de agregar muestra cómo agregar dinámicamente una nueva ttwig secundaria. El método set_position de Axes se usa para cambiar la posición anterior a una nueva posición. A continuación, agrega una nueva subplot con la nueva posición.