1. 程式人生 > >Numpy的介紹與基本使用方法

Numpy的介紹與基本使用方法

1、什麼是Numpy

numpy官方文件:https://docs.scipy.org/doc/numpy/reference/?v=20190307135750

NumPy是一個功能強大的Python庫,主要用於對多維陣列執行計算。NumPy這個詞來源於兩個單詞-- Numerical和Python。
​ 它是 Python 生態系統中資料分析、機器學習和科學計算的主力軍。它極大地簡化了向量和矩陣的操作處理。Python 資料科學相關的一些主要軟體包(如 scikit-learn、SciPy、pandas 和 tensorflow)都以 NumPy 作為其架構的基礎部分。除了能對數值資料進行切片(slice)和切塊(dice)之外,使用 NumPy 還能為處理和除錯上述庫中的高階例項帶來極大便利組。它將常用的數學函式都支援向量化運算,使得這些數學函式能夠直接對陣列進行操作,將本來需要在Python級別進行的迴圈,放到C語言的運算中,明顯地提高了程式的運算速度。

2、為什麼要用Numpy

NumPy是Python中的一個運算速度非常快的一個數學庫,它非常重視陣列。它允許你在Python中進行向量和矩陣計算,並且由於許多底層函式實際上是用C編寫的,因此你可以體驗在原生Python中永遠無法體驗到的速度。

import numpy as np
import time

# 用python自帶方法處理
def func(values):
    result = []
    for v in values:
        result.append(v * v)
    return result
data = range(10000)
%timeit func(data)
執行結果:
1.07 ms ± 20.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

# 用numpy中的方法處理
arr = np.arange(0,10000)
%timeit arr ** arr
執行結果:
397 µs ± 32.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

NumPy絕對是科學Python成功的關鍵之一,如果你想要進入Python中的資料科學或機器學習,你就要必須學習它。從最後的執行結果來看numpy的處理速度要比python的處理速度快上十幾倍,當然這只是它的其中一項優勢,下面就通過一些具體的操作來看一看numpy的用法與優勢。

3、怎麼用Numpy

安裝方法:

pip install numpy

引用方式:

import numpy as np

這是官方認證的匯入方式,可能會有人說為什麼不用from numpy import *,是因為在numpy當中有一些方法與Python中自帶的一些方法,例如maxmin等衝突,為了避免這些麻煩大家就約定俗成的都使用這種方法。

Numpy的核心特徵就是N-維陣列對——ndarray.

3.1、為什麼要用ndarray?

numpy所有的操作都是圍繞著陣列展開的,這個陣列的名字就叫做ndarray,在學習ndarray陣列之前肯定有人會說這個東西和Python中的列表差不多啊,為什麼不用列表呢,列表還要方便些。其實列表list本身是為了處理更廣泛、更通用的目的而構建的,其實從這一方面來看ndarray對於處理這個陣列型別結構的資料會更加方便。

接下來我們可以通過具體的例項來展示一下ndarray的優勢。
現在有這樣一個需求:

已知若干家跨國公司的市值(美元),將其換算為人民幣

按照Python當中的方法
第一種:是將所有的美元通過for迴圈依次迭代出來,然後用每個公司的市值乘以匯率
第二種:通過map方法和lambda函式對映

這些方法相對來說也挺好用的,但是再來看通過ndarray物件是如何計算的

通過ndarray這個多維陣列物件可以讓這些批量計算變得更加簡單,當然這隻它其中一種優勢,接下來就通過具體的操作來發現。

3.2、ndarray-建立

方法 描述
array() 將列表轉換為陣列,可選擇顯式指定dtype
arange() range的numpy版,支援浮點數
linspace() 類似arange(),第三個引數為陣列長度
zeros() 根據指定形狀和dtype建立全0陣列
ones() 根據指定形狀和dtype建立全1陣列
empty() 根據指定形狀和dtype建立空陣列(隨機值)
eye() 根據指定邊長和dtype建立單位矩陣

1、arange():
np.arange(1.2,10,0.4)
執行結果:
array([1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. , 4.4, 4.8, 5.2, 5.6, 6. ,
       6.4, 6.8, 7.2, 7.6, 8. , 8.4, 8.8, 9.2, 9.6])
# 在進行資料分析的時候通常我們遇到小數的機會遠遠大於遇到整數的機會,這個方法與Python內建的range的使用方法一樣
-----------------------------------------------------------------
2、linspace()
np.linspace(1,10,20)
執行結果:
array([ 1.        ,  1.47368421,  1.94736842,  2.42105263,  2.89473684,
        3.36842105,  3.84210526,  4.31578947,  4.78947368,  5.26315789,
        5.73684211,  6.21052632,  6.68421053,  7.15789474,  7.63157895,
        8.10526316,  8.57894737,  9.05263158,  9.52631579, 10.        ])
# 這個方法與arange有一些區別,arange是顧頭不顧尾,而這個方法是顧頭又顧尾,在1到10之間生成的二十個數每個數字之間的距離相等的,前後兩個數做減法肯定相等
----------------------------------------------------------------
3、zeros()
np.zeros((3,4))
執行結果:
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
# 會用0生成三行四列的一個多維陣列
---------------------------------------------------------------------
4、ones()
np.ones((3,4))
執行結果:
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])
# 會用1生成三行四列的一個多維陣列
------------------------------------------------------------------------
5、empty()
np.empty(10)
執行結果:
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
# 這個方法只申請記憶體,不給它賦值
-----------------------------------------------------------------------
6、eye()
np.eye(5)
執行結果:
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 1.]])
# 對角矩陣

3.3、ndarray是一個多維陣列列表

接下來就多維陣列舉個例子:

為了建立一個2維陣列,我們是傳遞了一個列表的列表給這個array()函式。如果我們想要的是一個三維陣列,我們就必須要一個列表的列表的列表(也就是三層列表),以此類推。

很多情況下,處理一個新的維度只需要在numpy函式的引數中新增一個逗號

有的人可能會說了,這個陣列跟Python中的列表很像啊,它和列表有什麼區別呢?

'''
    在python中列表是可以存任意型別的值的,但是在陣列當中的元素必須型別必須相同。這是因為列表中存的只是每個元素的地址,不管執行多少次,值的位置是不會改變的,不需要在意資料的型別;而在ndarray當中存的是具體的值,每一次執行都是重新存放。
'''
l1 = ['1','2',4]
na = np.array(l1)
print(f"ndarry:{id(na[0])}")
print(f"list:{id(l1[0])}")

> ndarry:2140960887632
  list:2140897577592
"""
通過多次執行其實就可以發現,ndarray陣列的id值一直在不停的換,而list的id值始終保持不變
"""
  • 陣列物件內的元素型別必須相同
  • 陣列大小不可修改

3.4、常用屬性

屬性 描述
T 陣列的轉置(對高維陣列而言)
dtype 陣列元素的資料型別
size 陣列元素的個數
ndim 陣列的維數
shape 陣列的維度大小(以元組形式)
itemsize 每個項佔用的位元組數
nbytes 陣列中的所有資料消耗掉的位元組數
# T:轉置 轉置是一種特殊的資料重組形式,可以返回底層資料的檢視而不需要複製任何內容。
# 通俗點說,轉置就是將資料旋轉90度,行變成列,列變成行。

li1 = [
    [1,2,3],
    [4,5,6]
] 
a = np.array(li1)
a.T
執行結果:
array([[1, 4],
       [2, 5],
       [3, 6]])

# dtype:返回當前資料的資料型別
arr = np.arange(10)
arr.dtype
執行結果:
dtype('int32')

# size:返回當前陣列記憶體在的元素個數
l1 = [[[1,2,3],
       [4,5,6]],
     [[7,8,9],
     [1,5,9]
     ]]
arr1 = np.array(l1)
arr1.size
執行結果:
12

# ndim:返回當前陣列維度
l1 = [[[1,2,3],
       [4,5,6]],
     [[7,8,9],
     [1,5,9]
     ]]
arr1 = np.array(l1)
arr1.ndim
執行結果:
3

# shape:返回陣列維度大小
l1 = [[[1,2,3,4],
       [4,5,6,5],
      [6,8,3,6]],
     [[7,8,9,7],
     [1,5,9,7],
      [4,6,8,4]
     ]]
arr1 = np.array(l1)
arr1.shape
執行結果:
(2, 3, 4)
"""
最終三個引數代表的含義依次為:二維維度,三維維度,每個陣列內資料大小

要注意這些陣列必須要是相同大小才可以
"""

3.5、資料型別

  • dtype
型別 描述
布林型 bool_
整型 int_ int8 int16 int32 int 64
無符號整型 uint8 uint16 uint32 uint64
浮點型 float_ float16 float32 float64
整型:
int32只能表示(-2**31,2**31-1),因為它只有32個位,只能表示2**32個數

無符號整型:
只能用來存正數,不能用來存負數

"""
補充:
astype()方法可以修改陣列的資料型別
示例: 
data.astype(np.float)
"""

3.6、向量化數學運算

  • 陣列和標量(數字)之間運算
li1 = [
    [1,2,3],
    [4,5,6] 
] 
a = np.array(li1)
a * 2
執行結果:
array([[ 2,  4,  6],
       [ 8, 10, 12]])

與標量之間進行向量化運算,多維陣列與一維陣列沒有任何區別,都會將你要運算的數字對映到陣列中的每一個元素上進行運算

  • 同樣大小陣列之間的運算
# l2陣列
l2 = [
    [1,2,3],
    [4,5,6]
]
a = np.array(l2)

# l3陣列
l3 = [
    [7,8,9],
    [10,11,12]
]
b = np.array(l3)

a + b  # 計算

執行結果:
array([[ 8, 10, 12],
       [14, 16, 18]])

陣列與陣列之間的向量化運算,兩個運算的陣列必須相同大小,否則會報錯

3.7、索引和切片

  • 索引

一維索引使用與python本身的列表沒有任何區別,所以接下來主要針對大的是多維陣列

# np重塑
arr = np.arange(30).reshape(5,6) # 後面的引數6可以改為-1,相當於佔位符,系統可以自動幫忙算幾列

# 將二維變一維
arr.reshape(30)

# 索引使用方法
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]])

現在有這樣一組資料,需求:找到20

列表寫法:arr[3][2]
陣列寫法:arr[3,2]  # 中間通過逗號隔開就可以了

  • 切片
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]])

arr[0,1:4]  # >>array([1, 2, 3])
arr[1:4,0]  # >>array([ 6, 12, 18])
arr[::2,::2] # >>array([[ 0,  2,  4],
             #           [12, 14, 16],
             #           [24, 26, 28]])
arr[:,1]  # >>array([ 1,  7, 13, 19, 25])

切片不會拷貝,直接使用的原檢視,如果硬要拷貝,需要在後面加.copy()方法

最後會發現修改切片後的資料影響的依然是原資料。有的人可能對一點機制有一些不理解的地方,像Python中內建的都有賦值的機制,而Numpy去沒有,其實是因為NumPy的設計目的是處理大資料,所以你可以想象一下,假如NumPy堅持要將資料複製來複制去的話會產生何等的效能和記憶體問題。

  • 布林型索引

現在有這樣一個需求:給一個數組,選出陣列種所有大於5的數。

