1. 程式人生 > >numpy基礎入門-多維陣列物件

numpy基礎入門-多維陣列物件

Numpy,即Numeric Python是高效能科學計算和資料分析的基礎包。NumPy為我們提供了豐富的數學函式、強大的多維陣列物件以及優異的運算效能。NumPy與SciPy、Matplotlib、SciKits等其他眾多Python科學計算庫很好地結合在一起,共同構建了一個完整的科學計算生態系統。

功能主要包括:

1、一個強大的N維陣列物件Array;
2、比較成熟的(廣播)函式庫;
3、用於整合C/C++和Fortran程式碼的工具包;
4、實用的線性代數、傅立葉變換和隨機數生成函式。

import numpy as np

Numpy的ndarry:一種多維陣列
它最重要的一個特點是其N維陣列物件(即ndarry),可以利用這種陣列對整塊資料執行一些數學運算。

建立Ndarray,使用array函式,它接受一切序列型的物件,然後產生一個新的含有傳入資料的Numpy陣列

data1=[6,7.6,8,0,5]
arr1=np.array(data1)
arr1
array([ 6. ,  7.6,  8. ,  0. ,  5. ])

巢狀序列(比如一組等長列表組成的列表),將會轉換為一個多維陣列

data2=[[1,2,3,4,],[5,6,7,8]]
arr2=np.array(data2)
data2
[[1, 2, 3, 4], [5, 6, 7, 8]]
arr2.shape
(2, 4)
arr2.ndim
2

資料型別儲存在一個特殊的dtype物件中。

arr1.dtype
dtype('float64')
arr2.dtype
dtype('int32')

使用zero和one分別可以建立指定的長度或形狀的全0或全1陣列。empty可以建立一個沒有任何具體值的陣列

np.zeros(10)
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])
np.zeros((3,6))
array([[ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.]])
np.empty((2,3,4))
array([[[  3.53905284e-316,   7.36157812e-322,   0.00000000e+000,
           0.00000000e+000],
        [  2.18226563e+243,   1.16095484e-028,   3.23769002e+131,
           1.13168766e-095],
        [  9.29846444e+242,   9.16526748e+242,   7.90316782e-071,
           3.68423986e+180]],

       [[  9.92169729e+247,   4.78111609e+180,   1.02124020e+277,
           4.54814392e-144],
        [  6.06003395e+233,   1.06400250e+248,   1.15359351e+214,
           4.89287583e+199],
        [  8.03704417e-095,   9.07235856e+223,   1.16417019e-028,
           3.59453120e+246]]])

empty在多數情況下,返回的都是一些未初始化的垃圾值

arange是python內建函式range的陣列版本

np.arange(15)
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
np.arange(16).dtype
dtype('int32')
np.ones(3)
array([ 1.,  1.,  1.])
np.eye(3)
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
np.identity(3)
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])

這裡寫圖片描述

ndarray的資料型別

dtype(資料型別)是一個特殊的物件,含有ndarray將一塊記憶體解釋為特定資料型別所需的資訊。

arr1=np.array([1,2,3],dtype=np.float64)
arr2=np.array([1,2,3],dtype=np.int32)
arr1.dtype
dtype('float64')
arr2.dtype
dtype('int32')

數值型dtype的命名方式相同,一個型別名,後面加上表示各個元素位長的數字,標準的雙精度浮點值需要佔用8個位元組(即64位),因此該型別在numpy中記為float64.
這裡寫圖片描述
這裡寫圖片描述
可以通過ndarray的astype方式顯示地轉換其他的dtype型別

arr=np.array([1,2,3,4,5])
arr.dtype
dtype('int32')
float_arr=arr.astype(np.float64)
float_arr.dtype
dtype('float64')

可以看到整數轉換為浮點數,如果浮點數轉換為整數,則小數部分會被去掉

arr=np.array([3.7,-1.2,-2.6,0.5,12.8,10.2])
arr
array([  3.7,  -1.2,  -2.6,   0.5,  12.8,  10.2])
arr.astype(np.int32)
array([ 3, -1, -2,  0, 12, 10])

如果某字串陣列表示的全都是數字,可以使用astype轉換為數值型

numeric_strings=np.array(['1.25','-6.8','66'],dtype=np.string_)
numeric_strings.astype(float)
array([  1.25,  -6.8 ,  66.  ])
numeric_strings.astype(float64)
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-51-a186845b40d0> in <module>()
----> 1 numeric_strings.astype(float64)


