1. 程式人生 > >從零開始學習PYTHON3講義(十二)畫一顆心送給你

從零開始學習PYTHON3講義(十二)畫一顆心送給你



(內容需要,本講使用了大量線上公式,如果因為轉帖網站不支援公式無法顯示的情況,歡迎訪問原始部落格。)

《從零開始PYTHON3》第十二講

上一節課我們主要講解了數值計算和符號計算。數值計算的結果,很常用的目的之一就是用於繪製圖像,從影象中尋找公式的更多內在規律。

Python科學繪圖

科學繪圖是計算機圖形學的一個重要分支。同其它繪圖方式相比,更簡單易用,能讓使用者把工作的主要精力集註在公式和演算法上而不是繪圖本身。此外科學繪圖的工具包普遍精度更高,資料、圖的對應關係準確,從而保證基於圖的研究工作順利進行。最後,科技繪圖一般都使用同數學相同的座標系,避免了不必要的資料轉換。

常用的一個高質量科學繪圖包是第三方出品的MatplotLib,安裝方式跟其它擴充套件庫相同(這裡只示例基於Windows環境的安裝):

#首先使用管理員模式執行cmd命令列,隨後在命令列執行:
pip install matplotlib  #某些系統需要使用pip3

所有的繪圖,無論是基於顯示器還是印表機(繪圖儀),都可以看做一個寬*高的二維矩陣。這就產生了一個座標系統,那麼矩陣中的任意一個點,就會有座標(x,y),x代表橫方向座標,y軸代表縱向座標。
三維的遊戲、VR等應用,在計算的整個過程中使用的是x、y、z三維座標體系,但最後繪製到螢幕上的時候,還是會根據透視縮放的對映關係,將影象投影到二維矩陣中。
那麼一副影象,就是有很多個點組成的,每個點都有其x,y的座標。如果組成一個列表,通常是[[x1,y1],[x2,y2],[x3,y3]]這樣的形式,比如剛才這個例子就代表了3個點的列表。如果是一條線,則可以用[[x1,y1],[x2,y2]]兩個點來描述,這兩個點就是一條線的兩個端點座標。
在我們今天講的數學繪圖中,通常使用的是另外一種座標表示方法。科學繪圖會使用x座標點的列表和y座標點的列表,兩個列表來描述一組點。比如:[x1,x2,x3],[y1,y2,y3]。
即便只有一個點,也要把x座標和y座標分開到兩個列表中去,此時列表就成為只有一個元素的列表,[x],[y]。

使用這種資料結構的原因是這樣的,比如我們試圖繪製函式:
$$
y = f(x)
$$
的影象。通常的情況我們首先是有一列的x值,那麼通過函式計算之後,組成的就是一個結果y的列表。
因此事實上,為函式繪製圖像,我們不大可能跟傳統影象一樣,上來就有一個完整的座標點列表,而只能是x、y兩個列表。列表中相同下標的值,是對應的x、y座標,而y座標的值,來自於上面所示函式對於x列表的計算結果。以一個3座標的列表為例,大致是[x1,x2,x3],[y1,y2,y3]這樣的形式。

言語總是枯燥無味的,讓我們來看一段程式碼,用於繪製一副正弦函式圖:

#繪製正弦曲線

#引入數值計算庫,改為短名稱
import numpy as np
#引入繪相簿,改為短名稱
import matplotlib.pyplot as plt

#生成一個由-4到4、均分為200個元素的列表
x = np.linspace(-4, 4, 200) 
#計算當x取值範圍-4至4時所有的sin函式解
f = np.sin(x)

#繪製
plt.plot(x, f, 'red') 

#將繪製好的圖顯示出來
plt.show()

有幾條命令再更詳細的解釋一下:

  • np.linspace函式我們在講數值計算的時候學過,是生成一個200個元素的列表,這個列表是numpy庫的列表型別,跟python內建的列表是基本相容的,但並不是同一種類型。這200個元素均分了從-4到+4的值範圍,包含了-4/+4本身。
  • np.sin(x),看起來跟內建的math.sin(x)很像,事實上當x是一個數字變數的時候,兩者完全相同。但在這裡,x是一個列表,包含200個元素。那兩者就完全不同了。內建的math.sin一次呼叫只能處理一個數字。np.sin是一次處理整個陣列。因此呼叫完成後,結果f中是包含了200個值,每個相同下標的值,是對應x列表中對應下標值的正弦函式結果值。所以f在這裡實際就是y座標的值。
  • 終於看到了plt.plot函式,裡面有三個引數,x是x座標列表,本例中包含了200個元素,f實際是y軸座標列表,也包含了200個元素,最後的'red'表示使用紅色繪製。此函式在繪製這個陣列的時候,每兩個點之間,預設會使用直線連線上,從而讓整體上形成一條平滑的曲線。
  • 最後plt.show()是把繪製好的圖形顯示出來,此語句之後的繪圖將被忽略掉,所以請確保這一條語句是所有plt呼叫的最後一句。

繪製的結果請看下圖。

Figure_1
乾淨漂亮的正弦曲線出現了!

