簡體   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