Python Matplotlib线图与contour/imshow对齐

man*_*off 9 python matplotlib

如何使用Python和Matplotlib将一个子图的视觉宽度设置为等于另一个子图的宽度?第一个图具有固定的宽高比和imshow的正方形像素.然后我想在它下面放一个线条图,但我不能这样做并让一切都对齐.

我很确定该解决方案涉及此转换教程页面上的信息.我尝试过使用fig.transFigure,ax.transAxes,ax.transData等,但还没有成功.我需要在上面板中找到轴的宽度和高度以及偏移,然后能够在下面板中设置轴的宽度,高度和偏移.不应包括轴标签和刻度线等,也不应更改对齐方式.

例如,以下代码

fig = plt.figure(1)
fig.clf()

data = np.random.random((3,3))
xaxis = np.arange(0,3)
yaxis = np.arange(0,3)

ax = fig.add_subplot(211)
ax.imshow(data, interpolation='none')
c = ax.contour(xaxis, yaxis, data, colors='k')

ax2 = fig.add_subplot(212)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Joe*_*ton 8

matplotlib轴的轮廓由三件事控制:

  1. 图中的轴的边界框(由子图规范或特定范围控制,例如fig.add_axes([left, bottom, width, height]).轴限制(不计入刻度标签)将始终在此框内.
  2. adjustable通过更改数据限制或轴"box"的形状来控制是否允许更改限制或纵横比的参数.这可能是"datalim","box""box-forced".(后者用于共享轴.)
  3. 轴限制和纵横比.对于具有固定纵横比的图,adjustable将更改轴框或数据限制(取决于)以保持指定的纵横比.纵横比是指数据坐标,而不是直接的轴的形状.

对于最简单的情况:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2)

data = np.random.random((3,3))
xaxis = np.arange(0,3)
yaxis = np.arange(0,3)

axes[0].imshow(data, interpolation='none')
c = axes[0].contour(xaxis, yaxis, data, colors='k')

axes[1].set_aspect(1)

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


共享轴

但是,如果您想确保它保持相同的形状,并且您可以使用具有相同数据限制的两个图表,则可以执行以下操作:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2), sharex=True, sharey=True)
plt.setp(axes.flat, adjustable='box-forced')

data = np.random.random((5,3))
xaxis = np.arange(0,3)
yaxis = np.arange(0,5)

axes[0].imshow(data, interpolation='none')
c = axes[0].contour(xaxis, yaxis, data, colors='k')

axes[1].plot([-0.5, 2.5], [-0.5, 4.5])
axes[1].set_aspect(1)

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

但是,您可能会注意到这看起来不太合适.那是因为第二个子图是由于我们绘制内容的顺序而控制第一个子图的范围.

基本上,对于共享轴,我们最后绘制的内容将控制初始范围,因此如果我们只是交换我们正在绘制的顺序:

    import numpy as np
    import matplotlib.pyplot as plt

    fig, axes = plt.subplots(nrows=2, sharex=True, sharey=True)
    plt.setp(axes.flat, adjustable='box-forced')

    data = np.random.random((5,3))
    xaxis = np.arange(0,3)
    yaxis = np.arange(0,5)

    axes[1].plot([-0.5, 2.5], [-0.5, 4.5])
    axes[1].set_aspect(1)

    axes[0].imshow(data, interpolation='none')
    c = axes[0].contour(xaxis, yaxis, data, colors='k')

    plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

当然,如果您不关心链接图的交互式缩放/平移,您可以完全跳过共享轴,只需:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2)

data = np.random.random((5,3))
xaxis = np.arange(0,3)
yaxis = np.arange(0,5)

axes[0].imshow(data, interpolation='none')
c = axes[0].contour(xaxis, yaxis, data, colors='k')

axes[1].plot([-0.5, 2.5], [-0.5, 4.5])

# Copy extents and aspect from the first axes...
axes[1].set_aspect(axes[0].get_aspect())
axes[1].axis(axes[0].axis())

plt.show()
Run Code Online (Sandbox Code Playgroud)

非共享轴

如果您不希望两个轴具有相同的数据范围,则可以强制它们具有相同的大小(但如果以交互方式进行缩放,则不会链接它们).为此,您需要根据第二个图的范围和第一个图的范围/方面来计算第二个图的宽高比.

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=2)

data = np.random.random((3,3))
xaxis = np.arange(0,3)
yaxis = np.arange(0,3)

axes[0].imshow(data, interpolation='none')
c = axes[0].contour(xaxis, yaxis, data, colors='k')

axes[1].plot(np.linspace(0, 10, 100), np.random.normal(0, 1, 100).cumsum())

# Calculate the proper aspect for the second axes
aspect0 = axes[0].get_aspect()
if aspect0 == 'equal':
    aspect0 = 1.0
dy = np.abs(np.diff(axes[1].get_ylim()))
dx = np.abs(np.diff(axes[1].get_xlim()))

aspect = aspect0 / (float(dy) / dx)
axes[1].set_aspect(aspect)

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


man*_*off 2

在撰写本文时,现有的两个答案很有帮助,但没有提供解决方案。解决方案如下。这里使用的密钥(而不是其他答案中使用的密钥)是直接访问位置spineplt.draw()需要在访问它们之前更新坐标。

import numpy as np
from matplotlib import pyplot as plt
im = np.arange(256).reshape(16,16)

fig = plt.figure(1)
fig.clf()
ax = fig.add_subplot(211)

ax.imshow(im)
plt.show()

transAxes = ax.transAxes
invFig = fig.transFigure.inverted()

llx,urx = plt.xlim()
lly,ury = plt.ylim()

llx0, lly0 = transAxes.transform((0,0))
llx1, lly1 = transAxes.transform((1,1))

plt.draw()
spleft = ax.spines['left'].get_verts()
spright = ax.spines['right'].get_verts()
llx0 = spleft[0,0]
llx1 = spright[0,0]

axp = invFig.transform(((lly0,llx0),(lly1,llx1)))
ax2 = fig.add_axes([axp[0,1],axp[0,0]-0.5,axp[1,1]-axp[0,1],axp[1,0]-axp[0,0]])
ax2.plot(np.arange(10))
plt.draw()
Run Code Online (Sandbox Code Playgroud)