Python的學習一定要多動手練習,所以請自己也來來試試繪製過程。比如改變引數範圍從-10到+10,比如把200個列表元素改成只有10個,看看是什麼效果?

我們繼續為這個畫面做一些輔助性的補充。請把下面的程式碼加到plt.show()語句之前:

#為某條曲線增加註釋文字
plt.text(1.3, 0.9, 'sin(x)',color='b')

#為整個圖增加標題
plt.title('y=f(x)', fontsize=16)

只加了兩行程式碼。第一行程式碼是在畫面中增加註釋性的文字,其實只有一條曲線意義並不大。但多條曲線,如果沒有註釋的文字,看起來就很困難了。第一行程式碼裡面,頭兩個引數是座標,表示註釋文字出現的起始位置,這個座標的單位就是正弦曲線的數學值,這一點,在其它繪圖系統中都是要做很複雜的變換才能搞的定,在這裡直接用就好了;第三個引數是顯示的文字;第四個引數“b”代表blue,意思是用藍色顯示註釋文字,這是想告訴你,其實前面的“red”,一樣可以簡寫成“r”。
第二行程式碼視為整個畫面增加一個標題,標題會顯示在圖片的上方中間的位置,第二個引數給了個字型尺寸,其實第二個引數可以沒有,那樣的話會自動給出一個適合標題的尺寸,一般都適於大多數情況使用。
有一點需要注意,畫面中的註釋文字,和標題,都不能直接支援中文。通常國外的軟體,或多或少支援其它語言文字都有小問題。解決也並不難,就是需要指定使用中文的字型,這個超出了本課程的範圍,有興趣瞭解的可以參考教學資源包中的demo.py程式,注意字型的設定,是跟作業系統相關的,不具備可移植性。
Figure_2
上圖是增加了註釋文字和標題之後的效果。你可能注意到了,圖片視窗中有選單是可以直接儲存圖片的。這樣的圖片直接引用到論文中效果一流。

科學繪相簿我們使用了已經內建的正弦函式作為示例開始,這樣為了降低使用的難度,專注解釋繪圖操作的機理。

在實際應用中,要繪製的通常都是很複雜的數學公式,這時候前面講過的數學內容就用得上了。建議你自己定義一個函式,把複雜的公式,使用Python描述出來。注意因為要繪圖,所以通常都是需要使用數值計算庫而不是符號計算庫。
下面我們舉一個例子,簡單起見,我們只使用最簡單的直線公式(僅為示例,單純畫直線有很多更好的辦法):
$$
y=ax+b
$$

#繪製直線

#引入數值計算庫
import numpy as np
#引入繪相簿
import matplotlib.pyplot as plt

#定義一個直線函式
def line(x,a,b):
    return a*x+b

#我們用於示例,定義的函式沒有計算陣列的功能
x1=-4
y1=line(x1,0.2,0)
x2=4
y2=line(x2,0.2,0)

#繪製直線,只需要兩個端點
plt.plot([x1,x2],[y1,y2],'r')

#增加註釋文字
plt.text(-3.9, -0.8, '0.2*x',color='r')
#為整個圖增加標題
plt.title('y=ax+b', fontsize=16)

#將繪製好的圖顯示出來
plt.show()

整體的程式結構跟上面畫正弦函式的幾乎相同。區別有兩點,1是使用自定義的函式替代np.sin函式;2是計算的時候,因為我們自己定義的函式不支援陣列運算,所以自己要逐次計算每個點。好在是直線,只要計算兩個點就成了。
此外有一點要說明的,我們前面其實提到過,plt.plot函式,會自動連線每個點,使得整體成為連貫的線條,所以這個繪圖示例的結果,我們給出兩個點,最終得到了一條線。下面是執行結果:
Figure_3
一個小思考題,排除這個直線函式。如果我們自己定義的函式式曲線,那肯定還是需要自己定義的Python函式,除了實現函式的計算,還要能實現陣列的計算比較合理,這應當如何做呢?最好能用自己定義的函式來說明一下,或者用虛擬碼來描述一下也可以接受。
科技繪圖部分的最後,推薦去官方網站看一看經典的“繪圖畫廊”,都是使用MatplotLab繪製的,都附有原始碼,而且程式還都不算長,相信會讓你很有收穫。多說一句,Python的內建函式和第三方函式總量浩如煙海,你看別人程式的時候肯定會碰到很多不理解的函式或者關鍵字,基本的語法我們都已經學過了,這些不明白的內容求助搜尋引擎,一定能讓你快速的找到答案。


海龜繪圖

今天一開始講的科學繪圖工具包非常強大。Python也內建了一套簡單易用的繪圖包,名字是海龜繪圖,庫名為:turtle,內建庫無需安裝,直接在程式一開始引用就可以了。

海龜繪圖是在上世紀90年代非常流行的一套兒童繪圖工具包,曾經風靡一時,但可惜在當今各種高階工具層出不窮的情況下,已經比較沒落了。
但我的感覺是對於初學者,還是有很多參考意義,所以下面的內容,著重瞭解和動手嘗試,對於具體的內容,有興趣當然可以學習,興趣不足的,練練手就好了,不要求你重點記憶。

