Matplotlib 中文用戶指南 3.7 變換教程

變換教程

原文:Transformations Tutorial

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

像任何圖形包一樣沥阱,matplotlib 建立在變換框架之上似芝,以便在坐標(biāo)系减响,用戶數(shù)據(jù)坐標(biāo)系,軸域坐標(biāo)系犀变,圖形坐標(biāo)系和顯示坐標(biāo)系之間輕易變換莲组。 在 95 %的繪圖中,你不需要考慮這一點(diǎn)燥筷,因?yàn)樗l(fā)生在背后,但隨著你接近自定義圖形生成的極限呻惕,它有助于理解這些對(duì)象荆责,以便可以重用 matplotlib 提供給你的現(xiàn)有變換滥比,或者創(chuàng)建自己的變換(見matplotlib.transforms)亚脆。 下表總結(jié)了現(xiàn)有的坐標(biāo)系,你應(yīng)該在該坐標(biāo)系中使用的變換對(duì)象盲泛,以及該系統(tǒng)的描述濒持。 在『變換對(duì)象』一列中,axAxes實(shí)例寺滚,fig是一個(gè)圖形實(shí)例柑营。

坐標(biāo)系 變換對(duì)象 描述
數(shù)據(jù) ax.transData 用戶數(shù)據(jù)坐標(biāo)系,由xlimylim控制
軸域 ax.transAxes 軸域坐標(biāo)系村视;(0,0)是軸域左下角官套,(1,1)是軸域右上角
圖形 fig.transFigure 圖形坐標(biāo)系;(0,0)是圖形左下角蚁孔,(1,1)是圖形右上角
顯示 None 這是顯示器的像素坐標(biāo)系; (0,0)是顯示器的左下角奶赔,(width, height)是顯示器的右上角,以像素為單位杠氢。 或者站刑,可以使用恒等變換(matplotlib.transforms.IdentityTransform())來(lái)代替None

上表中的所有變換對(duì)象都接受以其坐標(biāo)系為單位的輸入鼻百,并將輸入變換到顯示坐標(biāo)系绞旅。 這就是為什么顯示坐標(biāo)系沒(méi)有『變換對(duì)象』的原因 - 它已經(jīng)以顯示坐標(biāo)為單位了摆尝。 變換也知道如何反轉(zhuǎn)自身,從顯示返回自身的坐標(biāo)系因悲。 這在處理來(lái)自用戶界面的事件(通常發(fā)生在顯示空間中)堕汞,并且你想知道數(shù)據(jù)坐標(biāo)系中鼠標(biāo)點(diǎn)擊或按鍵按下的位置時(shí)特別有用。

數(shù)據(jù)坐標(biāo)

讓我們從最常用的坐標(biāo)囤捻,數(shù)據(jù)坐標(biāo)系開始臼朗。 每當(dāng)向軸域添加數(shù)據(jù)時(shí),matplotlib 會(huì)更新數(shù)據(jù)對(duì)象蝎土,set_xlim()set_ylim()方法最常用于更新视哑。 例如,在下圖中誊涯,數(shù)據(jù)的范圍在x軸上為從 0 到 10挡毅,在y軸上為從 -1 到 1。

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 10, 0.005)
y = np.exp(-x/2.) * np.sin(2*np.pi*x)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)

plt.show()

你可以使用ax.transData實(shí)例將數(shù)據(jù)變換為顯示坐標(biāo)系暴构,無(wú)論是單個(gè)點(diǎn)或是一系列點(diǎn)跪呈,如下所示:

In [14]: type(ax.transData)
Out[14]: <class 'matplotlib.transforms.CompositeGenericTransform'>

In [15]: ax.transData.transform((5, 0))
Out[15]: array([ 335.175,  247.   ])

In [16]: ax.transData.transform([(5, 0), (1,2)])
Out[16]:
array([[ 335.175,  247.   ],
       [ 132.435,  642.2  ]])

你可以使用inverted()方法創(chuàng)建一個(gè)變換,從顯示坐標(biāo)變換為數(shù)據(jù)坐標(biāo):

In [41]: inv = ax.transData.inverted()

In [42]: type(inv)
Out[42]: <class 'matplotlib.transforms.CompositeGenericTransform'>

In [43]: inv.transform((335.175,  247.))
Out[43]: array([ 5.,  0.])

如果你一直關(guān)注本教程取逾,如果你的窗口大小或 dpi 設(shè)置不同耗绿,顯示坐標(biāo)的確切值可能會(huì)有所不同。 同樣砾隅,在下面的圖形中误阻,在 ipython 會(huì)話中,由顯示標(biāo)記的點(diǎn)可能并不相同晴埂,因?yàn)槲臋n圖形大小默認(rèn)值是不同的究反。

注意

如果在 GUI 后端中運(yùn)行上述示例中的源代碼,你還可能發(fā)現(xiàn)數(shù)據(jù)和顯示標(biāo)注的兩個(gè)箭頭不會(huì)指向完全相同的點(diǎn)儒洛。 這是因?yàn)轱@示點(diǎn)是在顯示圖形之前計(jì)算的精耐,并且 GUI 后端可以在創(chuàng)建圖形時(shí)稍微調(diào)整圖形大小。 如果你自己調(diào)整圖的大小琅锻,效果更明顯卦停。 這是你很少想要處理顯示空間的一個(gè)很好的原因,但是你可以連接到'on_draw'事件來(lái)更新圖上的圖坐標(biāo)恼蓬;請(qǐng)參閱事件處理和選擇惊完。

當(dāng)你更改軸的xy的范圍時(shí),將更新數(shù)據(jù)范圍滚秩,以便變換生成新的顯示點(diǎn)专执。 注意,當(dāng)我們只是改變ylim郁油,只有y顯示坐標(biāo)改變本股,當(dāng)我們改變xlim也同理攀痊。 我們?cè)谡務(wù)?Bbox 時(shí)會(huì)深入。

In [54]: ax.transData.transform((5, 0))
Out[54]: array([ 335.175,  247.   ])

In [55]: ax.set_ylim(-1,2)
Out[55]: (-1, 2)

In [56]: ax.transData.transform((5, 0))
Out[56]: array([ 335.175     ,  181.13333333])

In [57]: ax.set_xlim(10,20)
Out[57]: (10, 20)

In [58]: ax.transData.transform((5, 0))
Out[58]: array([-171.675     ,  181.13333333])

軸域坐標(biāo)

在數(shù)據(jù)坐標(biāo)系之后拄显,軸域可能是第二有用的坐標(biāo)系苟径。 這里,點(diǎn)(0,0)是軸域或子圖的左下角躬审,(0.5,0.5)是中心棘街,(1.0,1.0)是右上角。 你還可以引用范圍之外的點(diǎn)承边,因此(-0.1,1.1)位于軸的左上方遭殉。 此坐標(biāo)系在將文本放置在軸中時(shí)非常有用,因?yàn)槟阃ǔP枰诠潭ǖ奈恢茫ɡ绮┲S域窗格的左上角)放置文本氣泡险污,并且在平移或縮放時(shí)保持該位置固定。 這里是一個(gè)簡(jiǎn)單的例子富岳,創(chuàng)建四個(gè)面板蛔糯,并將他們標(biāo)記為'A''B'窖式,'C'蚁飒,'D',你經(jīng)常在期刊上看到它們萝喘。

你也可以在軸坐標(biāo)系中創(chuàng)建線條或者補(bǔ)丁淮逻,但是以我的經(jīng)驗(yàn),這比使用ax.transAxes放置文本更不實(shí)用蜒灰。 盡管如此弦蹂,這里是一個(gè)愚蠢的例子肩碟,它在數(shù)據(jù)空間中繪制了一些隨機(jī)點(diǎn)强窖,并且覆蓋在一個(gè)半透明的圓上面,這個(gè)圓以軸域的中心為圓心削祈,半徑為軸域的四分之一翅溺。 - 如果你的軸域不保留高寬比(見set_aspect ()),它將看起來(lái)像一個(gè)橢圓髓抑。 使用平移/縮放工具移動(dòng)咙崎,或手動(dòng)更改數(shù)據(jù)的xlimylim,你將看到數(shù)據(jù)移動(dòng)吨拍,但圓將保持固定褪猛,因?yàn)樗辉跀?shù)據(jù)坐標(biāo)中,并且將始終保持在軸域的中心 羹饰。

