我想使用多个轴创建一个方形图,make_axes_locateable如matplotlib文档中所示.然而,虽然这适用于x和y数据具有相同范围的图,但是当范围是不同的数量级时,它不起作用.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
x = np.random.normal(512, 112, 240)
y = np.random.normal(0.5, 0.1, 240)
_, ax = plt.subplots()
divider = make_axes_locatable(ax)
xhax = divider.append_axes("top", size=1, pad=0.1, sharex=ax)
yhax = divider.append_axes("right", size=1, pad=0.1, sharey=ax)
ax.scatter(x, y)
xhax.hist(x)
yhax.hist(y, orientation="horizontal")
x0,x1 = ax.get_xlim()
y0,y1 = ax.get_ylim()
ax.set_aspect(abs(x1-x0)/abs(y1-y0))
plt.show()
Run Code Online (Sandbox Code Playgroud)
虽然此代码使用的set_aspect答案如我如何制作matplotlib散点图方块?轴未正确修改,如下所示:
我尝试修复此问题:
ax.set_aspect(abs(x1-x0)/abs(y1-y0), share=True)
Run Code Online (Sandbox Code Playgroud)
但这导致了以下结果:
在调用scatter之后和创建两个直方图轴之前设置方面似乎没有任何效果,即使它看起来是在文档示例中完成的.当数据范围相同时,此代码可以正常工作:
更新:此问题的一个主要限制是使用make_axes_locateable而不是GridSpec在下面的评论中讨论.我正在处理的问题涉及创建绘图函数,接受Axes对象进行处理和修改,而不需要了解图中的图形或任何其他轴,如下面的代码所示:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as grid
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
def joint_plot(x, y, ax=None):
"""
Create a square joint plot of x and y.
"""
if ax is None:
ax = plt.gca()
divider = make_axes_locatable(ax)
xhax = divider.append_axes("top", size=1, pad=0.1, sharex=ax)
yhax = divider.append_axes("right", size=1, pad=0.1, sharey=ax)
ax.scatter(x, y)
xhax.hist(x)
yhax.hist(y, orientation="horizontal")
x0,x1 = ax.get_xlim()
y0,y1 = ax.get_ylim()
ax.set_aspect(abs(x1-x0)/abs(y1-y0))
plt.sca(ax)
return ax, xhax, yhax
def color_plot(x, y, colors, ax=None):
if ax is None:
ax = plt.gca()
divider = make_axes_locatable(ax)
cbax = divider.append_axes("right", size="5%", pad=0.1)
sc = ax.scatter(x, y, marker='o', c=colors, cmap='RdBu')
plt.colorbar(sc, cax=cbax)
ax.set_aspect("equal")
plt.sca(ax)
return ax, cbax
if __name__ == "__main__":
_, axes = plt.subplots(nrows=2, ncols=2, figsize=(9,6))
# Plot 1
x = np.random.normal(100, 17, 120)
y = np.random.normal(0.5, 0.1, 120)
joint_plot(x, y, axes[0,0])
# Plot 2
x = np.random.normal(100, 17, 120)
y = np.random.normal(100, 17, 120)
c = np.random.normal(100, 17, 120)
color_plot(x, y, c, axes[0,1])
# Plot 3
x = np.random.normal(100, 17, 120)
y = np.random.normal(0.5, 0.1, 120)
c = np.random.uniform(0.0, 1.0, 120)
color_plot(x, y, c, axes[1,0])
# Plot 4
x = np.random.normal(0.5, 0.1, 120)
y = np.random.normal(0.5, 0.1, 120)
joint_plot(x, y, axes[1,1])
plt.tight_layout()
plt.show()
Run Code Online (Sandbox Code Playgroud)
由于Axes-only约束,这个问题扩展了诸如在绘图中使用colorbar和python相互作用在轴('square')和set_xlim之间设置相等方面的问题.
处理该问题的一种方法是保持 x 轴和 y 轴的数据限制相等。这可以通过将值标准化为 0 和 1 之间来完成。这样命令就可以ax.set_aspect('equal')按预期工作。当然,如果只这样做,刻度标签的范围只会从 0 到 1,因此必须应用一点 matplotlib 魔法来将刻度标签调整到原始数据范围。这里的答案展示了如何使用FuncFormatter. 然而,由于原始刻度是根据间隔 [0,1] 选择的,FuncFormatter单独使用 a 将导致奇数刻度,例如,如果因子为 635,则 0.2 的原始刻度将变为 127。要获得“好”刻度,请使用 1还可以使用 a AutoLocator,它可以使用该函数计算原始数据范围的刻度tick_values()。然后,这些刻度可以再次缩放到区间 [0,1],然后FuncFormatter可以计算刻度标签。虽然有点复杂,但最终只需要大约 10 行额外代码:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from mpl_toolkits.axes_grid1 import make_axes_locatable
x = np.random.normal(512, 112, 240)
y = np.random.normal(0.5, 0.1, 240)
fig,ax=plt.subplots()
divider = make_axes_locatable(ax)
##increased pad from 0.1 to 0.2 so that tick labels don't overlap
xhax = divider.append_axes("top", size=1, pad=0.2, sharex=ax)
yhax = divider.append_axes("right", size=1, pad=0.2, sharey=ax)
##'normalizing' x and y values to be between 0 and 1:
xn = (x-min(x))/(max(x)-min(x))
yn = (y-min(y))/(max(y)-min(y))
##producinc the plots
ax.scatter(xn, yn)
xhax.hist(xn)
yhax.hist(yn, orientation="horizontal")
##turning off duplicate ticks (if needed):
plt.setp(xhax.get_xticklabels(), visible=False)
plt.setp(yhax.get_yticklabels(), visible=False)
ax.set_aspect('equal')
##setting up ticks and labels to simulate real data:
locator = mticker.AutoLocator()
xticks = (locator.tick_values(min(x),max(x))-min(x))/(max(x)-min(x))
ax.set_xticks(xticks)
ax.xaxis.set_major_formatter(mticker.FuncFormatter(
lambda t, pos: '{0:g}'.format(t*(max(x)-min(x))+min(x))
))
yticks = (locator.tick_values(min(y),max(y))-min(y))/(max(y)-min(y))
ax.set_yticks(yticks)
ax.yaxis.set_major_formatter(mticker.FuncFormatter(
lambda t, pos: '{0:g}'.format(t*(max(y)-min(y))+min(y))
))
fig.tight_layout()
plt.show()
Run Code Online (Sandbox Code Playgroud)
生成的图片看起来符合预期,并且在调整图像大小时也保持正方形。
旧答案:
这更像是一种解决方法,而不是解决方案:
您可以不使用 ,而是通过提供来ax.set_aspect()设置图形,使其成为正方形,其中宽度和高度以英寸为单位。由于 的高度和宽度都是 1 英寸,这意味着它也变成了正方形。figsize=(n,n)plt.subplotsnxhaxyhaxax
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
x = np.random.normal(512, 112, 240)
y = np.random.normal(0.5, 0.1, 240)
fig, ax = plt.subplots(figsize=(5,5))
divider = make_axes_locatable(ax)
xhax = divider.append_axes("top", size=1, pad=0.1, sharex=ax)
yhax = divider.append_axes("right", size=1, pad=0.1, sharey=ax)
ax.scatter(x, y)
xhax.hist(x)
yhax.hist(y, orientation="horizontal")
##turning off duplicate ticks:
plt.setp(xhax.get_xticklabels(), visible=False)
plt.setp(yhax.get_yticklabels(), visible=False)
plt.show()
Run Code Online (Sandbox Code Playgroud)
结果如下:
当然,一旦你调整了身材的大小,方形的外观就会消失。但是,如果您已经知道图形的最终尺寸并且只想保存它以供进一步使用,那么这应该是一个足够好的快速解决方案。