使用Numpy操作數組

Numpy是一個常用的Python科學技術庫,通過它可以快速對數組進行操作,包括形狀操作、排序、選擇、輸入輸出、離散傅立葉變換、基本線性代數,基本統計運算和隨機模擬等。許多Python庫和科學計算的軟件包都使用Numpy數組作爲操作對象,或者將傳入的Python數組轉化爲Numpy數組,因此在Python中操作數據離不開Numpy。

Numpy的核心是ndarray對象,由Python的n維數組封裝而來,但通過C語言預編譯相關的數組操作,因此比原生Python具有更高的執行效率,但仍然使用Python語言編碼,這樣就同時具有簡潔的代碼和高效的運行速度。ndarry與數組有些區別值得注意,numpy數組中的元素都具有相同的類型,並且在創建時就確定了固定的大小,這與Python數組對象可以動態增長不同。

1、數組屬性

Numpy對象的形式是同構多維數組,數組的維度稱爲軸(axis),每個維度上元素的個數稱爲軸的長度。例如下面是一個2×3的二維數組arr,第一軸長度爲3,第二軸長度爲2

    arr = [[ 1., 0., 0.],
           [ 0., 1., 2.]]

arr數組對象常用的屬性如下:

    # 數組軸的個數
    arr.ndim
    # 數組維度及長度,例如2×3的數組其shape爲(2, 3)
    arr.shape
    # 數組元素的總個數
    arr.size
    # 數組中元素的數據類型
    arr.dtype
    # 數組中元素所佔字節數
    arr.itemsize

2、創建數組

可以通過array()方法包裹普通python數組將其轉化爲numpy數組,通過dtype=規定元素的數據類型。數組可以是二維等高維數組,也可以是元組的形式。

填充指定大小的數組可以使用函數zeros(),將元素都填充爲0,或者ones()將元素填充爲1,empty()將元素填充爲隨機數

arange(a,b,c)函數用於從a到b每隔c長度生成一個數組元素。linspace(a,b,c)函數用於在a到b之間生成c個數組元素

	import numpy as np

    # 普通數組轉化爲numpy數組
    a1 = np.array([2, 3, 4], dtype=float)
    print(a1)                                    
    # 將元組數組轉化爲二維numpy數組
    a2 = np.array([(1, 2, 3), (3, 4, 5)])
    print(a2)
    # 將3×3的數組用1填充
    a3 = np.ones((3, 3))
    print(a3)
    # 從1到10,每隔2生成一個元素
    a4 = np.arange(1, 10, 2)
    print(a4)
    # 在1到12之間生成4個元素
    a5 = np.linspace(1, 12, 4, dtype=int)
    print(a5)
     
    '''
    普通數組轉化爲numpy對象:
    [2. 3. 4.]
    元組數組:
    [[1 2 3]
     [3 4 5]]
    用1填充數組:
    [[1. 1. 1.]
     [1. 1. 1.]
     [1. 1. 1.]]
    從1到10每隔2生成一個元素:
    [1 3 5 7 9]
    在1到12之間生成4個元素:
    [ 1  4  8 12]
    '''

3、數組操作

3.1 數組運算

算術運算符可以直接運用在矩陣上,其結果是將運算應用到每個元素上,例如矩陣A*B就是每個元素對應相乘,矩陣的乘法運算使用的是@符號

    A = np.array([[1, 1],
                  [0, 1]])
    B = np.array([[2, 0],
                  [3, 4]])
    print(A * B)
    print(A @ B)
     
    '''
    矩陣元素對應相乘:
    [[2 0]
     [0 4]]
    矩陣的乘法:
    [[5 4]
     [3 4]]
    '''

numpy中有些函數應用於整個數組,例如求和sum、最大值max、最小值min。如果在這些參數中指定了某個軸,則應用於指定軸。

還有一些函數應用於數組中的具體元素,例如求sincosexp、開方sqrt等,這些函數叫做通函數(ufunc)

    a = np.array([[0, 1, 2, 3],
                  [4, 5, 6, 7],
                  [8, 9, 10, 11]])
    print(a.max())  # 求整體的最大值,結果爲11
    print(a.sum(axis=0))  # 求每一列的和,結果爲:[12 15 18 21]
    print(np.sqrt(a))   # 數組每個元素求開方

3.2 選取元素

numpy中的數組同python中的list一樣可以進行索引、切片和迭代操作。a[x]代表訪問數組a下標爲x的元素,a[x:y]代表訪問數組從x到y的元素,如果省略x代表從頭開始,省略y代表直到結尾。a[x:y:a]代表從x到y每隔a個元素取一個值,如果a爲負數,代表逆序取值。

    a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    print(a[1:3])  # 輸出下標爲1到3的元素:[1 2]
    print(a[::-2])  # 逆序每隔兩個元素選一個值:[9 7 5 3 1]