混合變換

在數(shù)據(jù)與軸域坐標(biāo)混合的混合坐標(biāo)空間中繪制是非常實(shí)用的伊滋,例如創(chuàng)建一個(gè)水平跨度碳却,突出y數(shù)據(jù)的一些區(qū)域但橫跨x軸,而無(wú)論數(shù)據(jù)限制笑旺,平移或縮放級(jí)別等昼浦。實(shí)際上這些混合線條和跨度非常有用,我們已經(jīng)內(nèi)置了一些函數(shù)來(lái)使它們?nèi)菀桌L制(參見axhline()筒主,axvline()关噪,axhspan()axvspan())乌妙,但是為了教學(xué)目的使兔,我們使用混合變換實(shí)現(xiàn)這里的水平跨度。 這個(gè)技巧只適用于可分離的變換藤韵,就像你在正常的笛卡爾坐標(biāo)系中看到的火诸,但不能為不可分離的變換,如PolarTransform(極坐標(biāo)變換)荠察。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.transforms as transforms

fig = plt.figure()
ax = fig.add_subplot(111)

x = np.random.randn(1000)

ax.hist(x, 30)
ax.set_title(r'$\sigma=1 \/ \dots \/ \sigma=2$', fontsize=16)

# the x coords of this transformation are data, and the
# y coord are axes
trans = transforms.blended_transform_factory(
    ax.transData, ax.transAxes)

# highlight the 1..2 stddev region with a span.
# We want x to be in data coordinates and y to
# span from 0..1 in axes coords
rect = patches.Rectangle((1,0), width=1, height=1,
                         transform=trans, color='yellow',
                         alpha=0.5)

ax.add_patch(rect)

plt.show()

混合變換非常有用置蜀,其中x為數(shù)據(jù)坐標(biāo)而y為軸域坐標(biāo),我們擁有輔助方法來(lái)返回內(nèi)部使用的版本 mpl 悉盆,用于繪制ticks盯荤,ticklabels以及其他辑莫。方法是matplotlib.axes.Axes.get_xaxis_transform()matplotlib.axes.Axes.get_yaxis_transform()涧尿。 因此归薛,在上面的示例中捧毛,blended_transform_factory()的調(diào)用可以替換為get_xaxis_transform

trans = ax.get_xaxis_transform()

使用偏移變換來(lái)創(chuàng)建陰影效果

變換的一個(gè)用法捺檬,是創(chuàng)建偏離另一變換的新變換耻姥,例如兑牡,放置一個(gè)對(duì)象赃泡,相對(duì)于另一對(duì)象有一些偏移来农。 通常鞋真,你希望物理尺寸上有一些移位,例如以點(diǎn)或英寸沃于,而不是數(shù)據(jù)坐標(biāo)為單位涩咖,以便移位效果在不同的縮放級(jí)別和 dpi 設(shè)置下保持不變。

偏移的一個(gè)用途是創(chuàng)建一個(gè)陰影效果繁莹,其中你繪制一個(gè)與第一個(gè)相同的對(duì)象檩互,剛好在它的右邊和下面,調(diào)整zorder來(lái)確保首先繪制陰影咨演,然后繪制對(duì)象闸昨,陰影在它之上。 變換模塊具有輔助變換ScaledTranslation。 它可以這樣來(lái)實(shí)例化:

trans = ScaledTranslation(xt, yt, scale_trans)

其中xtyt是變換的偏移饵较,scale_trans是變換溉跃,在應(yīng)用偏移之前的變換期間縮放xtyt。 一個(gè)典型的用例是告抄,將圖形的fig.dpi_scale_trans變換用于scale_trans參數(shù)撰茎,來(lái)在實(shí)現(xiàn)最終的偏移之前,首先將以點(diǎn)為單位的xtyt縮放到顯示空間打洼。
DPI 和英寸偏移是常見的用例龄糊,我們擁有一個(gè)特殊的輔助函數(shù),來(lái)在matplotlib.transforms.offset_copy()中創(chuàng)建它募疮,它返回一個(gè)帶有附加偏移的新變換炫惩。 但在下面的示例中,我們將自己創(chuàng)建偏移變換阿浓。 注意使用加法運(yùn)算符:

