origin画图_把heatmap翻一转:imshow的origin和extent
背景故事:
之前畫熱度圖用的seaborn,里面好像有一個inversey的選項,可以把熱度圖直接做垂直翻轉。結合numpy的histogram2d來看,這是十分實用的,因為histogram2d得到的2d heatmap直接用matplotlib.pyplot的Axes.imshow方法畫出來的圖,是上下顛倒的,如果用一次np.flip(heatmap, 0),會發現圖放正了,但是縱坐標的刻度還是反的,需要反著寫Axes.set_yticks與Axes.set_yticklabels來調正。很顯然,plt作為使用如此廣泛的庫,沒有理由這么搞人心態。
所以查了下np.histogram2d的文檔,發現范例里有用到extent,再去看了看matplotlib的文檔,發現了extent的具體用法,在這里記錄一下。
官方原文點上面那個“extent的具體用法”,有超鏈接的,點進去就可以看了,下面的內容是我自己寫的范例。
首先看看背景故事里說的麻煩的情況:
import numpy as np import matplotlib.pyplot as pltx = np.random.normal(0, 1, (100,)) y = np.random.normal(0, 1, (100,)) heatmap, xedge, yedge = np.histogram2d(x, y, bins=(100, 10), range=((-4, 4), (-3, 3)))fig, ax = plt.subplots() fig.set_size_inches(4, 12) ax.set_xlabel('x') ax.set_ylabel('y') ax.imshow(heatmap) fig.savefig('./bad1.png')這是不加任何修飾,直接畫出來的圖。該圖體現了一個最顯然的問題,就是我的x應該是100個bin,y應該是10個bin,應該是一個矮胖的形狀而不是一個高瘦的。
這是因為np.histogram2d的返回值就是這樣設定的,如果需要畫圖,要先轉置一下:
heatmap = heatmap.T那么再畫一次:
fig, ax = plt.subplots() fig.set_size_inches(12, 4) ax.set_xlabel('x') ax.set_ylabel('y') ax.imshow(heatmap) fig.savefig('./bad2.png')現在看上去對勁了,但是縱坐標是反的,我們希望的圖是橫縱坐標都從0開始。那么怎么解決呢?
fig, ax = plt.subplots() fig.set_size_inches(12, 4) ax.set_xlabel('x') ax.set_ylabel('y') heatmap_inverse = np.flip(heatmap, 0) ax.imshow(heatmap) ax.set_yticks([5, 9]) ax.set_yticklabels(['5', '0']) fig.savefig('./bad3.png')這么一看效果是達到了,實際上操作很麻煩而且思路很奇怪,如果把code給別人看別人可能會疑惑。所以就需要用到imshow的origin參數。
先在一個簡單的2d heatmap上測試,產生一個長這樣的方陣:
test_map = np.eye(10) test_map *= np.arange(1, 11, 1)就是對角元為1到10的對角矩陣,畫出來是這樣的:
test_fig, test_ax = plt.subplots() test_ax.imshow(test_map) test_fig.savefig('./test_default.png')修改一下origin參數試試:
test_fig, test_ax = plt.subplots() test_ax.imshow(test_map, origin='upper') test_ax.set_title('origin = upper') test_fig.savefig('./test_upper.png') test_ax.imshow(test_map, origin='lower') test_ax.set_title('origin = lower') test_fig.savefig('./test_lower.png')可以看出,origin默認值的就是upper,它的意思是從上往下畫;對應地,lower就是從下往上畫。不難發現,不論怎樣規定,橫坐標都是從左往右的,這也確實比較符合人們的習慣。
那么之前的熱度圖就好畫了,指定origin為lower即可。
fig, ax = plt.subplots() fig.set_size_inches(12, 4) ax.set_xlabel('x') ax.set_ylabel('y') ax.imshow(heatmap, origin='lower') fig.savefig('./good.png')這就不再需要自己折騰了。以上就是origin的設置。然后可能有人就想說了,我不喜歡橫坐標從左到右,我要復古一下,從右到左,怎么辦呢?那就需要用到extent了。
首先給出四個不同的extent,保存在列表里:
extent = [[0, 9, 0, 9], [0, 9, 9, 0], [9, 0, 0, 9], [9, 0, 9, 0]]extent參數接受1個array-like的參數,也就是有sequence概念的輸入(tuple、list、1darray均可)。這個array-like的參數分別代表了左右下上,也就是[橫軸最左邊的值,橫軸最右邊的值,縱軸最下面的值,縱軸最上面的值]這樣。
然后畫圖看一下效果:
for idx in range(4):test_fig, test_ax = plt.subplots()test_ax.imshow(test_map, extent=extent[idx])test_ax.set_title('extent%d'%(idx+1))test_fig.savefig('./test_extent%d.png'%(idx+1))可以發現,成功地調換了坐標軸的順序,但是軸變了,圖沒變,好像很離譜。這是因為,extent的作用是改變橫縱坐標展示的范圍,如果你去看官方的文檔,會發現它可以搭配Axes.set_xlim與Axes.set_ylim使用,來讓圖片轉起來,就像下面這樣:
for idx in range(4):test_fig, test_ax = plt.subplots()test_ax.imshow(test_map, extent=extent[idx])test_ax.set_title('extent_with_xylim%d'%(idx+1))test_ax.set_xlim([0, 9])test_ax.set_ylim([0, 9])test_fig.savefig('./test_extent%d_with_xylim.png'%(idx+1))然而實際上你也會發現,圖雖然轉起來了,但是圖上的點和橫縱坐標并不能對應上,以這四張圖為例,只有圖2(extent_with_xylim2)是對的,而它等價于指定origin='lower'。那么我們想要的那種,圖轉起來,坐標也對應上,應該怎么操作呢?
xylim = [[[0, 9], [0, 9]], [[0, 9], [9, 0]], [[9, 0], [0, 9]], [[9, 0], [9, 0]]] for idx in range(4):test_fig, test_ax = plt.subplots()test_ax.imshow(test_map, extent=[0, 9, 9, 0])test_ax.set_title('xylim%d'%(idx+1))test_ax.set_xlim(xylim[idx][0])test_ax.set_ylim(xylim[idx][1])test_fig.savefig('./test_xylim%d.png'%(idx+1))其實只要設置好xlim和ylim就可以了,然后用extent來限定坐標范圍(如果不設置的話,第一行、列和最后一行、列將很窄)。一個疑問也許是,為什么這里的extent=[0, 9, 9, 0]?因為默認的origin是upper,縱坐標是倒過來的;如果設置origin為lower,就可以把extent設置為更符合直覺的[0, 9, 0, 9]。
matplotlib的水真是深啊~
(完。)
總結
以上是生活随笔為你收集整理的origin画图_把heatmap翻一转:imshow的origin和extent的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 检查汽车减震坏没坏要多久?
- 下一篇: 别克君越2.0的为什么钥匙在身边说还搜不