簡體   English   中英

如何使用適合在一起的圖像和輪廓圖創建 Matplotlib 圖?

[英]How to create Matplotlib figure with image and profile plots that fit together?

我想將 2d 數據繪制為圖像,沿着 x 和 y 軸繪制輪廓圖,顯示在下方和側面。 這是顯示數據的一種非常常見的方式,因此可能有一種更簡單的方法來解決這個問題。 我想找到最簡單、最健壯的方法來正確執行此操作,並且不使用 matplotlib 之外的任何內容(盡管我有興趣了解可能特別相關的其他軟件包)。 特別是,如果數據的形狀(縱橫比)發生變化,該方法應該在不改變任何內容的情況下工作。

我的主要問題是讓副圖正確縮放,以便它們的邊界與主圖相匹配。

示例代碼:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
# generate grid and test data
x, y = np.linspace(-3,3,300), np.linspace(-1,1,100)
X, Y = np.meshgrid(x,y)
def f(x,y) :
    return np.exp(-(x**2/4+y**2)/.2)*np.cos((x**2+y**2)*10)**2
data = f(X,Y)

# 2d image plot with profiles
h, w = data.shape
gs = gridspec.GridSpec(2, 2,width_ratios=[w,w*.2], height_ratios=[h,h*.2])
ax = [plt.subplot(gs[0]),plt.subplot(gs[1]),plt.subplot(gs[2])]
bounds = [x.min(),x.max(),y.min(),y.max()]
ax[0].imshow(data, cmap='gray', extent = bounds, origin='lower')
ax[1].plot(data[:,w/2],Y[:,w/2],'.',data[:,w/2],Y[:,w/2])
ax[1].axis([data[:,w/2].max(), data[:,w/2].min(), Y.min(), Y.max()])
ax[2].plot(X[h/2,:],data[h/2,:],'.',X[h/2,:],data[h/2,:])
plt.show()

正如您從下面的輸出中看到的那樣,將圖像向右縮放的方式與邊界不正確匹配。

部分解決方案:

1)手動調整圖形大小以找到正確的縱橫比,使其正確顯示(可以自動使用圖像比例+填充+使用的寬度比嗎?)。 當已經有很多應該自動處理這些事情的打包選項時,這似乎很俗氣。 編輯:如果填充沒有改變, plt.gcf().set_figheight(f.get_figwidth()*h/w)似乎工作。

2) 添加ax[0].set_aspect('auto') ,然后使邊界ax[0].set_aspect('auto') ,但圖像不再具有正確的縱橫比。

上面代碼示例的輸出: 示例輸出

您可以使用sharexsharey來執行此操作,將ax=行替換為:

ax = [plt.subplot(gs[0]),]
ax.append(plt.subplot(gs[1], sharey=ax[0]))
ax.append(plt.subplot(gs[2], sharex=ax[0]))

在此處輸入圖片說明

我還沒有能夠通過使用以產生布局subplotgridspec ,同時仍保留(1)的軸線和比(2)施加在軸的限制。 另一種解決方案是將軸手動放置在圖形中,並相應地控制圖形的大小(正如您在 OP 中已經提到的那樣)。 雖然這需要比使用更多的工作subplotgridspec ,這種方法仍然相當簡單,也可以很強大和靈活的生產,其中在利潤率精細控制和軸的位置需要復雜的布局。

下面是一個示例,說明如何根據給定軸的大小設置圖形的大小來實現這一點。 相反,也可以將軸擬合在預定義大小的圖形中。 然后將使用圖形邊距作為緩沖區來保持軸的縱橫比。

import numpy as np
import matplotlib.pyplot as plt

plt.close('all')

#------------------------------------------------------------ generate data ----

# generate grid and test data
x, y = np.linspace(-3, 3, 300), np.linspace(-1, 1, 100)
X, Y = np.meshgrid(x,y)
def f(x,y) :
    return np.exp(-(x**2/4+y**2)/.2)*np.cos((x**2+y**2)*10)**2
data = f(X,Y)

