13

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

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()

这会产生以下结果:

在此处输入图像描述

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

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

在此处输入图像描述

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

在此处输入图像描述

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

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

4

3 回答 3

15

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 参数以像素为单位,

  • pad:设置轴之间的空间和它的 colobar
  • width:颜色条的宽度

替换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)

在此处输入图像描述

于 2012-08-10T01:04:29.837 回答
10

@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()

代码输出

于 2012-08-15T22:08:59.583 回答
1

要添加其他答案,您需要将参数的绝对值设为.set_aspect

x0,x1 = ax1.get_xlim()
y0,y1 = ax1.get_ylim()
ax1.set_aspect(abs(x1-x0)/abs(y1-y0))
于 2013-10-19T08:31:34.887 回答