li = [random.randint(1,10) for _ in range(30)]
a = np.array(li)
a[a>5]
執行結果:
array([10,  7,  7,  9,  7,  9, 10,  9,  6,  8,  7,  6])
----------------------------------------------
原理:
a>5會對a中的每一個元素進行判斷,返回一個布林陣列
a > 5的執行結果:
array([False,  True, False,  True,  True, False,  True, False, False,
       False, False, False, False, False, False,  True, False,  True,
       False, False,  True,  True,  True,  True,  True, False, False,
       False, False,  True])
----------------------------------------------
布林型索引:將同樣大小的布林陣列傳進索引,會返回一個有True對應位置的元素的陣列

布林型索引是numpy當中的一個非常常用的用法,通過布林型索引取值方便又快捷。

4、通用函式

能對陣列中所有元素同時進行運算的函式就是通用函式

4.1、常見通用函式

能夠接受一個數組的叫做一元函式,接受兩個陣列的叫二元函式,結果返回的也是一個數組

  • 一元函式:
函式 功能
abs、fabs 分別是計算整數和浮點數的絕對值
sqrt 計算各元素的平方根
square 計算各元素的平方
exp 計算各元素的指數e**x
log 計算自然對數
sign 計算各元素的正負號
ceil 計算各元素的ceiling值
floor 計算各元素floor值,即小於等於該值的最大整數
rint 計算各元素的值四捨五入到最接近的整數,保留dtype
modf 將陣列的小數部分和整數部分以兩個獨立陣列的形式返回,與Python的divmod方法類似
isnan 判斷陣列中的缺失值
isinf 表示那些元素是無窮的布林型陣列
cos,sin,tan 普通型和雙曲型三角函式

以下簡單演示常用的幾個一元函式:

# 計算整數絕對值
arr = np.random.randint(-10,10,20)
np.abs(arr)
> array([9, 7, 3, 1, 5, 8, 7, 9, 4, 2, 7, 3, 4, 6, 6, 9, 2, 5, 8, 1])
-------------------------------------------------------------------------------
# 計算浮點數絕對值
arr = np.random.randn(2,5)
np.fabs(arr)
> array([[0.09892302, 0.06200835, 1.0324653 , 1.58089607, 0.44506652],
       [0.34897694, 1.04843539, 0.83976969, 0.4731551 , 0.92229931]])
------------------------------------------------------------------------------
# 計算各元素的平方根
arr = np.arange(10)
np.sqrt(arr)
> array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,
       2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])
  • 二元函式:
函式 功能
add 將陣列中對應的元素相加
subtract 從第一個陣列中減去第二個陣列中的元素
multiply 陣列元素相乘
divide、floor_divide 除法或向下圓整除法(捨棄餘數)
power 對第一個陣列中的元素A,根據第二個陣列中的相應元素B計算A**B
maximum,fmax 計算最大值,fmax忽略NAN
miximum,fmix 計算最小值,fmin忽略NAN
mod 元素的求模計算(除法的餘數)
arr = np.random.randint(0,10,5)
arr1 = np.random.randint(0,10,5)
arr,arr1
> (array([0, 1, 8, 2, 6]), array([5, 4, 1, 7, 0]))
-------------------------------------------------------------------------
# add 將陣列中對應的元素相加
np.add(arr,arr1)
> array([5, 5, 9, 9, 6])
-------------------------------------------------------------------------
# subtract 從第一個陣列中減去第二個陣列中的元素
np.subtract(arr,arr1)
> array([-5, -3,  7, -5,  6])
-------------------------------------------------------------------------
# multiply 陣列元素相乘
np.multiply(arr,arr1)
> array([ 0,  4,  8, 14,  0])
-------------------------------------------------------------------------
...

補充內容:浮點數特殊值

浮點數:float
浮點數有兩個特殊值:

1、nan(Not a Number):不等於任何浮點數(nan != nan)
---------------------------------------------
2、inf(infinity):比任何浮點數都大
---------------------------------------------
  • Numpy中建立特殊值:np.nan、np.inf
  • 資料分析中,nan常被用作表示資料缺失值

