用matplotlib畫正弦函數sin的圖形

2023/10/15閱讀時間約 9 分鐘

用matplotlib畫正弦函數sin的圖形會有多難呢?應該是挺簡單的。不過,要畫得漂亮讓人滿意,還是非得費一番功夫調整不可。

要畫sin的圖形,就橫軸取一些點,算出對應的縱軸值,也就是y=sin x,然後用plot(x, y)畫出來就可以了。程式如下:

import math
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

xmin = -7
xmax = 10
n = int((xmax-xmin)/0.1)
x = [xmin+0.1*i for i in range(n)]
y = [math.sin(x[i]) for i in range(n)]

ax.set_xlabel("x")
ax.set_ylabel("y=sinx")

ax.plot(x, y)

plt.show()

畫出來的圖長這樣:

raw-image

這張圖的長相和通常看到的sin圖形長相,可說是天差地遠。通常看到的sin圖形長這樣:

raw-image

之所以會有這麼大的差別,是因為畫第一張圖的時候,單純就只是用matplotlib預設的設定,沒有再進行調整。要想畫出第二張圖的樣子,就得針對各個元件,如座標軸的標題、刻度等,進行調整。這其實沒什麼太大的學問,甚至可以說是有點無聊,只要有辦法查到各個元件設定的方法,就可以畫出想要的樣子。

用matplotlib畫圖的時候,可以設定的地方很多,身為腦容量低下的人,實在記不了那麼多,事實上也不需要去記,反正都查得到。不過,比較好的做法,還是把畫過的圖都保存起來,然後詳細的寫下是怎麼畫出來的。這樣當以後需要時,就可以參考,可以大大的節省查資料的時間。

現在就來把第一張圖調整成第二張圖的樣子。

首先來調整座標軸。因為matplotlib共有上、下、左、右四個座標軸,而sin的圖形中只有兩個座標軸,所以必須要關掉兩個。要關掉座標軸,可以這樣寫

ax.spines[["top", "right"]].set_visible(False)

這樣上、右兩個座標軸就會被隱藏而看不到。剩下左、下兩個座標軸,要把它們的交叉點從左下角挪到數據點(0, 0)的位置,可以這樣設定:

ax.spines[["bottom", "left"]].set_position(("data", 0))

再來就是調整刻度線的長相。原本的刻度線,是在座標軸某一邊的短線,現在要改成穿過座標軸的短線。設定的方式很簡單,這樣寫就可以了:

ax.xaxis.set_tick_params(direction="inout")
ax.yaxis.set_tick_params(direction="inout")

接下來是數據的顯示範圍。雖然sin的值是在-1至1之間,但是要讓圖好看一些,不會覺得太擁擠,上下應該要多留點空間,所以縱軸的顯示範圍就設定成-1.5至1.5間。至於橫軸的範圍,用數據點的最大和最小值來設定就可以了。設定的方式如下:

ax.set_xlim([xmin, xmax])
ax.set_ylim([-1.5, 1.5])

刻度線旁邊那個數字,有人叫「刻度值」,不過英文是「tick label」,所以也有叫「刻度標籤」、「刻度文字」的。這就是在查詢使用方法時讓人頭大的地方,如果不知道英文的說法,只用自己習慣的中文說法來查,往往會事倍功半。接下來,就來改「刻度標籤」。

要把刻度標籤改成想要的樣子,第一步當然就是要告訴matplotlib,這些標籤是哪些數值的標籤。以橫軸來說,現在要把標籤放在π的整數倍上,所以要這樣設定:

ax.set_xticks([-2*math.pi, -math.pi, 0, math.pi, 2*math.pi])

當然啦,那個list裡頭的東西,也可以用comprehension的方式來寫,反正只要能把要放上標籤的數值放在裡頭就行。

接下來就是對應的標籤內容。標籤的內容也是放在list裡頭,是字串。因為現在要顯示的文字有π,所以就用raw string搭配TeX來寫:

labels = [r"$\qquad -2\pi$", r"$\qquad\quad -\pi$ ", r"$\quad\,\,\, 0$", r"$\pi\quad$ ", r"$\qquad 2\pi$"]

這裡頭用了一大堆TeX的「\quad」、「\qquad」、「\ ,」,還有單純的空格,主要的目的是調整標籤的位置。因為sin的曲線會穿過橫軸,如果不調整標籤的位置,曲線和標籤就會疊在一起,醜醜的。那為什麼不只單純用Tex就好?在TeX中有不同寬度的空格可以用,應該不需要用到單純的空格了啊,這樣會搞得人家好亂!唉!這也是沒辦法的事啊!那個有用到單純空格的地方,都是不管塞了多少TeX的空格,那個標籤就是不動如山,說什麼都不肯再挪動一下位置的釘子戶,最後只能靠單純的空格才肯乖乖聽話。

寫好標籤文字之後,就可以把它設定到橫軸了。設定的時候,順便調整了文字的大小和Tex的字體。預設的大小和字體,實在是不優。

ax.set_xticklabels(labels, fontsize=14, math_fontfamily="cm")

縱軸的刻度標籤設定方式也是類似的做法:

ax.set_yticks([-1, 1])
ax.set_yticklabels([r"$-1$", r"$1$"], fontsize=14, math_fontfamily="cm")

最後要調整的,是座標軸的標籤。先來看橫軸的標籤要怎麼調整。

既然座標軸標籤也是標籤,那在調整刻度標籤文字時所用的招式,也就順理成章可以拿來調整座標軸標籤文字。調整方式如下:

ax.set_xlabel(r"$x$", fontsize=18, math_fontfamily="cm")

標籤文字調整好了,再來就是標籤放置的位置。橫軸標籤預設的位置是在座標軸下方,但是這樣容易和刻度標籤夾雜在一起,造成辨識上的困難。所以,把它放在橫軸右邊,會是個比較好的做法。調整的方式很簡單,直接指定放置位置就好了:

x.xaxis.set_label_coords(1.03, 0.535)

這裡要特別注意的是,這段程式中,那個放置位置(1.03, 0.535)所代表的意思。刮號中第一個數字,代表的是橫軸長度的1.03倍;第二個數字,代表的是縱軸長度的0.535倍。這也就是說,衡量座標軸標籤位置的單位,是座標軸的長度,而不是像素、公分、公釐等這些東西。所以,不管座標軸實際上有多長,在設定座標軸標籤位置時,它就是1。因此,座標軸標籤位置(1, 1)所代表的,是在橫軸和縱軸末端的那個位置,而如果數字超過1,就代表會放在座標軸外面。

另一個要注意的地方是,座標軸標籤位置的原點,是設定在圖的左下角。所以,把座標軸標籤放在(0, 0),就相當於是把它放在左下角。那如果是放在(1, 1)呢?那當然就是右上角了。

除了上述兩點之外,還有一個地方要特別特別特別注意的,那就是設定的順序。一開始的時候,是先設定座標軸標籤的位置,然後再調整文字內容。結果試了老半天,不管怎麼調整座標軸標籤位置的數字,就是沒辦法放到指定的位置上。後來靈光一閃,把順序對調,先設定文字,然後設定位置,那個標籤才乖乖到定位上。先前的許多設定,誰先誰後都不會影響設定結果,就只有這個地方要特別當心,順序搞錯了,就會做不出來。

為什麼會想到要把設定的先後順序對調?說實在的,還真是不知道。或許這就是所謂的「靈感」。不過後來想想,總覺得應該是以前在處理什麼事情時,也碰過這種很計較先後順序的狀況,所以才會有這種靈光乍現的情形出現。沒有以前累積的經驗,應該也生不出靈感來。

縱軸標籤的設定,方式和橫軸差不多,唯一的不同點,是文字的方向。縱軸標籤的文字,預設是旋轉90度,就像第一張圖顯示的那樣。要顯示成第二張圖那樣,必須把旋轉角度設定為0度。當然啦,跟橫軸標籤一樣,設定的順序也很重要,要先設定文字,然後再設定放置位置。設定程式如下:

ax.set_ylabel(r"$y=\sin x$", rotation=0, fontsize=18, math_fontfamily="cm")
ax.yaxis.set_label_coords(0.52, 1)

整個設定方式就是這樣。至於像是設定橫軸標籤文字時,會有TeX空格沒作用的現象,還有設定座標軸標籤時,設定順序會影響結果等問題,就不去深究原因了,因為那看起來像是會涉及到matplotlib內部的運作方式問題。無論如何,圖畫得出來最重要,那些問題的答案,如果有緣,遲早會見到的。






13會員
86內容數
寫點東西自娛娛人
留言0
查看全部
發表第一個留言支持創作者!