繁体   English   中英

Python Matplotlib线图与contour / imshow对齐

[英]Python Matplotlib line plot aligned with contour/imshow

如何使用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)

在此输入图像描述

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

  1. 图中的轴边界框(由子图规范或特定范围控制,如图fig.add_axes([left, bottom, width, height]) 。轴限制(不计入刻度标签)将始终在此框内。
  2. 通过更改数据限制或轴“box”的形状来控制是否可以控制限制或纵横比变化的adjustable参数。 这可以是"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()

在此输入图像描述


共享轴

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

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

在此输入图像描述

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

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

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

在此输入图像描述

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

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

非共享轴

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

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

在此输入图像描述

您是否在寻找相对于第一轴的任意定位? 你可以玩这个数字的bbox。

ax2.set_position(ax.get_position().translated(0, -.5))将第二个轴放在第一个轴下,具有相同的基本形状。 或者你可以做到

box = ax.get_position()

# Positioning code here

ax2.set_position(box)

您的定位代码然后通过重新分配( box = box.translated(0, -.5) )或变异( box.x1 += .1 )来改变框。 框似乎显示了它的左下角和右上角,其属性为.p0,.x0,.y0和.p1,.x1,.y1; 以及.width和.height

Box或多或少是一个图形坐标,你也可以用原始数字“设置宽度,高度和偏移”: ax2.set_position([left, bottom, width, height])

PS:不幸的是,这个bbox还包括文本标签的宽度和高度。 例如,你的第一个图的宽度为0.27 ......高度为0.36 ......你不会通过改变尺寸来扭曲文本,但它确实意味着除非你从一个开始,否则很难得到一个完美的正方形。

在撰写本文时,现有的两个答案都很有用,但不提供解决方案。 随后是解决方案。 此处使用的密钥(不是其他答案)是直接访问spine位置。 在访问坐标之前需要plt.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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM