我正在尝试制作一个简单的子图,其中一个子图具有树状图,另一个子图具有热图,同时保持方轴。我尝试以下方法:

from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
from scipy.spatial.distance import pdist
fig = plt.figure(figsize=(7,7))
plt.subplot(2, 1, 1)
cm = matplotlib.cm.Blues
X = np.random.random([5,5])
pmat = pdist(X,"euclidean")
linkmat = linkage(pmat)
dendrogram(linkmat)
plt.subplot(2, 1, 2)
labels = ["a","b","c","d","e","f"]
Y = np.random.random([6,6])
plt.xticks(arange(0.5, 7.5, 1))
plt.gca().set_xticklabels(labels)
plt.pcolor(Y)
plt.colorbar()

这将产生以下结果:


但是问题在于轴不是正方形,并且色带被认为是第二个子图的一部分。我希望它悬挂在图外,并使其树状图框和热图框都为正方形并彼此对齐(即相同大小)。

正如文档建议的那样,我尝试在调用subplot时使用aspect='equal'获取方轴,但这破坏了绘图,给出了...


如果我尝试在每个子图后使用plt.axis('equal')而不是aspect='equal',它会奇怪地使热图平方而不是其边界框(请参见下文),同时完全破坏树状图并弄乱xtick标签的对齐方式... 。-引起混乱:


如何解决?总而言之,我正在尝试绘制一个非常简单的图形:顶部子图中的正方形树状图,底部子图中的正方形热图,右边的颜色条。没有什么花哨。

最后,还有一个更笼统的问题:是否有一条通用的规则/原理可以迫使matplotlib始终使轴保持正方形?我想不出一种情况,我不想要方轴,但通常不是默认行为。如果可能的话,我想强制所有地块都为正方形。

Aspect =" equal"表示数据空间中的相同长度将与屏幕空间中的相同长度,但是在顶部轴中,xaxis和yaxis的数据范围不同,因此不会是正方形。要解决此问题,可以将纵横比设置为x轴范围和y轴范围的比率:

from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
from scipy.spatial.distance import pdist
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
from numpy import arange
fig = plt.figure(figsize=(5,7))
ax1 = plt.subplot(2, 1, 1)
cm = matplotlib.cm.Blues
X = np.random.random([5,5])
pmat = pdist(X,"euclidean")
linkmat = linkage(pmat)
dendrogram(linkmat)
x0,x1 = ax1.get_xlim()
y0,y1 = ax1.get_ylim()
ax1.set_aspect((x1-x0)/(y1-y0))
plt.subplot(2, 1, 2, aspect=1)
labels = ["a","b","c","d","e","f"]
Y = np.random.random([6,6])
plt.xticks(arange(0.5, 7.5, 1))
plt.gca().set_xticklabels(labels)
plt.pcolor(Y)
plt.colorbar()

这是输出:


要定位颜色栏,我们需要编写一个ColorBarLocator类,pad和width参数以像素为单位,

垫:设置轴之间的空间和它的colobar

宽度:颜色栏的宽度

用以下代码替换plt.colorbar():

class ColorBarLocator(object):
def __init__(self, pax, pad=5, width=10):
self.pax = pax
self.pad = pad
self.width = width
def __call__(self, ax, renderer):
x, y, w, h = self.pax.get_position().bounds
fig = self.pax.get_figure()
inv_trans = fig.transFigure.inverted()
pad, _ = inv_trans.transform([self.pad, 0])
width, _ = inv_trans.transform([self.width, 0])
return [x+w+pad, y, width, h]
cax = fig.add_axes([0,0,0,0], axes_locator=ColorBarLocator(ax2))
plt.colorbar(cax = cax)


感谢您的回答,但您的地块仍无法像常规子图一样对齐。顶部和底部的边界框未对齐。我希望它们垂直对齐,并且颜色向后悬挂在右侧,有点像您拥有它但没有对齐。有什么想法吗?

感谢您的回答-它的输出完全正确,但是代码似乎非常复杂!有更容易的方法吗?似乎您必须是matplotlib开发人员之一,才能知道如何编写这样的内容,只是为了使颜色条排列起来...

以下是一些示例,但我认为它们也很难理解:matplotlib.sourceforge.net/examples/axes_grid/ matplotlib.sourceforge.net/examples/axes_grid/

我很好地知道了这些示例,但我认为它们不适用,因为其他子图中存在树状图,因为这会由于某些原因而使事情中断。

您知道为什么matplotlib弄乱了底部子图中x标签的垂直对齐吗?为什么arent a, b, c, d, e, f全部对齐?

@HYRY的答案非常好,应该得到所有荣誉。但是要结束有关很好地排列平方图的答案,您可以欺骗matplotlib认为这两个图都有颜色条,仅使第一个不可见:

from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
from scipy.spatial.distance import pdist
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
from numpy import arange
fig = plt.figure(figsize=(5,7))
ax1 = plt.subplot(2, 1, 1)
cm = matplotlib.cm.Blues
X = np.random.random([5,5])
pmat = pdist(X,"euclidean")
linkmat = linkage(pmat)
dendrogram(linkmat)
x0,x1 = ax1.get_xlim()
y0,y1 = ax1.get_ylim()
ax1.set_aspect((x1-x0)/(y1-y0))
plt.subplot(2, 1, 2, aspect=1)
labels = ["a","b","c","d","e","f"]
Y = np.random.random([6,6])
plt.xticks(arange(0.5, 7.5, 1))
plt.gca().set_xticklabels(labels)
plt.pcolor(Y)
plt.colorbar()
# add a colorbar to the first plot and immediately make it invisible
cb = plt.colorbar(ax=ax1)
cb.ax.set_visible(False)
plt.show()


要添加其他答案,您需要将参数的绝对值带到.set_aspect:

x0,x1 = ax1.get_xlim()
y0,y1 = ax1.get_ylim()
ax1.set_aspect(abs(x1-x0)/abs(y1-y0))