海龜繪圖的基本理念是這樣:想象沙灘上有隻小海龜。它只會向前走、轉向等簡單的動作,它在沙灘上爬行所留下的軌跡就是繪製出的圖形(其實繪圖命令中還有擡起畫筆、放下畫筆等操作,有興趣的使用help(turtle)可以檢視完整幫助)。
這種模式很適合使用迴圈語句繪製螺旋線等規律幾何結構,如果設計得當,可以得到很多炫目的幾何圖形。我們來看幾個例子:

#海龜繪圖演示

#引入海龜繪相簿
import turtle

#建立一支筆(一隻海龜)
t = turtle.Pen()
for x in range(100):
    #向前走x步
    t.forward(x)
    #左轉90度
    t.left(90)
turtle.done()

上面就是在第一講中我們介紹過的圖形,繪製結果為:
turtle1

換一個演算法再看看:

#海龜繪圖演示

#引入海龜繪相簿
import turtle

#建立一支筆(一隻海龜)
t = turtle.Pen()
t.pencolor('red')
for x in range(100):
    #向前走x步
    t.forward(x)
    #左轉90度
    t.left(95)
turtle.done()

注意這個程式中有了設定畫筆顏色的語句。下面是繪製結果:
turtle2
除了設定畫筆的顏色,還可以設定畫筆所封閉的區域中的填充顏色,請看這個例子和執行的結果:

#引入繪相簿
from turtle import *

#設定筆的顏色和填充顏色
color('red', 'yellow')
#開始填充
begin_fill()
while True:
    forward(200)
    left(170)
    #pos是當前海龜座標
    #abs(pos())表示判斷如果
    # 海龜回到原點附近則停止
    if abs(pos()) < 1:
        break
#結束填充
end_fill()
done()

turtle3

這些例子中,基本都使用了迴圈結構,希望你還記得迴圈的語法、邊界條件、迴圈體這些概念,並以此讀懂這些例子的原理。

在上面最後的例子中,有一些需要補充的。pos()返回當前小海龜的位置(x,y),是以數學複數的方式返回的。複數不在我們學習計劃內,所以這部分內容瞭解即可,大致原理:abs(pos())實際是計算sqrt(x * 2+y * 2),也既當前座標到原點的直線距離。所以上面例子中,使用這個方法來判斷小海龜畫筆,回到了原點附近,表示整個曲線繪製完整、並且頭尾連貫、閉合了。因為只有閉合的區域,才可能填充顏色。

本節課總體上都是很輕鬆愉悅的。相信你經過前面艱苦的學習,已經嚐到了收獲的滋味,我們也到了收穫的季節。娛樂起見,我們再提供一個網上轉帖的海龜繪圖程式,用於繪製小豬佩奇(程式比較長,請直接參考paige.py,此處感謝原作者):
turtle4

小豬佩奇的程式中,使用了很多海龜繪圖的縮寫功能,比如forward向前走命令可以縮寫為fd,向左轉命令left可以縮寫為lt。這些在help(turtle)文件中都能查到。縮寫只是為了編寫程式時候的方便,功能是完全一樣的。

除了前面講過的規則幾何圖案,想繪製這種定製的圖形,通常都需要使用“座標紙”,現在除了上淘寶,估計平常的商店都買不到了。然後把想繪製的圖形描繪在座標紙上,從而獲得每個點的準確座標。接下來就可以利用這些座標,利用擡筆、移動座標、落筆的功能,繪製定製的圖形了。
不過可惜啊,現在有了Photoshop之類的軟體,像座標紙描格子的過程,都足以在螢幕上繪製完成了,完全不需要程式設計的知識。這也是海龜繪圖逐漸沒落的原因。


練習時間

1.請繪製下面函式的影象:
$$
y=\sqrt{1-(|x|-1)^2},\arccos(1-|x|)-\pi
$$
這裡解釋一下:

  • y是兩個公式,用逗號隔開,表示取值是兩個,程式中應當分別計算,得到兩組值
  • x取值空間建議:-2至2
  • 根號函式:numpy.sqrt(),絕對值:numpy.fabs()
  • 平方:numpy.square(),同**2的區別,後者只計算一個值,前者計算整個列表
  • 其它函式:numpy.arccos,常量π:numpy.pi

2.海龜繪圖的練習,其實在第一講的時候我們練習過一些了,現在學習了這麼多,再來試試吧
A.修改前面例程的簡單引數,構建有趣的規則幾何圖形
B.開動腦筋,重新程式設計,繪製一副更有創意的圖形


本講小結

  • 圖形、影象是電腦科學中重要的組成部分
  • 科技繪圖用途廣泛,也是理工學習中必須用到的內容
  • 海龜繪圖簡單有趣,能顯示繪圖過程,適合簡單創意性的場合

練習答案

1.課程中的思考題,在自定義函式中,應當使用迴圈,遍歷引數的所有元素,逐個代入數學公式中計算,得到的結果逐個加入已經預先定義好的空列表中,最終返回這個完整的列表。程式程式碼略。

2.請參考ex1.py程式

3.海龜繪圖練習略