第04章 12/9NumPy基礎(chǔ):數(shù)組和矢量計算

資料來源:https://github.com/BrambleXu/pydata-notebook

NumPy(Numerical Python的簡稱)是Python數(shù)值計算最重要的基礎(chǔ)包侣诺。大多數(shù)提供科學(xué)計算的包都是用NumPy的數(shù)組作為構(gòu)建基礎(chǔ)扳还。

NumPy的部分功能如下:

  • ndarray系任,一個具有矢量算術(shù)運算和復(fù)雜廣播能力的快速且節(jié)省空間的多維數(shù)組骑素。
  • 用于對整組數(shù)據(jù)進行快速運算的標準數(shù)學(xué)函數(shù)(無需編寫循環(huán))芙扎。
  • 用于讀寫磁盤數(shù)據(jù)的工具以及用于操作內(nèi)存映射文件的工具梳庆。
  • 線性代數(shù)遭顶、隨機數(shù)生成以及傅里葉變換功能芜飘。
  • 用于集成由C、C++蹦狂、Fortran等語言編寫的代碼的A C API誓篱。

由于NumPy提供了一個簡單易用的C API,因此很容易將數(shù)據(jù)傳遞給由低級語言編寫的外部庫凯楔,外部庫也能以NumPy數(shù)組的形式將數(shù)據(jù)返回給Python窜骄。這個功能使Python成為一種包裝C/C++/Fortran歷史代碼庫的選擇,并使被包裝庫擁有一個動態(tài)的摆屯、易用的接口邻遏。
NumPy本身并沒有提供多么高級的數(shù)據(jù)分析功能,理解NumPy數(shù)組以及面向數(shù)組的計算將有助于你更加高效地使用諸如pandas之類的工具虐骑。因為NumPy是一個很大的題目准验,我會在附錄A中介紹更多NumPy高級功能,比如廣播廷没。
對于大部分數(shù)據(jù)分析應(yīng)用而言糊饱,我最關(guān)注的功能主要集中在:

  • 用于數(shù)據(jù)整理和清理、子集構(gòu)造和過濾颠黎、轉(zhuǎn)換等快速的矢量化數(shù)組運算另锋。
  • 常用的數(shù)組算法,如排序狭归、唯一化夭坪、集合運算等。
  • 高效的描述統(tǒng)計和數(shù)據(jù)聚合/摘要運算过椎。
  • 用于異構(gòu)數(shù)據(jù)集的合并/連接運算的數(shù)據(jù)對齊和關(guān)系型數(shù)據(jù)運算台舱。
  • 將條件邏輯表述為數(shù)組表達式(而不是帶有if-elif-else分支的循環(huán))。
  • 數(shù)據(jù)的分組運算(聚合、轉(zhuǎn)換竞惋、函數(shù)應(yīng)用等)柜去。

雖然NumPy提供了通用的數(shù)值數(shù)據(jù)處理的計算基礎(chǔ),但大多數(shù)讀者可能還是想將pandas作為統(tǒng)計和分析工作的基礎(chǔ)拆宛,尤其是處理表格數(shù)據(jù)時嗓奢。pandas還提供了一些NumPy所沒有的領(lǐng)域特定的功能,如時間序列處理等浑厚。

筆記:Python的面向數(shù)組計算可以追溯到1995年股耽,Jim Hugunin創(chuàng)建了Numeric庫。接下來的10年钳幅,許多科學(xué)編程社區(qū)紛紛開始使用Python的數(shù)組編程物蝙,但是進入21世紀,庫的生態(tài)系統(tǒng)變得碎片化了敢艰。2005年诬乞,Travis Oliphant從Numeric和Numarray項目整了出了NumPy項目,進而所有社區(qū)都集合到了這個框架下钠导。

NumPy之于數(shù)值計算特別重要的原因之一震嫉,是因為它可以高效處理大數(shù)組的數(shù)據(jù)。這是因為:

  • NumPy是在一個連續(xù)的內(nèi)存塊中存儲數(shù)據(jù)牡属,獨立于其他Python內(nèi)置對象票堵。NumPy的C語言編寫的算法庫可以操作內(nèi)存,而不必進行類型檢查或其它前期工作逮栅。比起Python的內(nèi)置序列悴势,NumPy數(shù)組使用的內(nèi)存更少。
  • NumPy可以在整個數(shù)組上執(zhí)行復(fù)雜的計算措伐,而不需要Python的for循環(huán)特纤。

