Python 如何使matplotlib中的tight_layout()使用插入图?
我正在使用matplotlib.pyplot生成一个包含多个子图的图。这是我最终想要的:一个2x2的“主要”绘图阵列。每个曲线在绘图中都有两条曲线,每条曲线使用不同的y轴。此外,我希望在每个图中都有一个较小的插图 到目前为止,我已经获得了第一部分,使用以下工作示例代码:Python 如何使matplotlib中的tight_layout()使用插入图?,python,matplotlib,Python,Matplotlib,我正在使用matplotlib.pyplot生成一个包含多个子图的图。这是我最终想要的:一个2x2的“主要”绘图阵列。每个曲线在绘图中都有两条曲线,每条曲线使用不同的y轴。此外,我希望在每个图中都有一个较小的插图 到目前为止,我已经获得了第一部分,使用以下工作示例代码: import matplotlib.pyplot as plt import numpy as np import os import shutil import time import sys #Simplest worki
import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
import time
import sys
#Simplest working example of tight_layout and plots problem
def two_scales(ax1, time, data1, data2, c1, c2, xlabel, y1label, y2label):
ax2 = ax1.twinx()
ax1.plot(time, data1, color=c1)
ax1.set_xlabel(xlabel)
ax1.set_ylabel(y1label)
ax2.plot(time, data2, color=c2)
ax2.set_ylabel(y2label)
return ax1, ax2
# Change color of each axis
def color_y_axis(ax, color):
"""Color your axes."""
for t in ax.get_yticklabels():
t.set_color(color)
return None
def insetPlots():
t = np.arange(0.01, 10.0, 0.01)
#Figure stuff
fig, baseAxes = plt.subplots(2,2,figsize=(10, 6))
baseAxesFlattened = baseAxes.flatten()
for i, dat in enumerate(baseAxesFlattened):
s1 = np.exp((i+1)*t)
s2 = .3*np.sin((i+1)*.2 * np.pi * t)
#Plotting them together
tempAx1, tempAx2 = two_scales(baseAxesFlattened[i], t, s1, s2, 'b', 'r','heyheyhey','yayaya','woopwoop')
#Changing the color of the axes
color_y_axis(tempAx1, 'b')
color_y_axis(tempAx2, 'r')
plt.tight_layout()
#plt.figure(figsize=(6, 8))
picname="/mypath/testtesttest.png"
plt.savefig(picname)
insetPlots()
到目前为止,这一切都很好:
现在我想添加插图。我很容易做到这一点:
import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
import time
import sys
#Simplest working example of tight_layout and plots problem
def two_scales(ax1, time, data1, data2, c1, c2, xlabel, y1label, y2label):
ax2 = ax1.twinx()
ax1.plot(time, data1, color=c1)
ax1.set_xlabel(xlabel)
ax1.set_ylabel(y1label)
ax2.plot(time, data2, color=c2)
ax2.set_ylabel(y2label)
return ax1, ax2
# Change color of each axis
def color_y_axis(ax, color):
"""Color your axes."""
for t in ax.get_yticklabels():
t.set_color(color)
return None
def insetPlots():
t = np.arange(0.01, 10.0, 0.01)
#Figure stuff
fig, baseAxes = plt.subplots(2,2,figsize=(10, 6))
baseAxesFlattened = baseAxes.flatten()
for i, dat in enumerate(baseAxesFlattened):
s1 = np.exp((i+1)*t)
s2 = .3*np.sin((i+1)*.2 * np.pi * t)
#Plotting them together
tempAx1, tempAx2 = two_scales(baseAxesFlattened[i], t, s1, s2, 'b', 'r','heyheyhey','yayaya','woopwoop')
#Changing the color of the axes
color_y_axis(tempAx1, 'b')
color_y_axis(tempAx2, 'r')
pos = tempAx1.get_position()
#print(pos)
posString = str(pos)
x0Ind, y0Ind, x1Ind, y1Ind = posString.find('x0'),posString.find('y0'),posString.find('x1'),posString.find('y1')
#print(x0Ind, y0Ind, x1Ind, y1Ind)
x0, y0, x1, y1 = float(posString[x0Ind+3:y0Ind-2]), float(posString[y0Ind+3:x1Ind-2]), float(posString[x1Ind+3:y1Ind-2]), float(posString[y1Ind+3:-1])
#print(x0, y0, x1, y1)
mainPlotW = x1 - x0
mainPlotH = y1 - y0
w, h = 0.3*mainPlotW, 0.25*mainPlotH
left, bottom, width, height = [x0 + .15*mainPlotW, y0 + .7*mainPlotH, w, h]
insetAx = fig.add_axes([left, bottom, width, height])
#insetAx.plot(range(6)[::-1], color='green')
s3 = np.sin(.2 * np.pi * t/(i+1))
insetAx.plot(t,s3, color='green')
#plt.tight_layout()
#plt.figure(figsize=(6, 8))
picname="/mypath/testtesttest.png"
plt.savefig(picname)
insetPlots()
注意,这里我已经注释掉了tight_layout()。这会产生这样的结果,在我想要的位置上有插入图:
因此,插入图的位置是正确的,但由于tight_layout()已消失,主图的轴标签重叠。如果我有tight_layout()(与上面的代码完全相同,但没有注释该行),我会得到以下结果:
主图的轴不再重叠,但插图现在位于错误的位置。我在运行代码时也会收到以下警告:
/usr/local/lib/python3.6/dist-packages/matplotlib/figure.py:2022: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
warnings.warn("This figure includes Axes that are not compatible "
我怎样才能使它们都起作用?我怀疑我做了一些简单的错误,比如以错误的方式放置插图
编辑:我已经找到了一个解决方案,但它很难看,我希望不是“正确”的方法。我怀疑tight_layout()正在移动东西,因此相对于tight_layout()之后的主图,插入图的位置(取决于主图的位置)变得混乱。因此,我通过绘制主图,进行紧凑布局,然后添加插入图来解决问题:
import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
import time
import sys
#Simplest working example of tight_layout and plots problem
def two_scales(ax1, time, data1, data2, c1, c2, xlabel, y1label, y2label):
ax2 = ax1.twinx()
ax1.plot(time, data1, color=c1)
ax1.set_xlabel(xlabel)
ax1.set_ylabel(y1label)
ax2.plot(time, data2, color=c2)
ax2.set_ylabel(y2label)
return ax1, ax2
# Change color of each axis
def color_y_axis(ax, color):
"""Color your axes."""
for t in ax.get_yticklabels():
t.set_color(color)
return None
def insetPlots():
t = np.arange(0.01, 10.0, 0.01)
#Figure stuff
fig, baseAxes = plt.subplots(2,2,figsize=(10, 6))
baseAxesFlattened = baseAxes.flatten()
majorAxes = []
for i, dat in enumerate(baseAxesFlattened):
s1 = np.exp((i+1)*t)
s2 = .3*np.sin((i+1)*.2 * np.pi * t)
#Plotting them together
tempAx1, tempAx2 = two_scales(baseAxesFlattened[i], t, s1, s2, 'b', 'r','heyheyhey','yayaya','woopwoop')
majorAxes.append(tempAx1)
#Changing the color of the axes
color_y_axis(tempAx1, 'b')
color_y_axis(tempAx2, 'r')
plt.tight_layout()
for i, dat in enumerate(baseAxesFlattened):
tempAx1 = majorAxes[i]
pos = tempAx1.get_position()
#print(pos)
posString = str(pos)
x0Ind, y0Ind, x1Ind, y1Ind = posString.find('x0'),posString.find('y0'),posString.find('x1'),posString.find('y1')
#print(x0Ind, y0Ind, x1Ind, y1Ind)
x0, y0, x1, y1 = float(posString[x0Ind+3:y0Ind-2]), float(posString[y0Ind+3:x1Ind-2]), float(posString[x1Ind+3:y1Ind-2]), float(posString[y1Ind+3:-1])
#print(x0, y0, x1, y1)
mainPlotW = x1 - x0
mainPlotH = y1 - y0
w, h = 0.3*mainPlotW, 0.25*mainPlotH
left, bottom, width, height = [x0 + .15*mainPlotW, y0 + .7*mainPlotH, w, h]
insetAx = fig.add_axes([left, bottom, width, height])
#insetAx.plot(range(6)[::-1], color='green')
s3 = np.sin(.2 * np.pi * t/(i+1))
insetAx.plot(t,s3, color='green')
#plt.tight_layout()
#plt.figure(figsize=(6, 8))
picname="/mypath/testtesttest.png"
plt.savefig(picname)
insetPlots()
有更干净的方法吗?紧密布局()
对于大多数常见的绘图来说只是一个有用的工具,但它不能处理所有情况
在您的特定情况下,我认为最好在创建插入轴之前调用tight_layout()
,并使用生成的轴位置为插入找到正确的坐标
import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
import time
import sys
#Simplest working example of tight_layout and plots problem
def two_scales(ax1, time, data1, data2, c1, c2, xlabel, y1label, y2label):
ax2 = ax1.twinx()
ax1.plot(time, data1, color=c1)
ax1.set_xlabel(xlabel)
ax1.set_ylabel(y1label)
ax2.plot(time, data2, color=c2)
ax2.set_ylabel(y2label)
return ax1, ax2
# Change color of each axis
def color_y_axis(ax, color):
"""Color your axes."""
for t in ax.get_yticklabels():
t.set_color(color)
return None
def insetPlots():
t = np.arange(0.01, 10.0, 0.01)
#Figure stuff
fig, baseAxes = plt.subplots(2,2,figsize=(10, 6))
baseAxesFlattened = baseAxes.flatten()
for i, ax in enumerate(baseAxesFlattened):
s1 = np.exp((i+1)*t)
s2 = .3*np.sin((i+1)*.2 * np.pi * t)
#Plotting them together
tempAx1, tempAx2 = two_scales(ax, t, s1, s2, 'b', 'r','heyheyhey','yayaya','woopwoop')
#Changing the color of the axes
color_y_axis(tempAx1, 'b')
color_y_axis(tempAx2, 'r')
fig.tight_layout()
for i, ax in enumerate(baseAxesFlattened):
pos = ax.get_position()
#print(pos)
mainPlotW = pos.x1 - pos.x0
mainPlotH = pos.y1 - pos.y0
w, h = 0.3*mainPlotW, 0.25*mainPlotH
left, bottom, width, height = [pos.x0 + .15*mainPlotW, pos.y0 + .7*mainPlotH, w, h]
insetAx = fig.add_axes([left, bottom, width, height])
insetAx.plot(range(6)[::-1], color='green')
s3 = np.sin(.2 * np.pi * t/(i+1))
insetAx.plot(t,s3, color='green')
insetPlots()
PS您正在使用pos
变量执行一些非常奇怪的操作,将其转换为str
,然后再将其转换回float
。我已经在代码的第二个循环中简化了您的代码我建议使用来定位插图。这大大简化了事情,不需要将绘图大小与任何内容相乘
然后,您可以选择在创建插图之前或之后调用fig.tight_layout()
,生成的绘图将不会更改(尽管在创建插图之后调用它会发出警告,在这种情况下您可以忽略该警告)
看来你和我得出了相同的结论。我在发布时没有看到您的编辑。无论如何,我认为这是处理你的特殊情况的“正确”方法。谢谢你的答复。关于pos变量,我这样做是因为get_position返回了一个“bbox”对象,我不知道我可以从中获取x1/etc成员,所以我做了一件非常简单的事情,解析字符串以获得数字。。。但这似乎更明智。
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
import numpy as np
def two_scales(ax1, time, data1, data2, c1, c2, xlabel, y1label, y2label):
ax2 = ax1.twinx()
ax1.plot(time, data1, color=c1)
ax1.set_xlabel(xlabel)
ax1.set_ylabel(y1label)
ax2.plot(time, data2, color=c2)
ax2.set_ylabel(y2label)
return ax1, ax2
# Change color of each axis
def color_y_axis(ax, color):
"""Color your axes."""
for t in ax.get_yticklabels():
t.set_color(color)
return None
def insetPlots():
t = np.arange(0.01, 10.0, 0.01)
#Figure stuff
fig, baseAxes = plt.subplots(2,2,figsize=(10, 6))
baseAxesFlattened = baseAxes.flatten()
for i, ax in enumerate(baseAxesFlattened):
s1 = np.exp((i+1)*t)
s2 = .3*np.sin((i+1)*.2 * np.pi * t)
#Plotting them together
tempAx1, tempAx2 = two_scales(ax, t, s1, s2, 'b', 'r',
'heyheyhey','yayaya','woopwoop')
#Changing the color of the axes
color_y_axis(tempAx1, 'b')
color_y_axis(tempAx2, 'r')
fig.tight_layout()
for i, ax in enumerate(baseAxesFlattened):
insetAx = fig.add_axes([0, 0, 1, 1], label="{}".format(i))
ip = InsetPosition(ax, [.15, 0.7, 0.3, 0.25]) #posx, posy, width, height
insetAx.set_axes_locator(ip)
insetAx.plot(range(6)[::-1], color='green')
s3 = np.sin(.2 * np.pi * t/(i+1))
insetAx.plot(t,s3, color='green')
# putting tight_layout here will produce a warning,
# yet the resulting plot is the same
# fig.tight_layout()
insetPlots()
plt.show()