# 2d image plot with profiles
h, w = data.shape
data_ratio = h / float(w)

#------------------------------------------------------------ create figure ----

#--- define axes lenght in inches ----

width_ax0 = 8.
width_ax1 = 2.
height_ax2 = 2.

height_ax0 = width_ax0 * data_ratio

#---- define margins size in inches ----

left_margin  = 0.65
right_margin = 0.2
bottom_margin = 0.5
top_margin = 0.25
inter_margin = 0.5

#--- calculate total figure size in inches ----

fwidth = left_margin + right_margin + inter_margin + width_ax0 + width_ax1
fheight = bottom_margin + top_margin + inter_margin + height_ax0 + height_ax2

fig = plt.figure(figsize=(fwidth, fheight))
fig.patch.set_facecolor('white')

#---------------------------------------------------------------- create axe----

ax0 = fig.add_axes([left_margin / fwidth,
                    (bottom_margin + inter_margin + height_ax2) / fheight,
                    width_ax0 / fwidth, height_ax0 / fheight])

ax1 = fig.add_axes([(left_margin + width_ax0 + inter_margin) / fwidth,
                    (bottom_margin + inter_margin + height_ax2) / fheight,
                     width_ax1 / fwidth, height_ax0 / fheight])

ax2 = fig.add_axes([left_margin / fwidth, bottom_margin / fheight,
                    width_ax0 / fwidth, height_ax2 / fheight])

#---------------------------------------------------------------- plot data ----

bounds = [x.min(),x.max(),y.min(),y.max()]
ax0.imshow(data, cmap='gray', extent = bounds, origin='lower')
ax1.plot(data[:,w/2],Y[:,w/2],'.',data[:,w/2],Y[:,w/2])
ax1.invert_xaxis()
ax2.plot(X[h/2,:], data[h/2,:], '.', X[h/2,:], data[h/2,:])

plt.show(block=False)
fig.savefig('subplot_layout.png')

結果是:

在此處輸入圖片說明

有趣的是, sharexsharey的解決方案對我不起作用。 它們對齊軸范圍但對齊軸長度 為了讓它們可靠地對齊,我補充說:

pos  = ax[0].get_position()
pos1 = ax[1].get_position()
pos2 = ax[2].get_position()
ax[1].set_position([pos1.x0,pos.y0,pos1.width,pos.height])
ax[2].set_position([pos.x0,pos2.y0,pos.width,pos2.height])

因此,結合 CT Zhu 的早期回答,這使得:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
# generate grid and test data
x, y = np.linspace(-3,3,300), np.linspace(-1,1,100)
X, Y = np.meshgrid(x,y)
def f(x,y) :
    return np.exp(-(x**2/4+y**2)/.2)*np.cos((x**2+y**2)*10)**2

data = f(X,Y)

# 2d image plot with profiles
h, w = data.shape
gs = gridspec.GridSpec(2, 2,width_ratios=[w,w*.2], height_ratios=[h,h*.2])
ax = [plt.subplot(gs[0]),]
ax.append(plt.subplot(gs[1], sharey=ax[0]))
ax.append(plt.subplot(gs[2], sharex=ax[0]))
bounds = [x.min(),x.max(),y.min(),y.max()]
ax[0].imshow(data, cmap='gray', extent = bounds, origin='lower')
ax[1].plot(data[:,int(w/2)],Y[:,int(w/2)],'.',data[:,int(w/2)],Y[:,int(w/2)])
ax[1].axis([data[:,int(w/2)].max(), data[:,int(w/2)].min(), Y.min(), Y.max()])
ax[2].plot(X[int(h/2),:],data[int(h/2),:],'.',X[int(h/2),:],data[int(h/2),:])
pos  = ax[0].get_position()
pos1 = ax[1].get_position()
pos2 = ax[2].get_position()
ax[1].set_position([pos1.x0,pos.y0,pos1.width,pos.height])
ax[2].set_position([pos.x0,pos2.y0,pos.width,pos2.height])
plt.show()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM