<button id="ssm6u"><optgroup id="ssm6u"></optgroup></button>
  • 跳轉至

    Numpy基礎

    概要

    本文講解了numpy的一些基礎操作. 同時還會介紹numpy中兩個重要的概念全局函數與廣播. 最后還介紹了numpy下面的兩個包, linalg線形代數計算包與random隨機生成包.

    keywords numpy 初始化 索引 random 隨機 線性代數

    何為Numpy?

    numpy-logo.jpg

    NumPy 是一個運行速度非??斓臄祵W庫,主要用于矩陣運算。

    為什么我們不用python原生的list呢?

    矩陣運算功能
    它可以讓你在 Python 中使用向量和數學矩陣,以及封裝了很多矩陣運算等操作, 這些都是list所不具備的.

    numpy更省空間
    NumPy中的數組的存儲效率和輸入輸出性能均遠遠優于Python中等價的基本數據結構,且其能夠提升的性能是與數組中的元素成比例的.

    處理速度更快

    NumPy的大部分代碼都是用C語言寫的,其底層算法在設計時就有著優異的性能,這使得NumPy比純Python代碼高效得多.

    關于這方面的討論細節, 大家可以在StackOverflow上的一篇問答中了解更多:

    why-numpy-instead-of-python-lists

    NumPy 是 Python 在科學計算領域取得成功的關鍵之一,如果你想通過 Python 學習數據科學(例如scipy)或者機器學習(例如numpy),就必須學習 NumPy。

    安裝并引入Numpy科學計算包模塊

    使用pip, 我們在終端輸入

    sudo pip3 install numpy
    

    即可安裝完成numpy.

    引入numpy模塊

    import numpy
    

    一般來講習慣上, 我們引入numpy模塊的時候, 會將numpy起一個別名np, 方便調用(因為比較簡短)

    import numpy as np
    

    ndarray初始化

    方法1-基礎定義

    接下來, 我們就寫一組簡單的代碼, 實驗一下ndarray的相關屬性.

    首先我們需要使用np.array 函數初始化幾個數組.

    import numpy as np
    
    # 一維數組
    A1 = np.array([1, 2, 3])
    
    # 二維數組
    A2 = np.array([[1, 2, 3], [4, 5, 6]])
    

    聲明數組的方法就是使用[]嵌套, ,分割同層級的元素.

    當然有了二維數組, 后面也會有更高維度的數組(或者說是矩陣).

    打印一下這兩個數組.

    print('A1: \n%s'%A1)
    
    print('A2: \n%s'%A2)
    

    [OUT]
    A1: 
    [1 2 3]
    A2: 
    [[1 2 3]
     [4 5 6]]
    

    方法2-初始化特定區間的整數數組

    初始化一個數組的方式, 我們還可以使用"批量"的方式

    np.arange的使用方法類似Python原生的range
    它的函數原型為

    arange([start,] stop[, step,], dtype=None)
    

    start : 數值區前開始 默認是0
    stop : 數值區間結束
    step : 數值增加間隔, 默認為1
    dtype : 數據類型

    代碼演示

    print('\n遞增')
    print(np.arange(0, 10, 1))
    

    OUTPUT

    遞增
        [0 1 2 3 4 5 6 7 8 9]
    

    print(np.arange(10))
    

    OUTPUT

     [0 1 2 3 4 5 6 7 8 9]
    

    可以看到上面的兩個語句是等同的.

    我們接下來實驗一下遞減

    print('\n 遞減')
    print(np.arange(10, 0, -1))
    

    OUTPUT

    遞減
        [10  9  8  7  6  5  4  3  2  1]
    

    方法3-初始化特定區間的浮點數組

    我們還可以使用linspace在一個數值區間內劃分若干個段(浮點數).

    # 隨機間隔
    np.linspace(1,10,5)
    

    OUTPUT

    array([  1.  ,   3.25,   5.5 ,   7.75,  10.  ])
    

    ndarray的屬性

    這里給大家展示幾個常用的屬性.

    屬性名稱 含義
    ndarray.ndim 數組的維度,等于Rank
    ndarray.shape (行數, 列數)
    ndarray.size 元素總個數 = 列數 * 行數
    ndarray.dtype 數組元素數據類型
    ndarray.itemsize 數組中每個元素,字節大小
    print('A2.ndim = %d' % A2.ndim)
    print('A2.shape')
    print(A2.shape)
    print('A2.size = %d' % A2.size)
    print('A2.dtype = %s'%A2.dtype)
    print('A2.itemsize = %d'%A2.itemsize)
    

    OUTPUT

    A2.ndim = 2
    A2.shape
    (2, 3)
    A2.size = 6
    A2.dtype = int64
    A2.itemsize = 8
    

    ndarray數據類型

    NumPy 的核心是數組(arrays)。具體來說是多維數組(ndarrays).

    nd: n-dimension n維的意思
    

    ndarray與我們之前介紹的 list之間巨大的差別在于, numpy的ndarray所有的元素數據類型必須相同. 這是為矩陣運算做保障的.

    Python支持的數據類型有整型、浮點型以及復數型,但這些類型不足以滿足科學計算的需求,因此NumPy中添加了許多其他的數據類型,如bool、inti、int64、float32、complex64等。同時,它也有許多其特有的屬性和方法。
    在Numpy中定義了24種新的Python基礎數據類型, 大多基于C語言的數據類型.

    不是所有的ndarray的數據類型, 都可以交給python-opencv計算的, 所以數據類型, 在使用的時候也要注意.

    dtype-hierarchy.png

    詳情見 arrays-scalars-官方文檔

    我們還可以使用astype函數, 將原本為int類型的ndarray轉換為浮點型.

    # 修改數據格式
    A2.astype(float)
    

    OUTPUT

    array([[ 1.,  2.,  3.],
            [ 4.,  5.,  6.]])
    

    slicing切片操作

    何為切片? 大家應該吃過面包吧

    bread-slicing.jpg

    一開始這里是一個大大的面包, 而我們需要這一整個面包其中的一部分, 所以,為了達到這個目的, 我們要進行切片, 可以橫著切, 也可以豎著切, 于是我們就獲取到了面包片.同樣對于多維數組, 我們切片是為了獲取數組的其中某一個子區域.

    一維數組切片

    Numpy 中多維數組的切片操作與 Python 中 list 的切片操作一樣,同樣由 start, stop, step 三個部分組成.

    首先我們聲明一個長度為10的一維數組.

    A = np.arange(10)
    

    OUTPUT 打印一下A

    In [7]: A

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

    那么我們需要獲取, 前三個元素.

    我們輸入A[:3], 使用:分隔, 第一個參數是起始值, 默認為0, 這里可以省略.

    后面的3是切片的結尾(但是取不到編號為3的元素)

    In [8]: A[:3]
    Out[8]: array([0, 1, 2])
    

    完整版是這種寫法:

    In [11]: A[0:3]
    Out[11]: array([0, 1, 2])
    

    :分割的第三個參數是step代表每次走多少步, 即序號增加的數值, 默認為1.

    所以更完整的寫法是:

    In [12]: A[0:3:1]
    Out[12]: array([0, 1, 2])
    

    我們也可以隔一個數值取一個. 我們設置step=2

    In [13]: A[0:10:2]
    Out[13]: array([0, 2, 4, 6, 8])
    

    step也可以是負值, 例如我們設置為step=-1

    In [15]: A[-1:5:-1]
    Out[15]: array([9, 8, 7, 6])
    

    需要說明的是, 這里start=-1 代表是最末尾的元素.

    如果我們只是填寫step=-1 結果等于原來數組的倒序

    In [14]: A[::-1]
    Out[14]: array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
    

    多維數組切片

    針對多維數組的切片, 不同之處在于, 需要使用,分隔.

    一圖勝千言, 相信大家看下面這張圖片就會秒懂.

    An Introduction to Scientific Python – NumPy

    ndarray的內存共享

    ndarray的之間的直接賦值與索引(切片 slicing)賦值, 那么這兩個元素之間的所使用的內存都是同一塊.

    也就意味著, 如果A = B, 修改B的值, A也會被修改, 因為他們共用了同一片內存空間.

    這樣比較高效, 但是如果不注意的話, 也會造成很多問題.

    我們先來演示一下.

    In [1]: import numpy as np
    
    In [2]: A = np.array([[1, 2], [3, 4]])
    
    In [3]: A
    Out[3]: 
    array([[1, 2],
           [3, 4]])
    
    In [4]: B = A
    
    In [5]: B[0] = -1
    
    In [6]: B
    Out[6]: 
    array([[-1, -1],
           [ 3,  4]])
    
    In [7]: A
    Out[7]: 
    array([[-1, -1],
           [ 3,  4]])
    

    ndarray的深度拷貝

    那么, 我們如何才可以復制一個ndarray, 修改新的ndarray而不影響之前的值呢?

    這里我們需要用到numpy的深度拷貝函數. np.copy

    new_img = np.copy(old_img)
    
    In [8]: A = np.array([[1, 2], [3, 4]])
    
    In [9]: B = np.copy(A)
    
    In [10]: B[0] = -1
    
    In [11]: B
    Out[11]: 
    array([[-1, -1],
           [ 3,  4]])
    
    In [12]: A
    Out[12]: 
    array([[1, 2],
           [3, 4]])
    

    當然我們也可以使用另外一種拷貝方式:

    B = A.copy()
    

    如果你對python中的淺度拷貝跟深度拷貝不是很清楚的話, 可以看一下博客園的這篇文章.

    圖解Python深拷貝和淺拷貝

    順便你再了解一下copy包里面的copydeepcopy 兩個函數都有哪些不同.

    20180129-python-shallow-and-deep.png

    ndarray的變形

    bianxing-zixingcheren.jpg

    在矩陣計算中, 有時候我們需要將矩陣進行變形, 例如原來是4*5的矩陣, 可能會變形為5*4 或者10*2.

    也有可能為改變維度, 原來是二維的轉變為一維的, 例如1*20.

    這里我們就需要兩個函數resizereshape

    reshape

    reshape函數的原型:

    reshape(a, newshape, order='C')
    

    其中 a 是要被resize的數組

    new_shape是新數組的尺寸, 類型為tuple 元組類型

    使用樣例

    In [29]: A = np.arange(10)
    
    In [30]: A
    Out[30]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    
    In [31]: np.reshape(A, (2, 5))
    Out[31]: 
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9]])
    

    reshape函數變形前與變形后的size必須相同.

    否則就會報錯.

    In [21]: np.reshape(A, (5, 5))
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-21-a0bcf970fe78> in <module>()
    ----> 1 np.reshape(A, (5, 5))
    
    /usr/lib/python3.6/site-packages/numpy/core/fromnumeric.py in reshape(a, newshape, order)                                                                           
        230            [5, 6]])
        231     """
    --> 232     return _wrapfunc(a, 'reshape', newshape, order=order)
        233 
        234 
    
    /usr/lib/python3.6/site-packages/numpy/core/fromnumeric.py in _wrapfunc(obj, method, *args, **kwds)
         55 def _wrapfunc(obj, method, *args, **kwds):
         56     try:
    ---> 57         return getattr(obj, method)(*args, **kwds)
         58 
         59     # An AttributeError occurs if the object does not have
    
    ValueError: cannot reshape array of size 10 into shape (5,5)
    

    ndarray對象A在調用reshape函數的時候, 返回一個新的ndarray對象, 原來的ndarray并不發生改變.

    In [23]: A
    Out[23]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    
    In [24]: A.reshape((2, 5))
    Out[24]: 
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9]])
    
    In [25]: A
    Out[25]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    

    resize

    resize相當于reshape的一種擴展, 他不要求新的array的size 必須與原來array的size相同, 可以自動填充.

    np.resize的函數原型

    resize(a, new_shape)
    

    其中 a 是要被resize的數組

    new_shape是新數組的尺寸, 類型為tuple 元組類型

    我們首先聲明一個1*10的一維數組

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

    將其轉換為2維數組.
    In [17]: np.resize(A, (2, 5))
    Out[17]: 
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9]])
    

    如果new_shape 大于a的shape, 則使用a填充.

    In [18]: np.resize(A, (4, 5))
    Out[18]: 
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9],
           [0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9]])
    
    In [19]: np.resize(A, (5, 5))
    Out[19]: 
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9],
           [0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9],
           [0, 1, 2, 3, 4]])
    

    ndarray對象A在調用resize函數的時候, 什么也不返回, 原來的ndarray值變更為resize之后的.

    In [25]: A
    Out[25]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    
    In [26]: A.resize((2, 5))
    
    In [27]: A
    Out[27]: 
    array([[0, 1, 2, 3, 4],
           [5, 6, 7, 8, 9]])
    

    矩陣拼接

    在opencv中常會用到圖像拼接, 這里就需要用到numpy中的矩陣拼接.

    矩陣拼接有個前提條件, 就是,拼接的那一面長度必須相同.

    • hstack() 橫向拼接

    • vstack() 縱向拼接

    我們先聲明兩個矩陣, A, 跟B

    A = np.arange(16).reshape(4,4)
    B = np.arange(16).reshape(4,4) * -1
    
    print(A)
    
    print(B)
    

    OUTPUT

    [[ 0 1 2 3]
    [ 4 5 6 7]
    [ 8 9 10 11]
    [12 13 14 15]]
    [[ 0 -1 -2 -3]
    [ -4 -5 -6 -7]
    [ -8 -9 -10 -11]
    [-12 -13 -14 -15]]

    然后將兩個矩陣先進行橫向拼接. 注意這里傳入的是tuple

    # 注意這里傳入的是tuple
    np.hstack((A, B))
    

    OUTPUT

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

    然后再嘗試進行縱向拼接.

    np.vstack((A, B))
    

    OUTPUT

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

    ufunc 全局函數

    作用于數組中每個元素的函數我們稱之為ufunc

    universal function  # 全局函數
    

    在ufunc中,如果是兩個ndarray之間的運算,shape必須相等

    • + 加法

    • - 減法

    • * 乘法, 注意這里不是點乘

    • / 除法

    聲明兩個尺寸相同的矩陣. AB

    In [54]: A = np.arange(12).reshape((4, 3))
    
    In [55]: B = np.arange(2, 14).reshape(4, 3)
    
    In [56]: A
    Out[56]: 
    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 6,  7,  8],
           [ 9, 10, 11]])
    
    In [57]: B
    Out[57]: 
    array([[ 2,  3,  4],
           [ 5,  6,  7],
           [ 8,  9, 10],
           [11, 12, 13]])
    

    全局加法

    In [58]: A + B
    Out[58]: 
    array([[ 2,  4,  6],
           [ 8, 10, 12],
           [14, 16, 18],
           [20, 22, 24]])
    

    全局減法

    In [59]: A - B
    Out[59]: 
    array([[-2, -2, -2],
           [-2, -2, -2],
           [-2, -2, -2],
           [-2, -2, -2]])
    

    全局乘法

    In [60]: A * B
    Out[60]: 
    array([[  0,   3,   8],
           [ 15,  24,  35],
           [ 48,  63,  80],
           [ 99, 120, 143]])
    

    全局除法

    In [61]: A / B
    Out[61]: 
    array([[ 0.        ,  0.33333333,  0.5       ],
           [ 0.6       ,  0.66666667,  0.71428571],
           [ 0.75      ,  0.77777778,  0.8       ],
           [ 0.81818182,  0.83333333,  0.84615385]])
    

    其他全局函數

    如果我們的計算對象是單個ndarray中的每個元素, 就需要使用到numpy內置的一些全局函數.

    例如np.sin() , 這個函數的作用就是返回一個矩陣, 其中每個元素都是原來矩陣A 進行sin運算的結果

    print(np.sin(A))
    

    [[ 0. 0.84147098 0.90929743 0.14112001]
    [-0.7568025 -0.95892427 -0.2794155 0.6569866 ]
    [ 0.98935825 0.41211849 -0.54402111 -0.99999021]
    [-0.53657292 0.42016704 0.99060736 0.65028784]]

    自定義全局函數

    我們也可以自定義自己的ufunc 具體方法見

    文檔-Writing your own ufunc

    需要先寫用C底層實現方法, 然后再python中調用.

    先MARK一下, 也想實現自己的C語言拓展, 嘿嘿, 不過暫時沒有想到啥應用場景.

    broadcasting 廣播

    那如果兩個ndarrayshape 不相同的話, 兩者之間可以進行計算么?

    答案是可以的, shape不同的兩個ndarray的操作都是由broadcasting管理的

    broadcast-icon.jpg

    舉個最簡單的例子.

    ndarray與數值的運算

    聲明一個矩陣A

    In [62]: A = np.array([1, 2])
    
    In [65]: A + 3
    Out[65]: array([4, 5])
    

    很明顯, A3 兩個對象的尺寸 顯然不相同, 但是二者之間可以進行運算, A中的每個元素都與3進行了加運算.

    同樣, 減法-, 乘法* ,除法/ 都是成立的.

    In [66]: A - 3
    Out[66]: array([-2, -1])
    
    In [67]: A * 3
    Out[67]: array([3, 6])
    
    In [68]: A / 3
    Out[68]: array([ 0.33333333,  0.66666667])
    

    ndarray與ndarray之間的廣播

    兩個ndarray進行廣播由個前提條件, 就是其中一個的shape應該是(N, 1) 另外一個是(1, N)

    有趣的是一維的ndarray的矩陣轉置的尺寸并不是(N, 1)

    In [87]: A.shape
    Out[87]: (2,)
    
    In [88]: A.T.shape
    Out[88]: (2,)
    

    所以, 我們先將矩陣A進行轉置, 并且reshape(N, 1)

    In [78]: C =  np.reshape(A.T, (2,1))
    

    OUTPUT

    In [90]: C
    Out[90]: 
    array([[1],
           [2]])
    
    In [91]: B
    Out[91]: array([2, 4, 6])
    

    加法廣播

    然后嘗試BC進行加法運算.

    In [92]: C + B
    Out[92]: 
    array([[3, 5, 7],
           [4, 6, 8]])
    
    In [93]: B + C
    Out[93]: 
    array([[3, 5, 7],
           [4, 6, 8]])
    

    可以看到C + BB + C計算的值是相等的.

    廣播的意思是, C中的元素, 分別與B中的元素進行向加, 最后將這些值存在一起.

    加入shape為(N, 1) 的矩陣與(1, M)的矩陣進行廣播運算, 那么會后獲取的矩陣shape(N, M).

    減法廣播

    這里可以看到C-B的結果與B-C的不同.

    C - B 是C中的元素分別減去B中的元素的廣播.
    B - C 是B中的元素分別減去C中的元素的廣播.

    In [94]: C - B
    Out[94]: 
    array([[-1, -3, -5],
           [ 0, -2, -4]])
    
    In [95]: B - C
    Out[95]: 
    array([[1, 3, 5],
           [0, 2, 4]])
    

    乘法廣播

    In [96]: C * B
    Out[96]: 
    array([[ 2,  4,  6],
           [ 4,  8, 12]])
    
    In [97]: B * C
    Out[97]: 
    array([[ 2,  4,  6],
           [ 4,  8, 12]])
    

    除法廣播

    In [98]: C / B
    Out[98]: 
    array([[ 0.5       ,  0.25      ,  0.16666667],
           [ 1.        ,  0.5       ,  0.33333333]])
    
    In [99]: B / C
    Out[99]: 
    array([[ 2.,  4.,  6.],
           [ 1.,  2.,  3.]])
    

    np.linalg 線性代數的基本操作

    linalg-xmind-notebook.png

    numpy的linalg里包含著線形代數的函數.

    引入linalg模塊

    引入并起名為LA

    import numpy.linalg as LA
    

    聲明一個矩陣A

    In [117]: A = np.random.rand(16).reshape((4, 4))
    
    In [118]: A
    Out[118]: 
    array([[ 0.70961003,  0.38232282,  0.3333471 ,  0.67033575],
           [ 0.42843928,  0.10718751,  0.60270476,  0.17157395],
           [ 0.59779218,  0.49990817,  0.38124034,  0.40720553],
           [ 0.39828826,  0.25732704,  0.21937875,  0.73758145]])
    

    矩陣轉置

    In [120]: A.T
    Out[120]: 
    array([[ 0.70961003,  0.42843928,  0.59779218,  0.39828826],
           [ 0.38232282,  0.10718751,  0.49990817,  0.25732704],
           [ 0.3333471 ,  0.60270476,  0.38124034,  0.21937875],
           [ 0.67033575,  0.17157395,  0.40720553,  0.73758145]])
    

    矩陣求逆

    In [119]: LA.inv(A)
    Out[119]: 
    array([[ 5.66701404, -0.28541964, -2.31329719, -3.80682588],
           [-3.70856223, -1.21853464,  4.49424461,  1.17271036],
           [-3.13121486,  2.0909343 ,  1.02248164,  1.79485974],
           [-0.83498553, -0.04266018, -0.62290378,  2.46845944]])
    

    矩陣乘法

    另外聲明一個矩陣B

    In [121]: B = np.random.rand(12).reshape((4, 3))
    
    In [123]: B
    Out[123]: 
    array([[ 0.53177202,  0.2393353 ,  0.28119078],
           [ 0.65747444,  0.07336546,  0.05293755],
           [ 0.61559046,  0.86580537,  0.91073328],
           [ 0.95129791,  0.14128977,  0.36143378]])
    

    矩陣相乘 dot

    In [124]: A.dot(B)
    Out[124]: 
    array([[ 1.47161254,  0.58120931,  0.76564731],
           [ 0.8325423 ,  0.65647117,  0.73706332],
           [ 1.26862768,  0.56736268,  0.68894366],
           [ 1.21769166,  0.40835537,  0.59199963]])
    

    行列式求解

    determinant

    In [126]: LA.det(A)
    Out[126]: -0.024091509709235282
    

    np.random 隨機函數模塊

    生成0-1區間的隨機數

    print('\n生成10個0-1區間內的隨機數值\n')
    print(np.random.rand(10))
    

    OUTPUT

    生成10個0-1區間內的隨機數值

    [ 0.62092807 0.79909637 0.29885985 0.3382252 0.50991553 0.1246462
    0.15690747 0.57804792 0.11380593 0.75276443]

    生成滿足正態分布的數組

    print('\n生成10個滿足正態分布的隨機數組\n')
    print(np.random.randn(10))
    

    OUTPUT

    生成10個滿足正態分布的隨機數組

    [-0.77878196 0.08298678 -1.1126773 -0.34096876 -1.39003661 -1.36326881
    1.14411379 -0.80727601 -0.94789066 0.01724758]

    拓展閱讀

    codingpy - 使用 Python 進行科學計算:NumPy入門

    語言比較風趣幽默, 翻譯自另外一片英文文章

    An Introduction to Scientific Python – NumPy


    韩国精品无码一区二区三区,精品无码一区二区三区AV,欧洲丰满美熟女乱又伦AV,亚洲午夜久久久影院伊人