如果是多維數組,則索引之間用逗號分隔。可以使用…代表省略某幾個維度,如果省略則會被認爲是該維度全部輸出,例如x[...,3] 等效於 x[:,:,:,:,3]。

可以通過for循環迭代多爲數組,其內容爲低一維度的子數組,如果希望遍歷每一個子元素,可以使用flat屬性。

    a = np.array([[0, 1, 2, 3],
                  [10, 11, 12, 13],
                  [40, 41, 42, 43]])
    # 輸出a第一維(行)的前三個,第二維(列)下標的1~3
    print(a[1:3, 0:3])
    # 輸出行的所有,下標爲2的列
    print(a[2, ...])
    # 遍歷數組
    for row in a:
        print(row)
    # 遍歷每個子元素
    for item in a.flat:
        print(item)
     
    '''
    後兩行的1~3列:
    [[10 11 12]
     [40 41 42]]
    第三行的所有列:
    [40 41 42 43]
    遍歷數組:
    [0 1 2 3]
    [10 11 12 13]
    [40 41 42 43]
    遍歷每個元素:
    0
    1
    2
    ......
    41
    42
    43
    '''

除了使用具體數字作爲索引,還可以使用numpy數組作爲索引。例如當索引i爲多維數組時,將會按照i的位置從數組a中取出元素

    a = np.arange(12) ** 2
    print(a)
    i = np.array([1, 3, 5])
    print(a[i])
    # 多維數組索引j
    j = np.array([[3, 4], [9, 7]])
    print(a[j])
     
    '''
    [  0   1   4   9  16  25  36  49  64  81 100 121]
    數組a的1、3、5個元素
    [ 1  9 25]
    通過多爲索引j取出a的數據填到對應位置
    [[ 9 16]
     [81 49]]
    '''

當索引數組由單個元素組成時,默認對數組a的第一個維度進行選擇。若需要對數組在多個維度上進行索引,則傳入多個索引組成的數組i,j並用逗號分隔

    a = np.array(([[0, 1, 2, 3],
                   [4, 5, 6, 7],
                   [8, 9, 10, 11]]))
    # 對a的第一個維度進行選擇
    i = np.array([0, 1])
    print(a[i])
    # 對數組a在多個維度上進行選擇,同時提供i,j代表取出a的[0,2]、[1,3]兩個元素
    j = np.array([2, 3])
    print(a[i, j])
    '''
    選擇多維數組a的第0、1兩行:
    [[0 1 2 3]
     [4 5 6 7]]
    a的[0,2]、[1,3]兩個元素:
    [2 7]
    '''

還可以通過True/False數組代表元素是否被選中,例如下面有一個True/False數組mask,新建一個全爲0的2×2數組padded,然後使用一個數組序列[1,2,3]去填充padded,可見mask爲True的地方被選擇並填充,False的地方未被選中,仍爲0.

mask = np.array([[True, False],
                 [True, True]])
padded = np.zeros((2, 2))
padded[mask] = np.array([1, 2, 3])  # 用序列填充被mask選中的位置
print(padded)
'''
[[1. 0.]
 [2. 3.]]
'''

3.3 改變維度

數組的reshape()方法可以將原數組重構成目標維度的數組,例如將一個2×6的數組重構爲3×4的數組,

數組在重構時不會修改原數組,而是返回修改後的結果數組

值得注意的是數組在重構和打印時都是從最右邊的維度開始往左進行,例如下面的3×4的數組b,先按行排列4個,然後再換行,排列這樣的3行。如果是多維,則按這樣的行列繼續輸出。如果數組維度爲-1,則會自動計算該維度的大小,例如含有12個元素的數組,第二、第三維是3×2,則第一維就是2

ravel()函數可以將數組展成一維數組。

    a=np.array([[1,2,3,4,5,6],
                [7,8,9,10,11,12]])
    b=a.reshape(3,4)
    print(b)
    # 多維數組,自動計算
    print(a.reshape(-1,3,2))
    # 展開數組
    flatted = b.ravel()
    print(flatted, end=' ')
    '''
    [[ 1  2  3  4]
     [ 5  6  7  8]
     [ 9 10 11 12]]
    2×3×2的多維數組:
    [[[ 1  2]
      [ 3  4]
      [ 5  6]]
     [[ 7  8]
      [ 9 10]
      [11 12]]]
    展開數組:
    [ 1  2  3  4  5  6  7  8  9 10 11 12] 
    '''

transpose()函數可以實現數組轉置的功能,例如下面數組a爲一個4×3×2的三維數組,通過transpose(2,0,1),其中的0、1、2分別代表數組原來的維度第一、二、三維,在括號中的位置代表轉置後的維度,因此結果將數組的第三維放到第一維,第一維放到第二維,第二維放到第三維,即轉置爲3×2×4的數組。

    a = np.array([
        [[1, 2, 3, 4],
         [5, 6, 7, 8],
         [9, 10, 11, 12]],
        [[13, 14, 15, 16],
         [17, 18, 19, 20],
         [21, 22, 23, 24]]
    ])
    b = a.transpose(2, 0, 1)
    print(b)
    '''
    [[[ 1  5  9]
      [13 17 21]]
     [[ 2  6 10]
      [14 18 22]]
     [[ 3  7 11]
      [15 19 23]]
     [[ 4  8 12]
      [16 20 24]]]
    '''

3.4 數組的合併

numpy的concatenate()可以用於數組的合併,其第一個參數爲要合併的數組,放在一個tuple()或list[]中。第二個參數axis指定合併的軸,默認axis=0,即從第一維合併所有子元素

a1 = np.array([[1, 3], [5, 7]])
a2 = np.array([[2, 4], [6, 8]])
a3 = np.concatenate((a1, a2))  # 合併數組
print(a3)
print(np.concatenate((a1, a2), axis=1))  # 指定合併的軸
'''
[[1 3]
 [5 7]
 [2 4]
 [6 8]]
[[1 3 2 4]
 [5 7 6 8]]
'''

numpy的hstack()函數可以在水平方向上合併多個數組,vstack()函數可以在垂直方向上合併多個數組
相反地,hsplit()vsplit()可以拆分爲指定數量的數組

    a=np.array([1,2,3])
    b=np.array([4,5,6])
    # 垂直方向合併
    c=np.vstack((a,b))
    print(c)
    # 水平方向合併
    print(np.hstack((a,b)))
    # 水平方向拆分
    print(np.hsplit(c,3))
    '''
    垂直堆疊
    [[1 2 3]
     [4 5 6]]
    水平合併
    [1 2 3 4 5 6]
    水平拆分爲三個1×2的:
    [array([[1],
           [4]]), 
    array([[2],
           [5]]), 
    array([[3],
           [6]])]
    '''

3.5 數組的複製

當一個數組對象賦值給一個新的變量時,是新開闢一個存儲空間還是隻是傳遞一個引用?答案是引用。

例如執行語句b=a,只是將一個引用傳給了b,對b執行的操作會直接影響a。查看a、b兩個對象的節點id也是一樣的

    a = np.array([1, 2, 3])
    b = a
    # 修改b
    b[0] = 0
    print(a)
    # 輸出a、b對象的id
    print(id(a), id(b))
     
    '''
    修改b,a也發生了變化
    [0 2 3]
    查看二者的id
    2290013812656 2290013812656
    '''

通過切片返回數組的視圖,修改視圖的形狀不會影響原數組,但是在視圖上修改數據原數組也會改變。在執行del a之後,由於c引用了a,a依舊會存在內存中不會被刪除

    c = a[:]
    # 修改視圖的形狀
    c.shape = 3, 1
    print(c, a)
    # 修改視圖c的數據
    c[0] = 1
    print(a[0])
     
    '''
    對視圖c的形狀做修改,a不會受到影響
    [[0]
     [2]
     [3]] [0 2 3]
    修改c的數據,a也會隨之改變:
    1
    '''

通過copy()方法可以生成數據的副本,對副本的操作完全不會影響原數組

    d= a.copy()
    d[0]=5
    # 修改數組的副本d,a不受影響,輸出a:[1 2 3]
    print(a)

4、保存文件

numpy通過save()將一個數組arr保存爲.npy文件,並且可以通過np.load()讀取該文件

    np.save("output.npy",arr)
     
    arr = np.load("output.npy")

如果需要儲存多個數組,可以通過np.savez()保存爲.npz文件,其變量名作爲數組的key值,同理可以使用load()讀取

    np.savez('array_save.npz',arr1,arr2,arr3)
     
    Arr=np.load('arr_save.npz')
    arr1=Arr['arr1']        # 通過key值取到不同數組

.npy與.npz文件無法手動查看,如果需要查看可以保存爲txt文本的格式,通過savetxt()保存,並通過loadtxt()讀取

    np.savetxt('data.txt',arr)
     
    data=np.loadtxt('data.txt')

在保存文件時可以通過fmt屬性設置輸出數組的格式,如下設置保存數據爲整型,且後跟一個製表符

np.savetxt("./output.txt", data, fmt='%d	')
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章