天天看點

基礎 | numpy ndarray 之内功心法,了解高維操作!

多元資料的形象表示

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)

基礎 | numpy ndarray 之内功心法,了解高維操作!

檢查一個 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]]

基礎 | numpy ndarray 之内功心法,了解高維操作!

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 軸 。。。

基礎 | numpy ndarray 之内功心法,了解高維操作!

有 C 語言基礎的,很容易了解 ndarray 的實作,就是 C 中的多元數組而已。

int data[2][3][4];

int data[4][6];

總結

就說這麼多,看了本文請親自動手寫代碼體驗一下。掌握此心法,可以縱橫 numpy 世界而無大礙。

心法3: 在索引中出現冒号(:),則結果中本軸繼續存在,如果隻是一個數值,則本軸消失。

原文釋出時間為:2018-09-20

本文作者:曲奇

本文來自雲栖社群合作夥伴“

磐創AI

”,了解相關資訊可以關注“

”。