如果我使用多个列来显示子图,如何动态地将新图添加到一组子图中? This为一列回答了这个问题,但我似乎无法修改那里的答案,使其动态添加到包含x
列的子图
我修改了Sadarthrion's answer并尝试了以下内容。在这里,为了举例,我制作了number_of_subplots=11
和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)
正如你所看到的,这并没有给我我想要的东西。第一个图占据了所有列,其他图比它们应该小
修改
('2.7.6 | 64-bit | (default, Sep 15 2014, 17:36:35) [MSC v.1500 64 bit (AMD64)]'
我也有matplotlib版本1.4.3:
import matplotlib as mpl
print mpl.__version__
1.4.3
我在下面尝试了Paul的回答,并收到以下错误消息:
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)
<ipython-input-1-f74203b1c1bf> in <module>()
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'
答案 0 :(得分:11)
假设您的网格至少有一个维度和总图数已知,我会使用gridspec
模块和一些数学。
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()
答案 1 :(得分:0)
这是我最终解决的问题。它允许您按名称引用子图,如果尚未使用该名称,则添加新的子图,重新定位过程中的所有先前子图。
用法:
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
代码:
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 ''))
答案 2 :(得分:0)
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()
正在做一些PyQt5工作,但add方法显示了如何动态添加新的子图。 Axes的set_position方法用于将旧位置更改为新位置。然后使用新位置添加新的子图。