[英]How can I get tight_layout() in matplotlib to work with inset plots?
I'm using matplotlib.pyplot to make a plot with several subplots. 我正在使用matplotlib.pyplot绘制带有多个子图的图。 Here's what I ultimately want: a 2x2 array of "major" plots. 这是我最终想要的:2x2的“主要”绘图阵列。 Each of these have two curves in the plot, each using a different y axis. 每条曲线在图中都有两条曲线,每条曲线使用不同的y轴。 In addition, I want a smaller inset plot in each of these. 另外,我希望在每一个图中都有一个较小的插图。
I've got the first part so far, using this working example code: 到目前为止,我已经使用了下面的示例代码:
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()
Which produces this, good so far: 到目前为止产生的结果:
Now I want to add the insets. 现在,我要添加插图。 I can do this too fairly easily: 我可以很容易地做到这一点:
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()
Note that here I've commented out tight_layout(). 请注意,这里我已注释掉ight_layout()。 This produces this, which has the inset plots in the positions I want them: 这产生了这个,在我想要的位置上有插图:
So this has the inset plots in the right positions, but because tight_layout() is gone, the axes labels for the major plots are overlapping. 这样就可以将插入图放置在正确的位置,但是因为tight_layout()已经消失,所以主要图的轴标签是重叠的。 If I have tight_layout() (so, same exact code as directly above, but with that line uncommented), I get this: 如果我有tight_layout()(因此,与上面直接相同的代码,但未注释该行),则得到以下信息:
Where the major plots' axes aren't overlapping anymore, but the insets are now in the wrong positions. 主图的轴不再重叠的地方,但是插图现在处于错误的位置。 I also get this warning when I run the code: 运行代码时,我也会收到此警告:
/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 "
How can I make them both work? 如何使它们都起作用? I suspect I'm doing something simple wrong, like placing insets in the wrong way. 我怀疑我在做一些简单的错误,例如以错误的方式放置插图。
EDIT: I've found a solution, but it's ugly and I hope not the "proper" way to do it. 编辑:我找到了一个解决方案,但是它很丑陋,我希望不要以“适当”的方式来解决。 I suspected that tight_layout() is moving things around, so the positions of the inset plots (which depend on the positions of the major plots) were getting messed up relative to the major plots after tight_layout(). 我怀疑tight_layout()正在四处移动,因此插图集的位置(取决于主要图的位置)相对于tight_layout()之后的主要图变得混乱。 So I solved the problem by plotting the major plots, doing tight layout, and then adding the inset ones: 因此,我通过绘制主要图,进行紧密布局并添加插图来解决了这个问题:
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()
Is there a cleaner way to do this? 有没有更清洁的方法可以做到这一点?
tight_layout()
is just a useful tool for most common plots, but it cannot deal with every situation. tight_layout()
只是大多数常见图的有用工具,但不能处理所有情况。
In your particular case, I think you are better off calling tight_layout()
before creating your inset axes, and using the resulting axes position to find the correct coordinates for your insets 在您的特定情况下,我认为您最好在创建插入轴之前先调用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 You're doing some pretty weird things with the pos
variable, transforming it to str
before casting it back to float
. PS您正在使用pos
变量做一些非常奇怪的事情,将其转换为str
然后再将其强制返回float
。 I've simplified your code in the second loop in my code 我在代码的第二个循环中简化了您的代码
I would suggest using mpl_toolkits.axes_grid1.inset_locator.InsetPosition
to position the inset. 我建议使用mpl_toolkits.axes_grid1.inset_locator.InsetPosition
定位插图。 This simplifies things a lot, not needing to multiply plot sizes with anything. 这大大简化了事情,不需要将绘图大小乘以任何东西。
You may then choose to call fig.tight_layout()
before or after creating the insets, the resulting plot will not change (though calling it after gives a warning, which you can ignore in this case). 然后,您可以选择在创建插图之前或之后调用fig.tight_layout()
,结果图不会改变(尽管在发出警告后调用它,在这种情况下可以忽略)。
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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.