如何在多个子图中绘图
Posted
技术标签:
【中文标题】如何在多个子图中绘图【英文标题】:How to plot in multiple subplots 【发布时间】:2022-01-21 13:04:45 【问题描述】:我对这段代码的工作原理有点困惑:
fig, axes = plt.subplots(nrows=2, ncols=2)
plt.show()
在这种情况下,无花果轴是如何工作的?它有什么作用?
还有为什么这不能做同样的事情:
fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)
【问题讨论】:
【参考方案1】:有几种方法可以做到这一点。 subplots
方法创建图形以及随后存储在 ax
数组中的子图。例如:
import matplotlib.pyplot as plt
x = range(10)
y = range(10)
fig, ax = plt.subplots(nrows=2, ncols=2)
for row in ax:
for col in row:
col.plot(x, y)
plt.show()
然而,这样的事情也可以,但它不是那么“干净”,因为你正在创建一个带有子图的图形,然后在它们之上添加:
fig = plt.figure()
plt.subplot(2, 2, 1)
plt.plot(x, y)
plt.subplot(2, 2, 2)
plt.plot(x, y)
plt.subplot(2, 2, 3)
plt.plot(x, y)
plt.subplot(2, 2, 4)
plt.plot(x, y)
plt.show()
【讨论】:
【参考方案2】:import matplotlib.pyplot as plt
fig, ax = plt.subplots(2, 2)
ax[0, 0].plot(range(10), 'r') #row=0, col=0
ax[1, 0].plot(range(10), 'b') #row=1, col=0
ax[0, 1].plot(range(10), 'g') #row=0, col=1
ax[1, 1].plot(range(10), 'k') #row=1, col=1
plt.show()
【讨论】:
我知道ax
是什么,但不知道fig
是什么。它们是什么?
ax 实际上是一个 numpy 数组。 fig 是matplotlib.figure.Figure
类,通过它您可以对绘制的图形进行大量操作。例如,您可以将颜色条添加到特定的子图,您可以更改所有子图后面的背景颜色。您可以修改这些子图的布局或为其添加新的小斧头。最好你可能想要一个可以通过fig.suptitle(title)
方法获得的所有子图的主标题。最后,一旦您对情节感到满意,您可以使用fig.savefig
方法保存它。 @Leevo【参考方案3】:
你也可以在 subplots 调用中解压坐标轴
并设置是否要在子图之间共享x轴和y轴
像这样:
import matplotlib.pyplot as plt
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
ax1.plot(range(10), 'r')
ax2.plot(range(10), 'b')
ax3.plot(range(10), 'g')
ax4.plot(range(10), 'k')
plt.show()
【讨论】:
【参考方案4】:您可能对从 matplotlib 版本 2.1 开始该问题的第二个代码也可以正常工作这一事实感兴趣。
来自change log:
Figure 类现在有 subplots 方法 Figure 类现在有一个 subplots() 方法,其行为与 pyplot.subplots() 相同,但在现有图形上。
例子:
import matplotlib.pyplot as plt
fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)
plt.show()
【讨论】:
【参考方案5】:阅读文档:matplotlib.pyplot.subplots
pyplot.subplots()
返回一个元组 fig, ax
,它使用符号解包在两个变量中
fig, axes = plt.subplots(nrows=2, ncols=2)
代码:
fig = plt.figure()
axes = fig.subplots(nrows=2, ncols=2)
不起作用,因为subplots()
是pyplot
中的一个函数,而不是Figure
对象的成员。
【讨论】:
【参考方案6】:依次遍历所有子图:
fig, axes = plt.subplots(nrows, ncols)
for ax in axes.flatten():
ax.plot(x,y)
访问特定索引:
for row in range(nrows):
for col in range(ncols):
axes[row,col].plot(x[row], y[col])
【讨论】:
【参考方案7】:带有熊猫的子图
此答案适用于带有pandas
的子图,它使用matplotlib
作为默认绘图后端。
这里有四个选项来创建以pandas.DataFrame
开头的子图
实施 1. 和 2. 用于宽格式数据,为每列创建子图。
实现 3. 和 4. 适用于长格式数据,为列中的每个唯一值创建子图。
在python 3.8.11
、pandas 1.3.2
、matplotlib 3.4.3
、seaborn 0.11.2
中测试
进口和数据
import seaborn as sns # data only
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# wide dataframe
df = sns.load_dataset('planets').iloc[:, 2:5]
orbital_period mass distance
0 269.300 7.10 77.40
1 874.774 2.21 56.95
2 763.000 2.60 19.84
3 326.030 19.40 110.62
4 516.220 10.50 119.47
# long dataframe
dfm = sns.load_dataset('planets').iloc[:, 2:5].melt()
variable value
0 orbital_period 269.300
1 orbital_period 874.774
2 orbital_period 763.000
3 orbital_period 326.030
4 orbital_period 516.220
1。 subplots=True
和 layout
,对于每一列
在pandas.DataFrame.plot
中使用参数subplots=True
和layout=(rows, cols)
此示例使用kind='density'
,但kind
有不同的选项,这适用于所有选项。如果不指定kind
,则默认使用折线图。
ax
是由 pandas.DataFrame.plot
返回的 AxesSubplot
数组
如果需要,请参阅How to get a Figure
object。
How to save pandas subplots
axes = df.plot(kind='density', subplots=True, layout=(2, 2), sharex=False, figsize=(10, 6))
# extract the figure object; only used for tight_layout in this example
fig = axes[0][0].get_figure()
# set the individual titles
for ax, title in zip(axes.ravel(), df.columns):
ax.set_title(title)
fig.tight_layout()
plt.show()
2。 plt.subplots
,对于每一列
使用matplotlib.pyplot.subplots
创建一个Axes
数组,然后将axes[i, j]
或axes[n]
传递给ax
参数。
此选项使用pandas.DataFrame.plot
,但可以使用其他axes
级别绘图调用作为替代(例如sns.kdeplot
、plt.plot
等)
使用.ravel
或.flatten
将Axes
的子图数组折叠成一维是最简单的。见.ravel
vs .flatten
。
适用于每个axes
的任何需要迭代的变量都与.zip
组合在一起(例如cols
、axes
、colors
、palette
等)。每个对象的长度必须相同。
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 6)) # define the figure and subplots
axes = axes.ravel() # array to 1D
cols = df.columns # create a list of dataframe columns to use
colors = ['tab:blue', 'tab:orange', 'tab:green'] # list of colors for each subplot, otherwise all subplots will be one color
for col, color, ax in zip(cols, colors, axes):
df[col].plot(kind='density', ax=ax, color=color, label=col, title=col)
ax.legend()
fig.delaxes(axes[3]) # delete the empty subplot
fig.tight_layout()
plt.show()
1. 和 2. 的结果。
3。 plt.subplots
,对于.groupby
中的每个组
这类似于 2.,不同之处在于它将 color
和 axes
压缩到 .groupby
对象。
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 6)) # define the figure and subplots
axes = axes.ravel() # array to 1D
dfg = dfm.groupby('variable') # get data for each unique value in the first column
colors = ['tab:blue', 'tab:orange', 'tab:green'] # list of colors for each subplot, otherwise all subplots will be one color
for (group, data), color, ax in zip(dfg, colors, axes):
data.plot(kind='density', ax=ax, color=color, title=group, legend=False)
fig.delaxes(axes[3]) # delete the empty subplot
fig.tight_layout()
plt.show()
4。 seaborn
人物级剧情
使用seaborn
图形级图,并使用col
或row
参数。 seaborn
是 matplotlib
的高级 API。见seaborn: API reference
p = sns.displot(data=dfm, kind='kde', col='variable', col_wrap=2, x='value', hue='variable',
facet_kws='sharey': False, 'sharex': False, height=3.5, aspect=1.75)
sns.move_legend(p, "upper left", bbox_to_anchor=(.55, .45))
【讨论】:
【参考方案8】:其他答案很好,这个答案是一个可能有用的组合。
import numpy as np
import matplotlib.pyplot as plt
# Optional: define x for all the sub-plots
x = np.linspace(0,2*np.pi,100)
# (1) Prepare the figure infrastructure
fig, ax_array = plt.subplots(nrows=2, ncols=2)
# flatten the array of axes, which makes them easier to iterate through and assign
ax_array = ax_array.flatten()
# (2) Plot loop
for i, ax in enumerate(ax_array):
ax.plot(x , np.sin(x + np.pi/2*i))
#ax.set_title(f'plot i')
# Optional: main title
plt.suptitle('Plots')
总结
-
准备图基础设施
获取 ax_array,一个子图数组
展平数组以便在一个“for 循环”中使用它
绘图循环
循环平展的 ax_array 以更新子图
可选:使用枚举来跟踪子图号
一旦展平,每个
ax_array
都可以从0
到nrows x ncols -1
单独索引(例如ax_array[0]
、ax_array[1]
、ax_array[2]
、ax_array[3]
)。
【讨论】:
【参考方案9】:这是一个简单的解决方案
fig, ax = plt.subplots(nrows=2, ncols=3, sharex=True, sharey=False)
for sp in fig.axes:
sp.plot(range(10))
【讨论】:
【参考方案10】:将axes
数组转换为一维
使用plt.subplots(nrows, ncols)
生成子图,其中both nrows 和ncols 均大于1,返回<AxesSubplot:>
对象的嵌套数组。
在nrows=1
或ncols=1
的情况下没有必要将axes
展平,因为axes
已经是一维的,这是默认参数squeeze=True
的结果
访问对象的最简单方法是使用.ravel()
、.flatten()
或.flat
将数组转换为一维。
.ravel
vs. .flatten
flatten
总是返回一个副本。
ravel
尽可能返回原始数组的视图。
axes
的数组转换为一维数组后,有多种绘图方法。
import matplotlib.pyplot as plt
import numpy as np # sample data only
# example of data
rads = np.arange(0, 2*np.pi, 0.01)
y_data = np.array([np.sin(t*rads) for t in range(1, 5)])
x_data = [rads, rads, rads, rads]
# Generate figure and its subplots
fig, axes = plt.subplots(nrows=2, ncols=2)
# axes before
array([[<AxesSubplot:>, <AxesSubplot:>],
[<AxesSubplot:>, <AxesSubplot:>]], dtype=object)
# convert the array to 1 dimension
axes = axes.ravel()
# axes after
array([<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>],
dtype=object)
-
遍历扁平数组
如果子图比数据多,这将导致
IndexError: list index out of range
改用选项 3,或选择轴的子集(例如 axes[:-2]
)
for i, ax in enumerate(axes):
ax.plot(x_data[i], y_data[i])
-
按索引访问每个轴
axes[0].plot(x_data[0], y_data[0])
axes[1].plot(x_data[1], y_data[1])
axes[2].plot(x_data[2], y_data[2])
axes[3].plot(x_data[3], y_data[3])
-
索引数据和坐标轴
for i in range(len(x_data)):
axes[i].plot(x_data[i], y_data[i])
zip
将坐标轴和数据放在一起,然后遍历元组列表
for ax, x, y in zip(axes, x_data, y_data):
ax.plot(x, y)
输出
【讨论】:
【参考方案11】:如果您真的想使用循环,请执行以下操作:
def plot(data):
fig = plt.figure(figsize=(100, 100))
for idx, k in enumerate(data.keys(), 1):
x, y = data[k].keys(), data[k].values
plt.subplot(63, 10, idx)
plt.bar(x, y)
plt.show()
【讨论】:
以上是关于如何在多个子图中绘图的主要内容,如果未能解决你的问题,请参考以下文章