要搞明白具體的性能差距,考察一個包含一百萬整數(shù)的數(shù)組废士,和一個等價的Python列表:

In [7]: import numpy as np

In [8]: my_arr = np.arange(1000000)

In [9]: my_list = list(range(1000000))

各個序列分別乘以2:

sys-系統(tǒng)特定的參數(shù)和功能
該模塊提供對解釋器使用或維護的一些變量的訪問叫潦,以及與解釋器強烈交互的函數(shù)蝇完。它始終可用

In [10]: %time for _ in range(10): my_arr2 = my_arr * 2
CPU times: user 20 ms, sys: 50 ms, total: 70 ms
Wall time: 72.4 ms

In [11]: %time for _ in range(10): my_list2 = [x * 2 for x in my_list]
CPU times: user 760 ms, sys: 290 ms, total: 1.05 s
Wall time: 1.05 s

基于NumPy的算法要比純Python快10到100倍(甚至更快)官硝,并且使用的內(nèi)存更少。

4.1 NumPy的ndarray:一種多維數(shù)組對象

NumPy最重要的一個特點就是其N維數(shù)組對象(即ndarray)短蜕,該對象是一個快速而靈活的大數(shù)據(jù)集容器氢架。你可以利用這種數(shù)組對整塊數(shù)據(jù)執(zhí)行一些數(shù)學(xué)運算,其語法跟標量元素之間的運算一樣朋魔。
要明白Python是如何利用與標量值類似的語法進行批次計算岖研,我先引入NumPy,然后生成一個包含隨機數(shù)據(jù)的小數(shù)組:

In [12]: import numpy as np

# Generate some random data
In [13]: data = np.random.randn(2, 3) # randn是標準正態(tài)分布

In [14]: data
Out[14]: 
array([[-0.2047,  0.4789, -0.5194],
       [-0.5557,  1.9658,  1.3934]])

然后進行數(shù)學(xué)運算:

In [15]: data * 10
Out[15]: 
array([[ -2.0471,   4.7894,  -5.1944],
       [ -5.5573,  19.6578,  13.9341]])

In [16]: data + data
Out[16]: 
array([[-0.4094,  0.9579, -1.0389],
       [-1.1115,  3.9316,  2.7868]])

第一個例子中,所有的元素都乘以10孙援。第二個例子中害淤,每個元素都與自身相加。

筆記:在本章及全書中拓售,我會使用標準的NumPy慣用法import numpy as np窥摄。你當然也可以在代碼中使用from numpy import*,但不建議這么做础淤。numpy的命名空間很大崭放,包含許多函數(shù),其中一些的名字與Python的內(nèi)置函數(shù)重名(比如min和max)鸽凶。
ndarray是一個通用的同構(gòu)數(shù)據(jù)多維容器币砂,也就是說,其中的所有元素必須是相同類型的玻侥。每個數(shù)組都有一個shape(一個表示各維度大小的元組)和一個dtype(一個用于說明數(shù)組數(shù)據(jù)類型的對象),每一個數(shù)組都有一個shape决摧,來表示維度大小。而dtype使碾,用來表示data type:

In [17]: data.shape
Out[17]: (2, 3)

In [18]: data.dtype
Out[18]: dtype('float64')

本章將會介紹NumPy數(shù)組的基本用法蜜徽,這對于本書后面各章的理解基本夠用。雖然大多數(shù)數(shù)據(jù)分析工作不需要深入理解NumPy票摇,但是精通面向數(shù)組的編程和思維方式是成為Python科學(xué)計算牛人的一大關(guān)鍵步驟拘鞋。

筆記:當你在本書中看到“數(shù)組”、“NumPy數(shù)組”矢门、"ndarray"時盆色,基本上都指的是同一樣?xùn)|西,即ndarray對象祟剔。

1 Greating ndarrays (創(chuàng)建n維數(shù)組)

創(chuàng)建數(shù)組最簡單的辦法就是使用array函數(shù)隔躲。它接受一切序列型的對象(包括其他數(shù)組),然后產(chǎn)生一個新的含有傳入數(shù)據(jù)的NumPy數(shù)組.array特征出來就是()最簡單的方法使用array函數(shù)物延,輸入一個序列即可宣旱,比如list:

In [19]: data1 = [6, 7.5, 8, 0, 1]

In [20]: arr1 = np.array(data1)

In [21]: arr1
Out[21]: array([ 6. ,  7.5,  8. ,  0. ,  1. ]) #有小數(shù)點因為形式統(tǒng)一,7.5改成75叛薯,小數(shù)點都沒了

嵌套序列(比如由一組等長列表組成的列表)將會被轉(zhuǎn)換為一個多維數(shù)組:

data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
arr2
#out結(jié)果
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

因為data2是列表的列表浑吟,NumPy數(shù)組arr2的兩個維度的shape是從data2引入的。因為data2是一個list of lists, 所以arr2維度為2耗溜。我們能用ndim和shape屬性來確認一下:

In [25]: arr2.ndim
Out[25]: 2 #幾行

In [26]: arr2.shape
Out[26]: (2, 4) #幾行幾列

除非特別說明(稍后將會詳細介紹)组力,np.array會嘗試為新建的這個數(shù)組推斷出一個較為合適的數(shù)據(jù)類型。數(shù)據(jù)類型保存在一個特殊的dtype對象中抖拴。比如說燎字,在上面的兩個例子中,我們有:

In [27]: arr1.dtype
Out[27]: dtype('float64')

In [28]: arr2.dtype
Out[28]: dtype('int64')

除np.array之外,還有一些函數(shù)也可以新建數(shù)組候衍。比如笼蛛,zeros和ones分別可以創(chuàng)建指定長度或形狀的全0或全1數(shù)組。empty可以創(chuàng)建一個沒有任何具體值的數(shù)組蛉鹿。要用這些方法創(chuàng)建多維數(shù)組伐弹,只需傳入一個表示形狀的元組即可:

In [29]: np.zeros(10)
Out[29]: array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

In [30]: np.zeros((3, 6))
Out[30]: 
array([[ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.]])

In [31]: np.empty((2, 3, 2)) #兩個三行二列
Out[31]: 
array([[[ 0.,  0.],
        [ 0.,  0.],
        [ 0.,  0.]],
       [[ 0.,  0.],
        [ 0.,  0.],
        [ 0.,  0.]]])

注意:認為np.empty會返回全0數(shù)組的想法是不安全的。很多情況下(如前所示)榨为,它返回的都是一些未初始化的垃圾值惨好。
arange是Python內(nèi)置函數(shù)range的數(shù)組版:

In [32]: np.arange(15)
Out[32]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

表4-1列出了一些數(shù)組創(chuàng)建函數(shù)。由于NumPy關(guān)注的是數(shù)值計算随闺,因此日川,如果沒有特別指定,數(shù)據(jù)類型基本都是float64(浮點數(shù))矩乐。


image.png

2.ndarray的數(shù)據(jù)類型

dtype(數(shù)據(jù)類型)是一個特殊的對象龄句,它含有ndarray將一塊內(nèi)存解釋為特定數(shù)據(jù)類型所需的信息:

In [33]: arr1 = np.array([1, 2, 3], dtype=np.float64)

In [34]: arr2 = np.array([1, 2, 3], dtype=np.int32)

In [35]: arr1.dtype
Out[35]: dtype('float64')

In [36]: arr2.dtype
Out[36]: dtype('int32')

dtype是NumPy靈活交互其它系統(tǒng)的源泉之一。多數(shù)情況下散罕,它們直接映射到相應(yīng)的機器表示分歇,這使得“讀寫磁盤上的二進制數(shù)據(jù)流”以及“集成低級語言代碼(如C、Fortran)”等工作變得更加簡單欧漱。數(shù)值型dtype的命名方式相同:一個類型名(如float或int)职抡,后面跟一個用于表示各元素位長的數(shù)字。標準的雙精度浮點值(即Python中的float對象)需要占用8字節(jié)(即64位)误甚。因此缚甩,該類型在NumPy中就記作float64。表4-2列出了NumPy所支持的全部數(shù)據(jù)類型窑邦。

筆記:記不住這些NumPy的dtype也沒關(guān)系擅威,新手更是如此。通常只需要知道你所處理的數(shù)據(jù)的大致類型是浮點數(shù)冈钦、復(fù)數(shù)郊丛、整數(shù)、布爾值瞧筛、字符串厉熟,還是普通的Python對象即可。當你需要控制數(shù)據(jù)在內(nèi)存和磁盤中的存儲方式時(尤其是對大數(shù)據(jù)集)驾窟,那就得了解如何控制存儲類型庆猫。

NumPy所支持的全部數(shù)據(jù)類型

你可以通過ndarray的astype方法明確地將一個數(shù)組從一個dtype轉(zhuǎn)換成另一個dtype:

In [37]: arr = np.array([1, 2, 3, 4, 5])

In [38]: arr.dtype
Out[38]: dtype('int64')

In [39]: float_arr = arr.astype(np.float64)

In [40]: float_arr.dtype
Out[40]: dtype('float64')

在本例中认轨,整數(shù)被轉(zhuǎn)換成了浮點數(shù)绅络。如果將浮點數(shù)轉(zhuǎn)換成整數(shù),則小數(shù)部分將會被截取刪除:

In [41]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])

In [42]: arr
Out[42]: array([  3.7,  -1.2,  -2.6,   0.5,  12.9,  10.1])

In [43]: arr.astype(np.int32)
Out[43]: array([ 3, -1, -2,  0, 12, 10], dtype=int32)

如果某字符串數(shù)組表示的全是數(shù)字,也可以用astype將其轉(zhuǎn)換為數(shù)值形式:

In [44]: numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)

In [45]: numeric_strings.astype(float)
Out[45]: array([  1.25,  -9.6 ,  42.  ])

注意:使用numpy.string_類型時恩急,一定要小心杉畜,因為NumPy的字符串數(shù)據(jù)是大小固定的,發(fā)生截取時衷恭,不會發(fā)出警告此叠。pandas提供了更多非數(shù)值數(shù)據(jù)的便利的處理方法。
如果轉(zhuǎn)換(casting)失敗的話随珠,如果轉(zhuǎn)換過程因為某種原因而失敗了(比如某個不能被轉(zhuǎn)換為float64的字符串)會給出一個ValueError提示灭袁。

可以用其他數(shù)組的dtype直接來制定類型,數(shù)組的dtype還有另一個屬性:

In [46]: int_array = np.arange(10)

In [47]: calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
#NumPy很聰明,它會將Python類型映射到等價的dtype上窗看。
In [48]: int_array.astype(calibers.dtype)
Out[48]: array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

你還可以用簡潔的類型代碼來表示dtype:

In [49]: empty_uint32 = np.empty(8, dtype='u4') #上圖表提到u4

In [50]: empty_uint32
Out[50]: 
array([         0, 1075314688,          0, 1075707904,          0,
       1075838976,          0, 1072693248], dtype=uint32)

筆記:調(diào)用astype總會創(chuàng)建一個新的數(shù)組(一個數(shù)據(jù)的備份)茸歧,即使新的dtype與舊的dtype相同。

3.NumPy數(shù)組的運算Arithmetic with NumPy Arrays

數(shù)組很重要显沈,因為它使你不用編寫循環(huán)即可對數(shù)據(jù)執(zhí)行批量運算软瞎。NumPy用戶稱其為矢量化(vectorization)。大小相等的數(shù)組之間的任何算術(shù)運算都會將運算應(yīng)用到元素級:
another expression:
數(shù)組之所以重要拉讯,是因為不用寫for循環(huán)就能表達很多操作涤浇,這種特性叫做vectorization(向量化)。任何兩個大小相等的數(shù)組之間的運算魔慷,都是element-wise(點對點):

In [51]: arr = np.array([[1., 2., 3.], [4., 5., 6.]])

In [52]: arr
Out[52]: 
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

In [53]: arr * arr
Out[53]: 
array([[  1.,   4.,   9.],
       [ 16.,  25.,  36.]])

In [54]: arr - arr
Out[54]: 
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

element-wise 我翻譯為點對點只锭,就是指兩個數(shù)組的運算,在同一位置的元素間才會進行運算院尔。
這種算數(shù)操作如果涉及標量(scalar)的話纹烹,會涉及到數(shù)組的每一個元素:

In [55]: 1 / arr
Out[55]: 
array([[ 1.    ,  0.5   ,  0.3333],
       [ 0.25  ,  0.2   ,  0.1667]])

In [56]: arr ** 0.5
Out[56]: 
array([[ 1.    ,  1.4142,  1.7321],
       [ 2.    ,  2.2361,  2.4495]])

兩個數(shù)組的比較會產(chǎn)生布爾數(shù)組:

In [57]: arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])

In [58]: arr2
Out[58]: 
array([[  0.,   4.,   1.],
       [  7.,   2.,  12.]])

In [59]: arr2 > arr
Out[59]:
array([[False,  True, False],
       [ True, False,  True]], dtype=bool)

4 Basic Indexing and Slicing(基本的索引和切片)

一維的我們之前已經(jīng)在list部分用過了,沒什么不同:

In [60]: arr = np.arange(10)

In [61]: arr
Out[61]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [62]: arr[5]
Out[62]: 5

In [63]: arr[5:8]
Out[63]: array([5, 6, 7])

In [64]: arr[5:8] = 12

In [65]: arr
Out[65]: array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])

如上所示召边,當你將一個標量值賦值給一個切片時(如arr[5:8]=12)铺呵,該值會自動傳播(也就說后面將會講到的“廣播”)到整個選區(qū)。跟列表最重要的區(qū)別在于隧熙,數(shù)組切片是原始數(shù)組的視圖片挂。這意味著數(shù)據(jù)不會被復(fù)制,視圖上的任何修改都會直接反映到源數(shù)組上贞盯。
作為例子音念,先創(chuàng)建一個arr的切片:

In [66]: arr_slice = arr[5:8]

In [67]: arr_slice
Out[67]: array([12, 12, 12])

現(xiàn)在,當我修稿arr_slice中的值躏敢,變動也會體現(xiàn)在原始數(shù)組arr中:

In [68]: arr_slice[1] = 12345

In [69]: arr
Out[69]: array([    0,     1,     2,     3,     4,    12, 12345,    12,     8,   
  9])

切片[ : ]會給數(shù)組中的所有值賦值:

In [70]: arr_slice[:] = 64

In [71]: arr
Out[71]: array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])

之所以這樣設(shè)計是出于性能和內(nèi)存的考慮闷愤,畢竟如果總是復(fù)制數(shù)據(jù)的話,會很影響運算時間件余。

當然如果想要復(fù)制讥脐,可以使用copy()方法遭居,比如arr[5:8].copy()

在一個二維數(shù)組里,單一的索引指代的是一維的數(shù)組(索引就是序號):

In [72]: arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

In [73]: arr2d[2]
Out[73]: array([7, 8, 9])

有兩種方式可以訪問單一元素:

In [74]: arr2d[0][2]
Out[74]: 3

In [75]: arr2d[0, 2]
Out[75]: 3

圖4-1說明了二維數(shù)組的索引方式旬渠。軸0作為行俱萍,軸1作為列。


NumPy數(shù)組中的元素索引

在多維數(shù)組中告丢,如果省略了后面的索引枪蘑,則返回對象會是一個維度低一點的ndarray(低緯度的多維數(shù)組)。因此岖免,在2×2×3數(shù)組arr3d中:

In [76]: arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])

In [77]: arr3d
Out[77]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],
       [[ 7,  8,  9],
        [10, 11, 12]]])
#arr3d[0]是一個2×3數(shù)組:
In [78]: arr3d[0]
Out[78]: 
array([[1, 2, 3],
       [4, 5, 6]])

標量值和數(shù)組都可以被賦值給arr3d[0]:

In [79]: old_values = arr3d[0].copy() #復(fù)制出一個新的數(shù)組
In [80]: arr3d[0] = 42
In [81]: arr3d
Out[81]: 
array([[[42, 42, 42],
        [42, 42, 42]],
       [[ 7,  8,  9],
        [10, 11, 12]]])

In [82]: arr3d[0] = old_values

In [83]: arr3d
Out[83]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],
       [[ 7,  8,  9],
        [10, 11, 12]]])

arr3d[1, 0]會給你一個(1, 0)的一維數(shù)組:

In [84]: arr3d[1, 0]
Out[84]: array([7, 8, 9])

上面的一步等于下面的兩步:

In [85]: x = arr3d[1]

In [86]: x
Out[86]: 
array([[ 7,  8,  9],
       [10, 11, 12]])

In [87]: x[0]
Out[87]: array([7, 8, 9])

注意岳颇,在上面所有這些選取數(shù)組子集的例子中,返回的數(shù)組都是視圖(不是實際復(fù)制過來的颅湘。

Indexing with slices(用切片索引)

ndarray的切片語法跟Python列表這樣的一維對象差不多:
In [88]: arr
Out[88]: array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])