NameError: name 'float64' is not defined

如果轉換過程失敗了,比如某個不能轉換為float64的字串就會引發TypeError,numpy很聰明,知道將python型別對映到等價的dtype上面。

陣列的另外一種用法

int_array=np.arange(10)
int_array.dtype
dtype('int32')
calibers=np.array([.22,.280,.357,.390,.44,.55],dtype=np.float64)
calibers
array([ 0.22 ,  0.28 ,  0.357,  0.39 ,  0.44 ,  0.55 ])
int_array.astype(calibers.dtype)
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

也可以用簡潔的程式碼表示dtype

empty_unint32=np.empty(8,dtype='u4')
empty_unint32
array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint32)

注意:astype無論如何都會建立一個新的陣列(原始資料的一種拷貝);浮點數只能表示近似的分數值(比如float32和float64)

陣列和標量之間的運算
大小相等的陣列之間的任何算術運算都會應用到元素級

arr=np.array([[1.,2.,3.],[4.,5.,6.]])
arr
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])
arr*arr
array([[  1.,   4.,   9.],
       [ 16.,  25.,  36.]])
arr+arr
array([[  2.,   4.,   6.],
       [  8.,  10.,  12.]])
arr-arr
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
1/arr
array([[ 1.        ,  0.5       ,  0.33333333],
       [ 0.25      ,  0.2       ,  0.16666667]])
arr**0.5
array([[ 1.        ,  1.41421356,  1.73205081],
       [ 2.        ,  2.23606798,  2.44948974]])

不同大小陣列之間的運算叫廣播

基本索引的切片

arr=np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr[6]
6
arr[4:8]
array([4, 5, 6, 7])
arr[4:8]=12
arr
array([ 0,  1,  2,  3, 12, 12, 12, 12,  8,  9])

可以看出將一個標量值賦值給一個切片時候,該值會自動傳播到整個選區,跟列表最重要的區別在於,陣列切片是原始陣列的檢視,
意味著資料不會被複制,檢視上的任何修改會直接反映到原來陣列上。

arr_slice=arr[5:8]
arr_slice[1]=123456
arr
array([ 0,  1,  2,  3, 64, 64, 64, 64,  8,  9])
arr_slice[:]=64
arr
array([ 0,  1,  2,  3, 64, 64, 64, 64,  8,  9])

如果想要得到的是ndarry切片的是一份副本而不是檢視,就需要顯示地進行復制操作,例如arr[5:8].copy()

copy_arr=arr[5:8].copy()
copy_arr
array([64, 64, 64])

在一個二維陣列中,各索引位置上的元素不在是標量而是一維陣列

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

這裡寫圖片描述

Numpy陣列中元素的索引

在多維陣列中如果省略了後面的索引,則返回物件會是一個維度低一點的ndarry(它含有高一級維度上的所有資料)

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

       [[ 7,  8,  9],
        [10, 11, 12]]])
arr3d[0]
array([[1, 2, 3],
       [4, 5, 6]])

arr3d[0]是一個2*3陣列

標量值和陣列都可以被賦值給arr3d[0]

old_values=arr3d[0].copy()
old_values
array([[1, 2, 3],
       [4, 5, 6]])
arr3d[0]=42
arr3d
array([[[42, 42, 42],
        [42, 42, 42]],

       [[ 7,  8,  9],
        [10, 11, 12]]])
arr3d[0]=old_values
arr3d
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])
arr3d[1,0]
array([7, 8, 9])

上面的這些選取陣列子集的例子中,返回的陣列都是檢視。

arr3d[:,0]
array([[1, 2, 3],
       [7, 8, 9]])
arr3d[0,1]
array([4, 5, 6])
arr3d[0,1,2]
6
arr3d[:,:,1]
array([[ 2,  5],
       [ 8, 11]])

切片索引

arr
array([ 0,  1,  2,  3, 64, 64, 64, 64,  8,  9])
arr[1:6]
array([ 1,  2,  3, 64, 64])

高維度可以在一個或者多個軸上進行切片,也可以根據陣列索引混合使用

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

可以看出是沿著0軸(即第一個軸)切片的,切片的沿著一個軸向選取元素的,可以一次傳入多個切片,就像傳入多個索引那樣

arr2d[:2,1:]
array([[2, 3],
       [5, 6]])

可以通過陣列索引和切片混合,可以得到低維度的切片

arr2d[1,:2]
array([4, 5])
arr2d[2,:1]
array([7])

注意:“只有冒號”表示選取整個軸,因此可以這樣只對高維軸進行切片

arr2d[:,:1]
array([[1],
       [4],
       [7]])
arr2d[:2,1:]
array([[2, 3],
       [5, 6]])
arr2d[:2,1:]=0
arr2d[:2,1:]
array([[0, 0],
       [0, 0]])

對切片表示式的賦值操作也會被擴充套件到整個選區。

布林型索引

現在使用numy.random中的randn函式生成一些正態分佈的隨機資料

names=np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
names
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'],
      dtype='<U4')
data=np.random.randn(7,4)
data
array([[ 1.20160119, -1.05723288, -0.57060877,  0.93471043],
       [ 0.5413258 ,  0.18615647,  0.57429245, -0.07045294],
       [-1.57840839,  0.03816457,  0.06441022,  1.15967355],
       [-0.87407974, -0.28018097,  0.80884835,  1.07076835],
       [-1.5814758 ,  0.81780567,  0.43782677, -0.91748426],
       [ 2.03128426, -0.21807742, -0.73759551,  0.42158797],
       [-2.13423716,  0.99993262, -1.02045863, -0.31841384]])

這裡寫圖片描述

假設每個名字都對應陣列中的一行,而我們想要選出對應於名字“Bob”的所有行。跟算術運算一樣,陣列的比較運算也是適量化的,
因此對nams和字串“Bob”的比較運算將會產生一個布林型陣列

names == 'Bob'
array([ True, False, False,  True, False, False, False], dtype=bool)
data[names=='Bob']
array([[ 1.20160119, -1.05723288, -0.57060877,  0.93471043],
       [-0.87407974, -0.28018097,  0.80884835,  1.07076835]])

布林型陣列的長度必須跟被索引的長度一致,也可以將布林型陣列跟切片、整數(或整數序列)混合使用

data[names=='Bob',2:]
array([[-0.57060877,  0.93471043],
       [ 0.80884835,  1.07076835]])
data[names=='Bob',3]
array([ 0.93471043,  1.07076835])

可以使用不等號(!=),也可以使用符號(~)對條件進行否定,其中負號(-)被捨棄使用了

names!='bob'
array([ True,  True,  True,  True,  True,  True,  True], dtype=bool)
data[~(names == 'Bob')]
array([[ 0.5413258 ,  0.18615647,  0.57429245, -0.07045294],
       [-1.57840839,  0.03816457,  0.06441022,  1.15967355],
       [-1.5814758 ,  0.81780567,  0.43782677, -0.91748426],
       [ 2.03128426, -0.21807742, -0.73759551,  0.42158797],
       [-2.13423716,  0.99993262, -1.02045863, -0.31841384]])

選取這三個名字中的兩個需要組合應用多個布林條件,使用&(和),|(或)布林算術符號運算即可

mask=(names=='Bob')|(names=='Will')
mask
array([ True, False,  True,  True,  True, False, False], dtype=bool)
data[mask]
array([[ 1.20160119, -1.05723288, -0.57060877,  0.93471043],
       [-1.57840839,  0.03816457,  0.06441022,  1.15967355],
       [-0.87407974, -0.28018097,  0.80884835,  1.07076835],
       [-1.5814758 ,  0.81780567,  0.43782677, -0.91748426]])

通過布林型選取陣列中的資料,將總數建立資料的副本,即使返回一模一樣的資料也是如此

警告:python關鍵字and和or在布林型陣列中無效

為了將data中的所有負值都設定為0,可以這樣做

data[data<0]=0
data
array([[ 1.20160119,  0.        ,  0.        ,  0.93471043],
       [ 0.5413258 ,  0.18615647,  0.57429245,  0.        ],
       [ 0.        ,  0.03816457,  0.06441022,  1.15967355],
       [ 0.        ,  0.        ,  0.80884835,  1.07076835],
       [ 0.        ,  0.81780567,  0.43782677,  0.        ],
       [ 2.03128426,  0.        ,  0.        ,  0.42158797],
       [ 0.        ,  0.99993262,  0.        ,  0.        ]])