offset = transforms.ScaledTranslation(dx, dy,
         fig.dpi_scale_trans)
shadow_transform = ax.transData + offset

這里顯示了他嚷,可以使用加法運(yùn)算符將變換鏈起來(lái)。 該代碼表示:首先應(yīng)用數(shù)據(jù)變換ax.transData芭毙,然后由dxdy點(diǎn)翻譯數(shù)據(jù)筋蓖。 在排版中,一個(gè)點(diǎn)是 1/72 英寸退敦,通過(guò)以點(diǎn)為單位指定偏移粘咖,你的圖形看起來(lái)是一樣的,無(wú)論所保存的 dpi 分辨率侈百。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.transforms as transforms

fig = plt.figure()
ax = fig.add_subplot(111)

# make a simple sine wave
x = np.arange(0., 2., 0.01)
y = np.sin(2*np.pi*x)
line, = ax.plot(x, y, lw=3, color='blue')

# shift the object over 2 points, and down 2 points
dx, dy = 2/72., -2/72.
offset = transforms.ScaledTranslation(dx, dy,
  fig.dpi_scale_trans)
shadow_transform = ax.transData + offset

# now plot the same data with our offset transform;
# use the zorder to make sure we are below the line
ax.plot(x, y, lw=3, color='gray',
  transform=shadow_transform,
  zorder=0.5*line.get_zorder())

ax.set_title('creating a shadow effect with an offset transform')
plt.show()

變換流水線

我們?cè)诒窘坛讨幸恢笔褂玫?code>ax.transData變換是三種不同變換的組合瓮下,它們構(gòu)成從數(shù)據(jù)到顯示坐標(biāo)的變換流水線。 Michael Droettboom 實(shí)現(xiàn)了變換框架钝域,提供了一個(gè)干凈的 API讽坏,它隔離了在極坐標(biāo)和對(duì)數(shù)坐標(biāo)圖中發(fā)生的非線性投影和尺度,以及在平移和縮放時(shí)發(fā)生的線性仿射變換例证。 這里有一個(gè)效率問(wèn)題路呜,因?yàn)槟憧梢云揭坪头糯竽愕妮S域,它會(huì)影響仿射變換战虏,但你可能不需要計(jì)算潛在的昂貴的非線性比例或簡(jiǎn)單的導(dǎo)航事件的投影拣宰。 也可以將仿射變換矩陣相乘在一起党涕,然后在一步之中將它們應(yīng)用于坐標(biāo)烦感。 這對(duì)所有可能的變換不都是有效的。

這里是在ax.transData實(shí)例在基本可分離的Axes類中的定義方式膛堤。

self.transData = self.transScale + (self.transLimits + self.transAxes)

我們已經(jīng)在Axes坐標(biāo)中引入了上面的transAxes實(shí)例手趣,它將軸或子圖邊界框的(0,0)(1,1)角映射到顯示空間,所以讓我們看看這兩個(gè)部分绿渣。

self.transLimits是從數(shù)據(jù)到軸域坐標(biāo)的變換; 也就是說(shuō)朝群,它將你的視圖xlimylim映射到軸域單位空間(然后transAxes將該單位空間用于顯示空間)。 我們可以在這里看到這一點(diǎn):

In [80]: ax = subplot(111)

In [81]: ax.set_xlim(0, 10)
Out[81]: (0, 10)

In [82]: ax.set_ylim(-1,1)
Out[82]: (-1, 1)

In [84]: ax.transLimits.transform((0,-1))
Out[84]: array([ 0.,  0.])

In [85]: ax.transLimits.transform((10,-1))
Out[85]: array([ 1.,  0.])

In [86]: ax.transLimits.transform((10,1))
Out[86]: array([ 1.,  1.])