In [89]: arr[1:6]
Out[89]: array([ 1,  2,  3,  4, 64])

對于之前的二維數(shù)組arr2d赦役,其切片方式稍顯不同:

In [90]: arr2d
Out[90]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

In [91]: arr2d[:2]
Out[91]: 
array([[1, 2, 3],
       [4, 5, 6]])

可以看到,切片是沿著axis 0(行)來處理的栅炒。所以掂摔,數(shù)組中的切片,是要沿著設(shè)置的axis來處理的赢赊。我們可以把arr2d[:2]理解為“選中arr2d的前兩行”乙漓。

你可以一次傳入多個切片,就像傳入多個索引那樣:

In [92]: arr2d[:2, 1:]
Out[92]: 
array([[2, 3],
       [5, 6]])  ## 前兩行释移,第二列之后

記住叭披,選中的是array view。通過混合整數(shù)和切片玩讳,能做低維切片涩蜘。比如,我們選中第二行的前兩列:

In [93]: arr2d[1, :2]
Out[93]: array([4, 5])
# 還可以選中第三列的前兩行:
In [94]: arr2d[:2, 2]
Out[94]: array([3, 6])

冒號表示提取整個axis(軸):

In [95]: arr2d[:, :1]
Out[95]: 
array([[1],
       [4],
       [7]])
二維數(shù)組切片

賦值也很方便:

In [96]: arr2d[:2, 1:] = 0

In [97]: arr2d
Out[97]: 
array([[1, 0, 0],
       [4, 0, 0],
       [7, 8, 9]])

5 Boolean Indexing (布爾索引)

假設(shè)我們的數(shù)組數(shù)據(jù)里有一些重復(fù)熏纯。這里我們用numpy.random里的randn函數(shù)來隨機生成正態(tài)分布數(shù)據(jù):

In [98]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])

In [99]: data = np.random.randn(7, 4)

In [100]: names
Out[100]: 
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'],
      dtype='<U4')

In [101]: data
Out[101]: 
array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
       [ 1.0072, -1.2962,  0.275 ,  0.2289],
       [ 1.3529,  0.8864, -2.0016, -0.3718],
       [ 1.669 , -0.4386, -0.5397,  0.477 ],
       [ 3.2489, -1.0212, -0.5771,  0.1241],
       [ 0.3026,  0.5238,  0.0009,  1.3438],
       [-0.7135, -0.8312, -2.3702, -1.8608]])

假設(shè)每一個name對應(yīng)data數(shù)組中的一行同诫,我們想要選中name為'Bob'的所有行。就像四則運算樟澜,用比較運算符(==):

In [102]: names == 'Bob'
Out[102]: array([ True, False, False,  True, False, False, False], dtype=bool)

然后用這個布爾數(shù)組當做索引:

In [103]: data[names == 'Bob']
Out[103]: 
array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
       [ 1.669 , -0.4386, -0.5397,  0.477 ]])

注意:布爾數(shù)組和data數(shù)組的長度要一樣误窖。
我們可以選中names=='Bob'的行,然后索引列:

In [104]: data[names == 'Bob', 2:]
Out[104]: 
array([[ 0.769 ,  1.2464],
       [-0.5397,  0.477 ]])

In [105]: data[names == 'Bob', 3]
Out[105]: array([ 1.2464,  0.477 ])

選中除了'Bob'外的所有行秩贰,可以用!=或者~:

In [106]: names != 'Bob'
Out[106]: array([False,  True,  True, False,  True,  True,  True], dtype=bool)

In [107]: data[~(names == 'Bob')]
Out[107]:
array([[ 1.0072, -1.2962,  0.275 ,  0.2289],
       [ 1.3529,  0.8864, -2.0016, -0.3718],
       [ 3.2489, -1.0212, -0.5771,  0.1241],
       [ 0.3026,  0.5238,  0.0009,  1.3438],
       [-0.7135, -0.8312, -2.3702, -1.8608]])

~操作符用來反轉(zhuǎn)條件很好用:

In [108]: cond = names == 'Bob'

