【问题标题】:how to make square subplots in matplotlib with heatmaps?如何使用热图在 matplotlib 中制作方形子图?
【发布时间】:2012-08-07 06:26:59
【问题描述】:

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

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总是使轴成正方形?我想不出一个我不想要方轴的情况,但这通常不是默认行为。如果可能的话,我想强制所有地块都是正方形的。

【问题讨论】:

    标签: python numpy matplotlib scipy


    【解决方案1】:

    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之间的空间
    • 宽度:颜色条的宽度

    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 会弄乱底部子图中 x 标签的垂直对齐方式吗?为什么a, b, c, d, e, f 不对齐?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2017-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-31
        • 1970-01-01
        • 1970-01-01
        • 2019-10-21
        • 2021-10-25
        相关资源
        最近更新 更多