In [87]: ax.transLimits.transform((5,0))
Out[87]: array([ 0.5,  0.5])

而且我們可以使用相同的反轉(zhuǎn)變換中符,從軸域單位坐標(biāo)變換回?cái)?shù)據(jù)坐標(biāo)姜胖。

In [90]: inv.transform((0.25, 0.25))
Out[90]: array([ 2.5, -0.5])

最后一個(gè)是self.transScale屬性,它負(fù)責(zé)數(shù)據(jù)的可選非線性縮放淀散,例如對(duì)數(shù)軸域右莱。 當(dāng)Axes初始化時(shí),這只是設(shè)置為恒等變換档插,因?yàn)榛镜?matplotlib 軸域具有線性縮放慢蜓,但是當(dāng)你調(diào)用對(duì)數(shù)縮放函數(shù)如semilogx()或使用set_xscale顯式設(shè)置為對(duì)數(shù)時(shí),ax.transScale屬性為處理非線性投影而設(shè)置郭膛。 縮放變換是相應(yīng)xaxisyaxisAxis實(shí)例的屬性晨抡。 例如,當(dāng)調(diào)用ax.set_xscale('log')時(shí)则剃,xaxis會(huì)將其縮放更新為matplotlib.scale.LogScale實(shí)例耘柱。

對(duì)于不可分離的軸域,PolarAxes棍现,還有一個(gè)要考慮的部分帆谍,投影變換。 matplotlib.projections.polar.PolarAxestransData類似于典型的可分離 matplotlib 軸域轴咱,帶有一個(gè)額外的部分汛蝙,transProjection

self.transData = self.transScale + self.transProjection + \
    (self.transProjectionAffine + self.transAxes)

transProjection將來(lái)自空間的投影,例如朴肺,地圖數(shù)據(jù)的緯度和經(jīng)度窖剑,或極坐標(biāo)數(shù)據(jù)的半徑和極角,處理為可分離的笛卡爾坐標(biāo)系戈稿。 在matplotlib.projections包中有幾個(gè)投影示例西土,深入了解的最好方法是打開這些包的源代碼,看看如何自己制作它鞍盗,因?yàn)?matplotlib 支持可擴(kuò)展的軸域和投影需了。 Michael Droettboom 提供了一個(gè)創(chuàng)建一個(gè)錘投影軸域的很好的教程示例;請(qǐng)參閱 api 示例代碼:custom_projection_example.py般甲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肋乍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子敷存,更是在濱河造成了極大的恐慌墓造,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異觅闽,居然都是意外死亡帝雇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門蛉拙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尸闸,“玉大人,你說(shuō)我怎么就攤上這事孕锄∈也妫” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵硫惕,是天一觀的道長(zhǎng)茧痕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)恼除,這世上最難降的妖魔是什么踪旷? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮豁辉,結(jié)果婚禮上令野,老公的妹妹穿的比我還像新娘。我一直安慰自己徽级,他們只是感情好气破,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著餐抢,像睡著了一般现使。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旷痕,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天碳锈,我揣著相機(jī)與錄音,去河邊找鬼欺抗。 笑死售碳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绞呈。 我是一名探鬼主播贸人,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼佃声!你這毒婦竟也來(lái)了艺智?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秉溉,失蹤者是張志新(化名)和其女友劉穎力惯,沒(méi)想到半個(gè)月后碗誉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體召嘶,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡父晶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弄跌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甲喝。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖铛只,靈堂內(nèi)的尸體忽然破棺而出埠胖,到底是詐尸還是另有隱情,我是刑警寧澤淳玩,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布直撤,位于F島的核電站,受9級(jí)特大地震影響蜕着,放射性物質(zhì)發(fā)生泄漏谋竖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一承匣、第九天 我趴在偏房一處隱蔽的房頂上張望蓖乘。 院中可真熱鬧,春花似錦韧骗、人聲如沸嘉抒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)些侍。三九已至,卻和暖如春政模,著一層夾襖步出監(jiān)牢的瞬間娩梨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工览徒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狈定,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓习蓬,卻偏偏與公主長(zhǎng)得像纽什,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躲叼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容