以下內(nèi)容主要學(xué)習(xí)自《利用Python進(jìn)行數(shù)據(jù)分析》
第4章 NumPy基礎(chǔ)(2)
NumPy是Numerical Python的簡(jiǎn)稱(chēng),它是目前Python數(shù)值計(jì)算中最為重要的基礎(chǔ)包。大多數(shù)計(jì)算包都提供了基于Numpy的科學(xué)函數(shù)功能,將NumPy的數(shù)組對(duì)象作為數(shù)據(jù)交換的通用語(yǔ)言。
本章將介紹NumPy數(shù)組的基礎(chǔ)操作。雖然深入理解NumPy對(duì)于大部分?jǐn)?shù)據(jù)分析應(yīng)用并不是必需的,但是精通基于數(shù)組的編程和思維是成為Python科學(xué)計(jì)算專(zhuān)家的第一步。

ndarray索引與切片
Numpy.ndarray的索引與切片是一個(gè)值得學(xué)習(xí)的話(huà)題。有很多方式可以讓你選中數(shù)據(jù)的子集或某個(gè)單一元素。
所謂“索引”,就是通過(guò)單一的索引號(hào),得到ndarray的部分?jǐn)?shù)據(jù);所謂“切片”,就是通過(guò)索引號(hào)段,得到ndarray的部分?jǐn)?shù)據(jù)。索引可以取得原數(shù)組降維的部分?jǐn)?shù)據(jù),而切片不會(huì)降低維度,而是得到一個(gè)維度相同的子集。
如果熟悉Python的列表操作,那么很容易掌握ndarray的索引與切片,因?yàn)檎Z(yǔ)法是相似的。
索引
通過(guò)索引獲得單一的元素
NumPy的索引語(yǔ)法與Python列表的索引語(yǔ)法是一樣的。如下是對(duì)一維數(shù)組進(jìn)行索引:
In[1]: arr = np.arange(10)
In[2]: arr
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In[3]: arr[5] #一維數(shù)組的索引
Out[3]: 5
如果要從二維數(shù)組(或多維數(shù)組)中取得單一元素,那么就要提供多個(gè)索引值。如下是對(duì)二維數(shù)組進(jìn)行索引:
In [4]: arr = arr.reshape(2, 5)
In [5]: arr
Out[5]:
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
In [6]: arr[1][3] # 二維數(shù)組的索引
Out[6]: 8
In [7]: arr[1, 3] # 盡管索引語(yǔ)法不同,但索引效果一樣
Out[7]: 8
通過(guò)索引獲得ndarray的子集
可以從二維(或多維)數(shù)組中,獲得ndarray的自己,方法是在多維數(shù)組中,如果省略后續(xù)索引值,返回的對(duì)象將是降低一個(gè)維度的數(shù)組。
# 生成一個(gè)維度為(2,3,4)的三維數(shù)組
In [1]: arr = np.arange(24).reshape(2, 3, 4)
In [2]: arr
Out[2]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
In [3]: arr[0] # 返回第1維、索引號(hào)為0的子集,返回的將是二維數(shù)組
Out[3]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [4]: arr[0][1] # 返回一維數(shù)組
Out[4]: array([4, 5, 6, 7])
切片
一維數(shù)組的切片
與Python列表類(lèi)似,NumPy數(shù)組可以通過(guò)相似的語(yǔ)法對(duì)數(shù)組進(jìn)行切片。
In[1]: arr = np.arange(10)
In[2]: arr
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In[3]: sub = arr[5:8] # 通過(guò)切片獲取部分?jǐn)?shù)據(jù)
In[4]: sub
Out[4]: array([5, 6, 7])
In [5]: sub.shape # sub是一個(gè)有三個(gè)元素的一維數(shù)組
Out[5]: (3,)
In[6]: sub[0:2] = 12 # 對(duì)切片賦值,就是對(duì)切片中的每一個(gè)元素賦值
In[7]: arr
Out[7]: array([ 0, 1, 2, 3, 4, 12, 12, 7, 8, 9])
注意,區(qū)別于Python的內(nèi)建列表,ndarray的切片是原數(shù)組的視圖,這意味著數(shù)據(jù)并不是被復(fù)制了,任何對(duì)于視圖的修改都會(huì)反映到原數(shù)組上。這是因?yàn)镹umPy被設(shè)計(jì)成適合處理非常大的數(shù)組,你可以想象如果NumPy持續(xù)復(fù)制數(shù)據(jù)會(huì)引起內(nèi)存和性能問(wèn)題。
如果你是NumPy新手,你可能對(duì)此感到驚訝,因?yàn)槠渌木幊陶Z(yǔ)言都是急切地復(fù)制數(shù)據(jù)。如果你堅(jiān)持要得到一份數(shù)組切片的拷貝,而不是一份視圖的話(huà),可以試用ndarray.copy()方法顯式地復(fù)制數(shù)據(jù)。
In [8]: sub = arr[0:3].copy() # 顯式地復(fù)制數(shù)據(jù)
In [9]: sub
Out[9]: array([0, 1, 2])
In [10]: sub[0:3] = 99
In [11]: sub
Out[11]: array([99, 99, 99])
# 通過(guò)復(fù)制的子集,不會(huì)影響原數(shù)組
In [12]: arr
Out[12]: array([ 0, 1, 2, 3, 4, 13, 13, 12, 8, 9])
多維數(shù)組的切片
In [1]: arr = np.arange(12).reshape(3,4)
In [2]: arr # 一個(gè)3X4的二維數(shù)組
Out[2]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
# 對(duì)二維數(shù)組進(jìn)行切片,得到一個(gè)子集
# 1:3表示從第2行開(kāi)始,取3-1=2行
# 2:4表示從第3列開(kāi)始,取4-2=2列
In [3]: arr[1:3, 2:4]
Out[3]:
array([[ 6, 7],
[10, 11]])
注意:索引可以取得原數(shù)組降維的部分?jǐn)?shù)據(jù),而切片不會(huì)降低維度,而是得到一個(gè)維度相同的子集。妥善地混合索引和切片,就可以得到低維度的切片。
In [4]: arr[1, 1:3] # 獲取第2行,2、3列數(shù)據(jù)
Out[4]: array([5, 6])
單獨(dú)一個(gè)冒號(hào)表示選擇整個(gè)軸上的數(shù)組,并且對(duì)原數(shù)組的切片賦值,那么原數(shù)組也會(huì)被賦值。這也是因?yàn)镹umPy被設(shè)計(jì)成適合處理非常大的數(shù)組,因此缺省是按地址引用。
In [1]: arr = np.arange(12).reshape(3,4)
In [2: arr
Out[2]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
# 獲取所有行、第1列的切片
In [3]: sub = arr[:, :1]
In [4]: sub
Out[4]:
array([[0],
[4],
[8]])
# 把切片中的所有元素賦值為0
In [5]: sub[:,:] = 0
# 對(duì)切片的賦值,會(huì)影響到原數(shù)組
In [6]: arr
Out[6]:
array([[ 0, 1, 2, 3],
[ 0, 5, 6, 7],
[ 0, 9, 10, 11]])
神奇索引
神奇索引這個(gè)詞很容易讓人誤解,它是NumPy的一個(gè)術(shù)語(yǔ)。簡(jiǎn)單來(lái)講,神奇索引的目的是用整數(shù)數(shù)組對(duì)ndarray數(shù)據(jù)進(jìn)行索引。
假設(shè)我們有一個(gè)8X4的數(shù)組:
In [1]: arr = np.empty((8,4))
In [2]: for i in range(8):
...: arr[i] = i+1
In [3]: arr
Out[3]:
array([[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.],
[4., 4., 4., 4.],
[5., 5., 5., 5.],
[6., 6., 6., 6.],
[7., 7., 7., 7.],
[8., 8., 8., 8.]])
如果我們要從如上的數(shù)組中,選擇第5、4、1、7行組成的子集,那么可以傳遞[4,3,0,6]來(lái)得到(注意數(shù)組下標(biāo)從0開(kāi)始)
In [4]: arr[[4, 3, 0, 6]]
Out[4]:
array([[5., 5., 5., 5.],
[4., 4., 4., 4.],
[1., 1., 1., 1.],
[7., 7., 7., 7.]])
如果使用負(fù)數(shù)索引,將從尾部進(jìn)行選擇:
In [5]: arr[[-3, -5, -7]]
Out[5]:
array([[6., 6., 6., 6.],
[4., 4., 4., 4.],
[2., 2., 2., 2.]])
注意上面的例子,原數(shù)組是二維的,但傳入了一個(gè)索引維度,所以是得到了降維后的子集。如果傳入兩個(gè)索引,將得到一維數(shù)組。
In [1]: arr = np.arange(12).reshape(3,4)
In [2]: arr
Out[2]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
# 選去第1行第2個(gè)元素、以及第3行第4個(gè)元素。
In [3]: arr[[0,2], [1, 3]]
Out[3]: array([ 1, 11])
請(qǐng)牢記:神奇索引與切片不同,它總是將數(shù)據(jù)復(fù)制到一個(gè)新的數(shù)組中。
布爾索引
假設(shè)我們有7X3的數(shù)據(jù),這7行數(shù)據(jù)數(shù)據(jù)屬于3個(gè)人,如下示例:
In [1]: names = np.array(['張三', '王五', '李四', '張三', '王五', '王五', '李四'])
# 生成一個(gè)7X3的數(shù)組,數(shù)組中的每個(gè)元素是0~10的隨機(jī)數(shù)
In [2]: data = np.random.randint(low=0, high=10, size=(7,3))
In [3]: data
Out[3]:
array([[4, 0, 2],
[8, 1, 5],
[3, 3, 1],
[4, 9, 3],
[3, 7, 3],
[7, 8, 4],
[5, 4, 7]])
用names數(shù)組與字符串'張三'比較會(huì)產(chǎn)生一個(gè)布爾值數(shù)組:
In [4]: names == '張三'
Out[4]: array([ True, False, False, True, False, False, False])
如果把這個(gè)布爾值數(shù)組當(dāng)作“神奇索引”,傳遞給data數(shù)組,會(huì)得到如下結(jié)果
In [5]: data[names == '張三']
Out[5]:
array([[4, 0, 2],
[4, 9, 3]])
如上,其效果等同于選擇出了所有“張三”的行數(shù)據(jù)。
ndarray排序
一維數(shù)組的排序
數(shù)組實(shí)例可以使用sort方法進(jìn)行排序。
In [1]: arr = np.random.randn(6)
In [2]: arr
Out[2]:
array([-1.51683294, 0.71207739, -1.91733282, -1.07901923, -0.35628366,
-0.52888226])
In [3]: arr.sort()
In [4]: arr
Out[4]:
array([-1.91733282, -1.51683294, -1.07901923, -0.52888226, -0.35628366,
0.71207739])
多維數(shù)組的排序
對(duì)于多維數(shù)組的排序,可以使用NumPy的頂層sort函數(shù),該函數(shù)可以接送axis參數(shù),以便按照某一個(gè)維度排序。
In [1]: arr = np.random.randint(-5, 5, (3,4))
In [2]: arr
Out[2]:
array([[ 2, 4, -4, -3],
[-1, -5, 2, 0],
[-3, -4, -3, -3]])
In [3]: np.sort(arr, axis=0)
Out[3]:
array([[-3, -5, -4, -3],
[-1, -4, -3, -3],
[ 2, 4, 2, 0]])
In [4]: np.sort(arr, axis=1)
Out[4]:
array([[-4, -3, 2, 4],
[-5, -1, 0, 2],
[-4, -3, -3, -3]])