以上函式使用非常方便,使用這些方法可以讓資料分析的操作更加便捷。

4.2、數學統計方法

函式 功能
sum 求和
cumsum 求字首和
mean 求平均數
std 求標準差
var 求方差
min 求最小值
max 求最大值
argmin 求最小值索引
argmax 求最大值索引
arr = np.random.randint(0,10,10)
arr
> array([2, 9, 6, 5, 4, 2, 9, 8, 0, 5])
-------------------------------------------------------------------------
# sum 求和
np.sum(arr)
> 50
-------------------------------------------------------------------------
# cumsum 求字首和
np.cumsum(arr)
array([ 2, 11, 17, 22, 26, 28, 37, 45, 45, 50], dtype=int32) # 依次累加
-------------------------------------------------------------------------
# mean 求平均數
np.mean(arr)
> 5.0
-------------------------------------------------------------------------
# 由於此檔內容太過簡單,後續就不一一展示了
...

4.3、隨機數

我們有學過python中生成隨機數的模組random,在numpy中也有一個隨機數生成函式,它在np.random的子包當中。在Python自帶的random當中只能生成一些簡單、基礎的隨機數,而在np.random當中是可以生成一些高階的隨機數的。np.random要比Python自帶的random包更加靈活。

函式 功能
rand 返回給定維度的隨機陣列(0到1之間的數)
randn 返回給定維度的隨機陣列
randint 返回給定區間的隨機整數
choice 給定的一維陣列中隨機選擇
shuffle 原列表上將元素打亂(與random.shuffle相同)
uniform 給定形狀產生隨機陣列
seed 設定隨機種子(使相同引數生成的隨機數相同)
standard_normal 生成正態分佈的隨機樣本數
# rand 返回給定維度的隨機陣列
np.random.rand(2,2,2)
> array([[[0.37992696, 0.18115096],
        [0.78854551, 0.05684808]],

       [[0.69699724, 0.7786954 ],
        [0.77740756, 0.25942256]]])
-------------------------------------------------------------------------
# randn 返回給定維度的隨機陣列
np.random.randn(2,4)
> array([[ 0.76676877,  0.21752554,  2.08444169,  1.51347609],
       [-2.10082473,  1.00607292, -1.03711487, -1.80526763]])
-------------------------------------------------------------------------
# randint 返回給定區間的隨機整數
np.random.randint(0,20)
> 15
-------------------------------------------------------------------------
# chocie 給定的一維陣列中隨機選擇
np.random.choice(9,3)  # # 從np.range(9)中,(預設)有放回地等概率選擇三個數
> array([5, 8, 2])
np.random.choice(9,3,replace=False)  # 無放回地選擇
> array([1, 2, 0])
------------------------------------------------------------------------
# shuffle 原列表上將元素打亂(與random.shuffle相同)
arr = np.arange(10)
np.random.shuffle(arr)
arr
> array([4, 5, 0, 3, 7, 8, 9, 1, 2, 6])
------------------------------------------------------------------------
# uniform 給定形狀產生隨機陣列
np.random.uniform(0,1,[3,3,3])
> array([[[0.80239474, 0.37170323, 0.5134832 ],
        [0.42046889, 0.40245839, 0.0812019 ],
        [0.8788738 , 0.48545176, 0.73723353]],

       [[0.79057724, 0.80644632, 0.65966656],
        [0.43833643, 0.53994887, 0.46762885],
        [0.44472436, 0.08944074, 0.34148912]],

       [[0.7042795 , 0.58397044, 0.13061102],
        [0.22925123, 0.97745023, 0.14823085],
        [0.6960559 , 0.07936633, 0.91221842]]])
