import numpy as np
# 一維資料不用贅言
data_1d = np.array([0, 1, 2, 3])
# 二維資料作為 m 行 n 列的表格,例如 2 行 3 列
data_2d = np.arange(6).reshape(2, 3)
# 三維資料作為 k 層 m 行 n 列 的積木塊, 例如 2 層 3 行 4 列
data_3d = np.arange(24).reshape(2, 3, 4)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyADN1EjNkZmNyUTN2AzM0QzMwYmM0YWO5gDZhF2MyUDN2QDOyEzY28CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
檢查一個 ndarray 資料的次元和大小,分别用 ndim 和 shape 屬性。
>>> print(data_3d.ndim)
3
>>> print(data_3d.shape)
(2, 3, 4)
shape 是一個很關鍵的屬性,我是這樣把它和各個軸對應的:
shape: (2, 3, 4)
k, m, n
z, y, x
心法1: x, y, z 對應的shape元組是從右往左數的。
這是我的個人習慣,也符合主流的用法。
圖像資料的小誤會打開一幅 640 x 480 的圖像:
import matplotlib.pylab as plt
image = plt.imread("lena.jpg")
print(image.shape)
# --- 結果 ---
# (480, 640, 3)
# (y, x, c)
不是 640 x 480 嗎, 怎麼倒過來了?我寫代碼的時候在這裡總是犯迷糊。
在口頭表達中,我們先說寬640,再說高480,而在計算機中是先高(y) 後寬(x),注意了!
每個像素有三個顔色分量(color),是以這個次元放在了最右邊,可以了解,順序就是 (y, x, c)
抽象軸上的操作對于4維及更高次元的資料,無法在3維空間圖示。這個時候,就不要考慮形象思維了,直接按照規則做處理。
用 shape 屬性傳回的元組,從左到右,座标軸分别命名為 axis 0, axis 1, ...,請注意,現在是從左向右數,正好是這個元組的 index,在以後的運算中,都按此規定。
>>> print(data.shape)
(3, 3, 2, 5)
# axis 0: 3
# axis 1: 3
# axis 2: 2
# axis 3: 5
心法2: 抽象座标軸順序從左向右。指定哪個軸,就隻在哪個軸向操作,其他軸不受影響。
排序(sorting)
data = np.array(np.arange(12))
np.random.shuffle(data)
data = data.reshape(3, 4)
print(data)
# [[10 8 3 2]
# [ 5 6 0 7]
# [11 4 9 1]]
print(np.sort(data, axis=0))
# [[ 5 4 0 1] | 小
# [10 6 3 2] | 到
# [11 8 9 7]] | 大
print(np.sort(data, axis=1))
# 小 到 大
# --------->
# [[ 2 3 8 10]
# [ 0 5 6 7]
# [ 1 4 9 11]]
如果你在心中能把抽象軸和 x, y, z 對應起來,則了解軸向排序很容易。
shape: (3, 4)
axis: 0, 1
AXIS: y, x
2. 求和、均值、方差、最大、最小、累加、累乘
這幾個函數調用,一般會指定軸向,注意心法2
sum,mean,std,var,min,max 會導緻這個軸被壓扁,縮減為一個數值
data = np.arange(24).reshape(2, 3, 4)
# [[[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
#
# [[12 13 14 15]
# [16 17 18 19]
# [20 21 22 23]]]
print( np.sum(data, axis=0) )
# 0軸被sum壓扁,1軸2軸不變
# [[12 14 16 18]
# [20 22 24 26]
# [28 30 32 34]]
print( np.sum(data, axis=1) )
# 1軸被sum壓扁,0軸2軸不變
# [[12 15 18 21]
# [48 51 54 57]]
cumsum,cumprod 不縮減軸向,隻在指定軸向操作,請讀者自己試驗。
3. 索引和切片(indexing and slicing)
心法3: 在索引中出現冒号(:),則本軸繼續存在,如果隻是一個數值,則本軸消失。
例如,像 :, :1, 1: 這樣的索引,保留此軸, data[:, :1, 2:] 中,三個軸都保留。 data[1, 4, 2] 三個軸都消失,隻傳回一個數值。
data[1:2, 0:1, 0:1] 中,三個軸都保留,但隻有一個資料元素,很神奇吧。
print( data )
print( data[0, :, :] )
# axis 0,即 z 軸,是數值,則 z 軸消失,切了一片 x-y
# [[ 0 1 2 3]
print( data[0, 1, 2] )
# 所有軸都消失,隻傳回一個标量資料
# 6
print( data[0:1, 1:2, 2:3] )
# 傳回三維資料,雖然隻有一個元素
# [[[6]]]
如何檢視 ndarray 的次元呢?可以通路 shape 屬性;如果列印出來了,那麼就數一數起始的中括号個數,比如 [[[6]]], 有三個 [,那麼就是三維數組。你記住了嗎?
4. 拼接(concatenating)
同樣遵循心法2,指定哪個軸,就在哪個軸向拼接:
data = np.arange(4).reshape(2, 2)
print( np.concatenate([data, data], axis=0) )
# 在軸向 0 拼接,即 y 方向
# [[0 1]
# [2 3]
# [0 1]
# [2 3]]
print( np.concatenate([data, data], axis=1) )
# 在軸向 1 拼接,即 x 方向
# [[0 1 0 1]
# [2 3 2 3]]
reshape 之迷亂你有沒有這個困惑:在 reshape 之後,資料在各個軸上是如何重新配置設定的?
搞清楚 ndarray 的資料在記憶體裡的存放方式,以及各個次元的通路方式,reshape 困惑就迎刃而解了。
心法4: ndarray 的資料在記憶體裡以一維線性存放,reshape 前後,資料沒有變化,隻是通路方式變了而已。
資料優先填充 X 軸向,其次 Y 軸,其次 Z 軸 。。。
有 C 語言基礎的,很容易了解 ndarray 的實作,就是 C 中的多元數組而已。
int data[2][3][4];
int data[4][6];
總結就說這麼多,看了本文請親自動手寫代碼體驗一下。掌握此心法,可以縱橫 numpy 世界而無大礙。
心法3: 在索引中出現冒号(:),則結果中本軸繼續存在,如果隻是一個數值,則本軸消失。
原文釋出時間為:2018-09-20
本文作者:曲奇
本文來自雲栖社群合作夥伴“
磐創AI”,了解相關資訊可以關注“
”。