預備課:向量、矩陣和廣播

套利課程的從零開始短課。把一列數看作向量,把表格看作矩陣,用 numpy 對所有數一次完成同一個運算,以及什麼是廣播。程式碼可執行,附有輸出。

這是課程"做你自己的套利機器人"的輔助課。第 13 課用一行 numpy 檢查數百萬個貨幣回路。本頁用四個價格解釋這些詞和這個技巧。

直接的辦法:迴圈

有一種幣的四個價格,你想知道每一步變化了多少:當前價格除以上一個價格。大家寫的第一個版本是迴圈:

prices = [100.0, 101.5, 99.8, 102.0]
ret = [prices[i+1] / prices[i] for i in range(len(prices) - 1)]
print([round(r, 4) for r in ret])
[1.015, 0.9833, 1.022]

這能用。但掃描器變慢正是因為這個:Python 一次只走列表裡的一項。四個價格沒人會察覺,一百萬個就會察覺。

向量:當作一個整體來用的列表

向量就是一列數。numpy 庫把它存放在一塊記憶體裡,讓你用一個表達式對整個列表做運算:

import numpy as np
p = np.array(prices)
print(np.round(p[1:] / p[:-1], 4))
print(p.mean(), p.max(), p.argmax())
[1.015  0.9833 1.022 ]
100.825 102.0 3

p[1:] 表示"除第一個以外的所有價格",p[:-1] 表示"除最後一個以外的所有價格"。兩者相除,是把相同位置的一對數相除:101.5/100、99.8/101.5、102/99.8。結果和迴圈一樣,但沒有迴圈。mean 是平均值,max 是最大值,argmax 是最大值的位置(下標 3,價格 102)。

矩陣:數字表格

矩陣是有行有列的表格。貨幣之間的匯率表就是一個矩陣:第 i 行第 j 列表示一個單位的 i 能換到多少 j。

M = np.array([[1, 2, 3],
              [4, 5, 6]])
print(M.shape, M[1, 2], M[:, 0], M[0, :])
print(M * 10)
(2, 3) 6 [1 4] [1 2 3]
[[10 20 30]
 [40 50 60]]

來讀一讀。shape (2, 3) 表示 2 行 3 列。M[1, 2] 是第 1 行第 2 列(從 0 開始數),即 6。M[:, 0] 是整個第一列,M[0, :] 是整個第一行。M * 10 把每個格子都乘以 10。

廣播:小表格被拉伸成大表格

把兩個形狀不同的東西相加會怎樣?numpy 會把較小的那個複製拉伸,直到形狀一致。這就叫廣播。

print(M + np.array([100, 200, 300]))
print(M + np.array([[1], [2]]))
[[101 202 303]
 [104 205 306]]
[[2 3 4]
 [6 7 8]]

第一行裡,三個數的一行被向下複製到 M 的每一行。第二行裡,一列 [[1], [2]] 被向右複製:第一行加 1,第二行加 2。第 13 課用同樣的拉伸,把同一個表格的三種視角相乘,一步得到所有貨幣三元組構成的立方體。

快多少

import time
x = np.arange(1_000_000, dtype=float)
t = time.perf_counter(); s = 0.0
for v in x: s += v * 2
t1 = time.perf_counter() - t
t = time.perf_counter(); s2 = (x * 2).sum(); t2 = time.perf_counter() - t
print(s == s2, "loop %.3f s vs numpy %.4f s" % (t1, t2))
True loop 0.111 s vs numpy 0.0011 s

答案相同,在這臺機器上大約快 100 倍(你的數字會不同)。迴圈把時間花在對每一項的記賬上,而 numpy 只把整塊資料一次交給編譯好的程式碼。

你的練習

  1. 執行所有程式碼,把輸出和課程對比。
  2. 自己做一個 5 個價格的向量,用一行程式碼算出每一步的比值。手算檢查第一個。
  3. 建一個 3 乘 3 的數字表。列印它的第二列和最後一行。
  4. 把 np.array([[10], [20], [30]]) 加到你的表上。先在紙上預測結果,再檢查。
  5. 把 x 的大小改成 1000 萬。兩個耗時怎麼變?

下一步

現在可以讀第 13 課了。它的技巧正是上面的第 4 步,只不過用的是匯率表。