------------------------------------------------------------------------
# seed 設定隨機種子(使相同引數生成的隨機數相同)
np.random.seed(0)  # 當seed(0)時生成以下隨機陣列
np.random.rand(5)
> array([0.5488135 , 0.71518937, 0.60276338, 0.54488318, 0.4236548 ])
np.random.seed(2)  # send(5)時生成以下隨機陣列
np.random.rand(5)
> array([0.5488135 , 0.71518937, 0.60276338, 0.54488318, 0.4236548 ])
np.random.seed(0)  # 再次使用send(0)會發現返回最開始的隨機陣列
np.random.rand(5)
> array([0.5488135 , 0.71518937, 0.60276338, 0.54488318, 0.4236548 ])
------------------------------------------------------------------------
# standard_normal 生成正態分佈的隨機樣本數
np.random.standard_normal([2,10])
> array([[-0.17937969, -0.69277058,  1.13782687, -0.16915725, -0.76391367,
        -0.4980731 , -0.36289111,  0.26396031, -0.62964191, -0.4722584 ],
       [-1.51336104,  1.10762468,  0.17623875, -0.94035354,  0.92959433,
        -1.06279492, -0.88640627,  1.92134696, -0.45978052, -1.08903444]])

np.random和Python原生的random的區別:

比較內容 random np.random
輸入型別 非空的列表型別(包括列表、字串和元組) 非空的列表型別(包括列表、字串和元組)+ numpy.array型別
輸出維度 一個數或一個list(多個數) 可指定複雜的size
指定(a,b)範圍 可以 整數可指定,浮點數不行,需自行轉換
批量輸出 不可 可。通過指定size引數
特定分佈 涵蓋了常用的幾個分佈; 只能單個輸出 幾乎涵蓋了所有分佈;可批量輸出

只要能把以上所有的內容掌握,資料分析這門功夫你就算是打通了任督二脈了,學起來輕鬆又愉快。

5、資料表示

所有需要處理和構建模型所需的資料型別(電子表格、影象、音訊等),其中很多都適合在 n 維陣列中表示:

表格和電子表格

表格和電子表格是二維矩陣。電子表格中的每個工作表都可以是它自己的變數。python 中最流行的抽象是 pandas 資料幀,它實際上使用了 NumPy 並在其之上構建。

音訊和時間序列

音訊檔案是樣本的一維陣列。每個樣本都是一個數字,代表音訊訊號的一小部分。CD 質量的音訊每秒包含 44,100 個樣本,每個樣本是-65535 到 65536 之間的整數。這意味著如果你有一個 10 秒的 CD 質量 WAVE 檔案,你可以將它載入到長度為 10 * 44,100 = 441,000 的 NumPy 陣列中。如果想要提取音訊的第一秒,只需將檔案載入到 audio 的 NumPy 陣列中,然後獲取 audio[:44100]。

以下是一段音訊檔案:

時間序列資料也是如此(如股票價格隨時間變化)。

影象

影象是尺寸(高度 x 寬度)的畫素矩陣。

如果影象是黑白(即灰度)的,則每個畫素都可以用單個數字表示(通常在 0(黑色)和 255(白色)之間)。想要裁剪影象左上角 10 x 10 的畫素嗎?在 NumPy 寫入即可。

下圖是一個影象檔案的片段:

如果影象是彩色的,則每個畫素由三個數字表示——紅色、綠色和藍色。在這種情況下,我們需要一個三維陣列(因為每個單元格只能包含一個數字)。因此彩色影象由尺寸為(高 x 寬 x3)的 ndarray 表示:

6、總結

使用numpy,可以為我們提供一組豐富而又靈活的資料結構,以金融的角度來看的話下面幾種型別是最重要的:

基本資料型別

在金融量化當中,整數、浮點數和字串給我們提供了原子資料型別

標準資料結構

元組、列表、字典和集合,這些在金融當中有許多應用領域,列表通常是最為常用的

陣列

說到陣列肯定就是今天所學習的numpy中的ndarray陣列,它對於資料的處理效能更高,程式碼更簡潔、方