通過一維陣列設定整行或整列的值也是簡單的

data[names!='Joe']=7
data
array([[ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 0.5413258 ,  0.18615647,  0.57429245,  0.        ],
       [ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 7.        ,  7.        ,  7.        ,  7.        ],
       [ 2.03128426,  0.        ,  0.        ,  0.42158797],
       [ 0.        ,  0.99993262,  0.        ,  0.        ]])

花式索引 是一個Numpy術語,指的是利用整數陣列進行索引

arr=np.empty((8,4))
arr
array([[ -6.13838792e-078,  -1.53355745e+181,  -7.86332188e-031,
         -4.01320607e-020],
       [ -1.63080546e+176,  -2.58653382e-247,  -3.48443862e-020,
         -1.68896544e+200],
       [ -7.86331065e-031,  -2.84375690e-227,   7.83497075e+012,
         -1.76690439e-286],
       [ -2.45631074e-145,  -1.41321561e+191,  -5.26157217e-228,
         -3.43723434e-150],
       [ -2.95588215e+191,  -4.85165577e-209,  -9.70435194e-083,
         -1.20930730e+201],
       [ -4.71890748e-200,  -5.37932783e+178,  -7.86322845e-031,
         -1.48209356e+181],
       [ -2.36374766e-064,  -2.09443292e-304,  -6.04055784e+195,
         -1.32298607e-194],
       [ -5.32612414e-025,  -1.32349375e+176,  -5.13949951e-209,
         -4.01320607e-020]])
for i in range(8):
    arr[i]=i
arr
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.]])

為了以特定的順序選取行子集,只需要傳入一個用於指定順序的整數列表或者ndarray即可

arr[[4,3,0,6]]
array([[ 4.,  4.,  4.,  4.],
       [ 3.,  3.,  3.,  3.],
       [ 0.,  0.,  0.,  0.],
       [ 6.,  6.,  6.,  6.]])

使用負數索引將會從末尾開始進行選取

arr[[-3,-5,-7]]
array([[ 5.,  5.,  5.,  5.],
       [ 3.,  3.,  3.,  3.],
       [ 1.,  1.,  1.,  1.]])
一次傳入多個索引陣列會有些特別。,其中的元素對應各個索引元組
arr=np.arange(32).reshape((8,4))
arr
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]])
arr[[1,5,7,2],[0,3,1,2]]
array([ 4, 23, 29, 10])

最終選取的元素是(1,0)、(5,3)、(7,1)和(2,2),下面採取選取行列的子集方法

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

另外一個方法:使用np.ix_函式,它可以將兩個一維整數陣列轉換為一個用於選取方形區域的索引器

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

花式索引和切片索引不同,它總是將資料複製到新陣列中。

陣列轉置和軸對換

轉置(response)是重塑的一種特殊形式,返回的是原資料的檢視,
不會進行任何資料的複製操作,還有一個T屬性

arr=np.arange(15).reshape((3,5))
arr
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
arr.T
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])

在進行矩陣計算時,經常使用到該操作,比如利用np.dot計算矩陣的內積 X T X

arr=np.random.randn(6,3)
arr
array([[-2.06876168,  0.97947713, -0.46649118],
       [ 0.44427728, -1.04164276,  0.50228949],
       [ 0.19183828,  0.06747897, -0.84046042],
       [-0.69185812,  1.36772582, -0.27280976],
       [ 1.06867616,  0.6844193 ,  1.57195584],
       [-0.32461881,  0.49908568,  0.1293782 ]])
np.dot(arr.T,arr)
array([[ 6.24007288, -2.85300014,  2.85364119],
       [-2.85300014,  4.63713872, -0.26951829],
       [ 2.85364119, -0.26951829,  3.73849153]])

對於高維陣列,tranpose需要得到一個由編號組成的元組才能對這些軸進行轉置

arr=np.arange(16).reshape((2,2,4))
arr
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])
arr.transpose((1,0,2))
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])

簡單轉置使用.T,其他的其實就是進行軸對換而已。

ndarray還有一個swapaxes方法,需要接受一對軸編號:

arr
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])
arr.swapaxes(1,2)
array([[[ 0,  4],
        [ 1,  5],
        [ 2,  6],
        [ 3,  7]],

       [[ 8, 12],
        [ 9, 13],
        [10, 14],
        [11, 15]]])