NumPy 與海龜繪圖(一)


Python 內建的 turtle 模組,提供了海龜繪圖的實作,例如,以下這個簡單的程式,可以畫出星狀圖:

from turtle import *

leng = 200
a = 170
n = 37

speed(0)
for _ in range(n):
    forward(leng) # 前進
    left(a)       # 轉向(角度)

done() # 繪圖完成(不關閉視窗)

執行結果如下:

NumPy 與海龜繪圖(一)

這又跟 NumPy 有什麼關係呢?使用 NumPy 其實是種重新看待需求的過程,將一切都看成是資料的處理,使用 NumPy 來實現海龜繪圖,可以很好地示範出這個看待需求的過程。

首先,試著用 Matplotlib 來製出相同的圖案吧!這只要線圖就可以了,當然,你需要提供線的每個座標點,這可以使用 turtle 模組的 pos 函式,用來取得海龜的位置:

from turtle import *
import matplotlib.pyplot as plt

leng = 200
a = 170
n = 37

speed(0)
coords = []
for _ in range(n):
    coords.append(pos())
    forward(leng)
    left(a)

x = [coord[0] for coord in coords]
y = [coord[1] for coord in coords]

plt.gca().set_aspect(1)
plt.plot(x, y)  
plt.show()

程式中使用 coords 收集了海龜繪圖迭代過程的座標點,這可以畫出以下的圖案:

NumPy 與海龜繪圖(一)

接著來用 NumPy 實現,基本出發點就是,可以不用迴圈就不用,不使用迴圈的意義其實在於,儘量透過 Universal 函式來處理資料,為了能透過 Universal 函式,你得有一筆資料,這該是什麼資料呢?

觀察上面的程式,你迭代了 n 次,每次的迭代會取得一次海龜座標,也就是說,你是將 0 到 n - 1 轉換為海龜座標,因此一開始的資料就只是:

step = np.arange(n)

有了初始的資料,接下來是將 0 到 n - 1 轉換為海龜座標:

def forward_left(_, leng, a):
    forward(leng)
    left(a)
    return np.array(pos())
forward_left = np.frompyfunc(forward_left, 3, 1)

coord = np.array(forward_left(step, leng, a).tolist())

x = np_pos[:,0] 
y = np_pos[:,1] 

forward_leftstep 中的每個元素,將之轉換為 pos 取得的座標,最後得到的結果,重組為 NumPy 陣列,這是為了後續可以透過索引範圍取出 xy,將以上片段組合起來:

from turtle import *
import numpy as np
import matplotlib.pyplot as plt

def forward_left(_, leng, a):
    forward(leng)
    left(a)
    return np.array(pos())
forward_left = np.frompyfunc(forward_left, 3, 1)

leng = 200
a = 170
n = 37

speed(0)

step = np.arange(n)
coord = forward_left(step, leng, a)
np_pos = np.array(coord.tolist())

x = np_pos[:,0] 
y = np_pos[:,1] 

plt.gca().set_aspect(1)
plt.plot(x, y)  
plt.show()

執行結果與上一張圖是相同的,如果 forward_left 不是你實作的,你只要看成是個黑箱就可以了,當它是個傳入引數就傳回值的函式就可以了。

只不過,forward_left 中使用了 forwardleftpos,其實是有點作弊的方式,前兩者是有副作用的函式,改變了內部海龜的狀態,而且實際上也沒用到 step 每次傳入的元素值,這些看來都…很不 NumPy!

很不 NumPy 的意思是,forwardleftpos 看來只是指令,不像是在做資料處理與轉換,如果想更 NumPy 一些,就得知道海龜繪圖的原理,進一步將原理看待為資料處理的過程,這下篇文件再來談…