In [109]: data[~cond]
Out[109]: 
array([[ 1.0072, -1.2962,  0.275 ,  0.2289],
       [ 1.3529,  0.8864, -2.0016, -0.3718],
       [ 3.2489, -1.0212, -0.5771,  0.1241],
       [ 0.3026,  0.5238,  0.0009,  1.3438],
       [-0.7135, -0.8312, -2.3702, -1.8608]])

選中2個或3個名字霹俺,組合多個布爾條件,用布爾運算符&毒费,|(或丙唧,另外python中的關(guān)鍵詞and和or不管用:

In [110]: mask = (names == 'Bob') | (names == 'Will')

In [111]: mask
Out[111]: array([ True, False,  True,  True,  True, False, False], dtype=bool)

In [112]: data[mask]
Out[112]: 
array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
       [ 1.3529,  0.8864, -2.0016, -0.3718],
       [ 1.669 , -0.4386, -0.5397,  0.477 ],
       [ 3.2489, -1.0212, -0.5771,  0.1241]])

用布爾索引總是會返回一份新創(chuàng)建的數(shù)據(jù),原本的數(shù)據(jù)不會被改變觅玻。
更改值的方式也很直覺想际。比如我們想讓所有負數(shù)變?yōu)?:

In [113]: data[data < 0] = 0

In [114]: data
Out[114]: 
array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
       [ 1.0072,  0.    ,  0.275 ,  0.2289],
       [ 1.3529,  0.8864,  0.    ,  0.    ],
       [ 1.669 ,  0.    ,  0.    ,  0.477 ],
       [ 3.2489,  0.    ,  0.    ,  0.1241],
       [ 0.3026,  0.5238,  0.0009,  1.3438],
       [ 0.    ,  0.    ,  0.    ,  0.    ]])

通過一維布爾數(shù)組設(shè)置整行或列的值也很簡單:

In [115]: data[names != 'Joe'] = 7

In [116]: data
Out[116]: 
array([[ 7.    ,  7.    ,  7.    ,  7.    ],
       [ 1.0072,  0.    ,  0.275 ,  0.2289],
       [ 7.    ,  7.    ,  7.    ,  7.    ],
       [ 7.    ,  7.    ,  7.    ,  7.    ],
       [ 7.    ,  7.    ,  7.    ,  7.    ],
       [ 0.3026,  0.5238,  0.0009,  1.3438],
       [ 0.    ,  0.    ,  0.    ,  0.    ]])

后面會看到培漏,這類二維數(shù)據(jù)的操作也可以用pandas方便的來做。

6.花式索引

花式索引(Fancy indexing)是一個NumPy術(shù)語沼琉,它指的是利用整數(shù)數(shù)組進行索引。假設(shè)我們有一個8×4數(shù)組:

In [117]: arr = np.empty((8, 4))

In [118]: for i in range(8):
   .....:     arr[i] = i

In [119]: arr
Out[119]: 
array([[ 0.,  0.,  0.,  0.],
       [ 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.]])

想要按一定順序選出幾行桩匪,可以用一個整數(shù)list或整數(shù)ndarray來指定順序:

In [120]: arr[[4, 3, 0, 6]]
Out[120]: 
array([[ 4.,  4.,  4.,  4.],
       [ 3.,  3.,  3.,  3.],
       [ 0.,  0.,  0.,  0.],
       [ 6.,  6.,  6.,  6.]])

這段代碼確實達到我們的要求了打瘪!使用負數(shù)索引將會從末尾開始選取行:

In [121]: arr[[-3, -5, -7]]
Out[121]: 
array([[ 5.,  5.,  5.,  5.],
       [ 3.,  3.,  3.,  3.],
       [ 1.,  1.,  1.,  1.]])

一次傳入多個索引數(shù)組會有一點特別。它返回的是一個一維數(shù)組傻昙,其中的元素對應(yīng)各個索引元組:

In [122]: arr = np.arange(32).reshape((8, 4))

In [123]: arr
Out[123]: 
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],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])

In [124]: arr[[1, 5, 7, 2], [0, 3, 1, 2]]  #對應(yīng)的前面幾行闺骚,后面括號幾列
Out[124]: array([ 4, 23, 29, 10])

可以看到[ 4, 23, 29, 10]分別對應(yīng)(1, 0), (5, 3), (7, 1), (2, 2)。不論數(shù)組有多少維妆档,fancy indexing的結(jié)果總是一維僻爽。
對于長方形區(qū)域,有下面的方法來截燃值搿:

In:arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
Out:array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

上面的意思是胸梆,先從arr中選出[1, 5, 7, 2]這四行:

array([[ 4, 5, 6, 7],
[20, 21, 22, 23],
[28, 29, 30, 31],
[ 8, 9, 10, 11]])

然后[:, [0, 3, 1, 2]]表示選中所有行,但是列的順序要按0,3,1,2來排须板。于是得到:

array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])

要記住碰镜,fancy indexing和切片不同,得到的是一個新的array习瑰。

7 Transposing Arrays and Swapping Axes(數(shù)組轉(zhuǎn)置和軸交換)

轉(zhuǎn)置也是返回一個view绪颖,而不是新建一個數(shù)組。有兩種方式甜奄,一個是transpose方法柠横,一個是T屬性:

In [126]: arr = np.arange(15).reshape((3, 5))

In [127]: arr
Out[127]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

In [128]: arr.T
Out[128]: 
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])

做矩陣計算的時候,這個功能很常用课兄,計算矩陣乘法的時候牍氛,用np.dot:

In [2]: import numpy as np

In [3]: arr = np.arange(8).reshape((4, 2))^M
   ...: print(arr.T)^M
   ...: print(arr)
   ...:
   ...:
[[0 2 4 6]
 [1 3 5 7]]
[[0 1]
 [2 3]
 [4 5]
 [6 7]]

In [4]: np.dot(arr.T, arr)
Out[4]:
array([[56, 68],
       [68, 84]])

上面的例子是 (2x4) x (4x2) = (2x2)。得到的結(jié)果是2x2維烟阐,就是普通的矩陣乘法糜俗。

對于多維數(shù)組,transpose會接受由軸數(shù)字組成的tuple曲饱,來交換軸:

In [132]: arr = np.arange(16).reshape((2, 2, 4))

In [133]: arr
Out[133]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],
       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])

In [134]: arr.transpose((1, 0, 2))  #原先是正常的(0,1,2)
Out[134]: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],
       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])

這里悠抹,第一個軸被換成了第二個,第二個軸被換成了第一個扩淀,最后一個軸不變楔敌。
這里,secode axis(1)被設(shè)為第一個驻谆,first axis(0)第二個卵凑,最后的axis沒邊庆聘。

使用.T來轉(zhuǎn)置swapping axes(交換軸)的一個特殊情況。ndarray有方法叫做swapaxes, 這個方法取兩個axis值勺卢,并交換這兩個軸:

In [135]: arr
Out[135]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],
       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])

In [136]: arr.swapaxes(1, 2) ## 直交換second axis和last axis
Out[136]: 
array([[[ 0,  4],
        [ 1,  5],
        [ 2,  6],
        [ 3,  7]],
       [[ 8, 12],
        [ 9, 13],
        [10, 14],
        [11, 15]]])

swapaxes也是返回源數(shù)據(jù)的視圖(不會進行任何復(fù)制操作)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伙判,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌森书,老刑警劉巖布轿,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異菇曲,居然都是意外死亡,警方通過查閱死者的電腦和手機抚吠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門常潮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人楷力,你說我怎么就攤上這事喊式。” “怎么了萧朝?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵垃帅,是天一觀的道長。 經(jīng)常有香客問我剪勿,道長贸诚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任厕吉,我火速辦了婚禮酱固,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘头朱。我一直安慰自己运悲,他們只是感情好,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布项钮。 她就那樣靜靜地躺著班眯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烁巫。 梳的紋絲不亂的頭發(fā)上署隘,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音亚隙,去河邊找鬼磁餐。 笑死,一個胖子當著我的面吹牛阿弃,可吹牛的內(nèi)容都是我干的诊霹。 我是一名探鬼主播羞延,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脾还!你這毒婦竟也來了伴箩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤鄙漏,失蹤者是張志新(化名)和其女友劉穎嗤谚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泥张,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡呵恢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年鞠值,在試婚紗的時候發(fā)現(xiàn)自己被綠了媚创。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡彤恶,死狀恐怖钞钙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情声离,我是刑警寧澤芒炼,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站术徊,受9級特大地震影響本刽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赠涮,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一子寓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笋除,春花似錦斜友、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至国拇,卻和暖如春洛史,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背酱吝。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工虹菲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掉瞳。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓毕源,卻偏偏與公主長得像浪漠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子霎褐,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容