量化百科

Python最好用的科学计算库:NumPy快速入门教程(一)

由iquant创建,最终由iquant 被浏览 25 用户

首先导入并检查你的numpy版本

import numpy
numpy.__version__
'1.14.3'

NumPy数组

NumPy数组指的是由同一类型元素(一般是数字)组成的多维数组。

基础知识

数组的维度、形状、大小

一维数组由一个中括号内的数字组成。

import numpy as np #这里的一个惯例是使用np作为numpy的别名
>>>a = np.array([1,2,3])
>>>a
array([1, 2, 3])

二维数组由多个一维数组组成

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

同理,n维数组由多个n-1维数组组成

>>>c = np.array([[[1,2,1],[1,3,2,]],[[2,2,2],[1,1,1]]])
>>>c
array([[[1, 2, 1],
        [1, 3, 2]],

       [[2, 2, 2],
        [1, 1, 1]]])

查看数组的维度使用np.ndim

>>>print('数组a的维度是:',a.ndim)
>>>print('数组b的维度是:',b.ndim)
>>>print('数组c的维度是:',c.ndim)
数组a的维度是: 1
数组b的维度是: 2
数组c的维度是: 3

np.shape:显示在每个维度里数组的大小,或者说数组的“形状”。如 n 行 m 列的矩阵,它的 shape 就是(n,m)。

>>>print('数组a的形状是:',a.shape)
>>>print('数组b的形状是:',b.shape)
>>>print('数组c的形状是:',c.shape)
数组a的形状是: (3,)
数组b的形状是: (3, 3)
数组c的形状是: (2, 2, 3)

np.size:数组中所有元素的总量,相当于数组的 shape 中所有元素的乘积,例如矩阵的元素总量为行与列的乘积。

>>>print('数组a的大小是:',a.size)
>>>print('数组b的大小是:',b.size)
>>>print('数组c的大小是:',c.size)
数组a的大小是: 3
数组b的大小是: 9
数组c的大小是: 12

数组的元素类型和存储大小

np.dtype:查看数组中元素的类型。创建数组时,可以指定dtype参数用于创建指定数据类型的数组

>>>a.dtype
dtype('int64')
>>>d = np.array([1.2, 2.3, 3.4], dtype = np.float)
>>>d
array([1.2, 2.3, 3.4])

np.itemsize:用于查看数组中元素占用的字节,例如,一个元素类型是float64的数组,其中的元素占用的字节大小是8(也就是64bit/8)

>>>d.itemsize
8

np.data:查看存储数组的真实内存地址

>>>d.data
<memory at 0x10bf10ac8>

创建数组


有很多种方法创建数组。例如,你可以通过使用数组函数从一个python的列表或元组创建。数组元素的类型由原数据的类型推断得到。

>>>b = np.array([1.2, 3.5, 5.1])
>>>b.dtype
dtype('float64')

np.array创建数组时,会将多层嵌套的列表转换为多维数组。

>>>b = np.array([(1.5,2,3), (4,5,6)])
>>>b
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

创建数组时我们还可以指定数据类型

>>>c = np.array( [ [1,2], [3,4] ], dtype=complex )#指定数据类型为复数
>>>c
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])

我们经常遇到这样的情况,我们并不知道数组的元素,但是知道数组的大小。因此,NumPy提供了多个函数,用于创建有初始数值的占位数组,这样可以减少不必要的数组增长及运算成本。

zeros函数创建包含全是0的数组,ones函数创建全是1的数组,empty函数创建一个随机数值数组,其中的数值由当时的内存状态决定。这些函数创建的数组的数据类型都是默认的float64.

>>>np.zeros( (3,4) )
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
>>>np.ones( (2,3,4), dtype=np.int16 )
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int16)
>>>np.empty( (2,3) )
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

为了生成连续的数值,NumPy提供了一个模拟range的函数,不同点是,numpy的range返回的是数组而不是列表

>>>np.arange( 10, 30, 5 ) # 三个参数分别代表起始值10,终止值30,步进5
array([10, 15, 20, 25])
>>>np.arange( 0, 2, 0.3 )
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

当 arange 使用浮点型作为参数时,因为浮点精度的有限性,不能预测创建的数组有多少个元素。在这种情况下,换成linspace函数可以更好地确定区间内到底需要产生多少个数组元素。

from numpy import pi
>>>np.linspace( 0, 2, 9 ) # 在[0,2]区间内生成9个值
array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])

打印数组

当你在屏幕打印一个数组时,NumPy显示这个数组的方式和嵌套的列表是相似的。但遵守以下布局:

  • 最后一维由左至右打印
  • 倒数第二维从上到下打印
  • 其余维都是从上到下打印,且通过空行分隔

如下所示,一维数组输出为一行、二维为矩阵、三维为矩阵列表。

>>>a = np.arange(6)  
>>>a
array([0, 1, 2, 3, 4, 5])
>>>b = np.arange(12).reshape(4,3)
>>>a
array([0, 1, 2, 3, 4, 5])
>>>c = np.arange(24).reshape(2,3,4)
>>>c
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]]])

如果数组太大了,NumPy自动跳过中间的部分不显示,只显示两边。

>>>print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>print(np.arange(10000).reshape(100,100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]

如果你想强制输出所有数据,可以设置set_printoptions参数。

>>>np.set_printoptions(threshold=np.nan)

基本运算

数组中的算术运算一般是元素级的运算,运算结果会产生一个新的数组。

不同于很多矩阵语言,乘积运算操作*在NumPy中是元素级的运算。如果想要进行矩阵运算,可以使用dot函数或方法。

>>>A = np.array( [[1,1],[0,1]] )
>>>B = np.array( [[2,0],[3,4]] )
>>>A*B# 元素乘积
array([[2, 0],
       [0, 4]])
>>>A.dot(B)# 矩阵运算
array([[5, 4],
       [3, 4]])
>>>np.dot(A, B)# 另一种方式矩阵运算
array([[5, 4],
       [3, 4]])

一些运算,例如+=*=,会内在的改变一个数组的值,而不是生成一个新的数组。

>>>a = np.ones((2,3), dtype=int)
>>>b = np.random.random((2,3))
>>>a *= 3
>>>a
array([[3, 3, 3],
       [3, 3, 3]])
>>>b += a
>>>b
array([[3.69902298, 3.1334804 , 3.62673199],
       [3.37038178, 3.74769131, 3.62235315]])
>>>a += b # b不会自动转换为整型
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-37-175b578703ae> in <module>()
----> 1 a += b # b不会自动转换为整型


TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64') with casting rule 'same_kind'

当操作不同数据类型的数组时,最后输出的数组类型一般会与更普遍或更精准的数组相同(这种行为叫做 Upcasting)。

>>>a = np.ones(3, dtype=np.int32)
>>>b = np.linspace(0,pi,3)
>>>b.dtype.name
'float64'
>>>c = a+b
>>>c.dtype.name
'float64'
>>>d = np.exp(c*1j)
>>>d.dtype.name
'complex128'

许多一元运算,如计算数组中所有元素的总和,是属于 ndarray 类的方法。

>>>a = np.random.random((2,3))
>>>a
array([[0.85827711, 0.5385761 , 0.0843277 ],
       [0.2609027 , 0.36414539, 0.12940627]])
>>>a.sum()
2.2356352707158513
>>>a.min()
0.08432769616897462
>>>a.max()
0.8582771053112916

默认状态下,这些运算会把数组视为一个数字列表而不关心它的shape。然而,可以指定axis参数针对哪一个维度进行运算。例如axis=0将针对每一个列进行运算。

>>>b = np.arange(12).reshape(3,4)
>>>b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>b.sum(axis=0)# 列相加
array([12, 15, 18, 21])
>>>b.min(axis=1) #行相加
array([0, 4, 8])
>>>b.cumsum(axis=1)#行累加
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]])

通用函数

NumPy提供一些熟悉的数学函数,例如sin, cos,和exp等。在NumPy中,这些函数称为“通用函数”, 这些运算是元素级的,生成一个数组作为结果。

>>>B = np.arange(3)
>>>B
array([0, 1, 2])
>>>np.exp(B)
array([1.        , 2.71828183, 7.3890561 ])
>>>np.sqrt(B)
array([0.        , 1.        , 1.41421356])
>>>C = np.array([2., -1., 4.])
>>>np.add(B, C)
array([2., 0., 6.])

索引、切片和迭代

一维数组可以索引、切片和迭代,就像列表和其他python数据类型。

>>>a = np.arange(10)**3
>>>a[2]
8
>>>a[2:5]
array([ 8, 27, 64])
>>>a[:6:2] = -1000 #从0到6,每隔2个设为-1000
>>>a
array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,
         729])
>>>a[ : :-1] #翻转数组a
array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1,
       -1000])

多维数组可以每个维度有一个索引值。这些索引值被逗号分开。

>>>def f(x,y):#定义一个函数用于生成数组
>>>    return 10*x+y
>>>b = np.fromfunction(f,(5,4),dtype=int)#利用函数f生成数组,数组的形状是(5,4),数组中(x,y)的元素值等于 f(x,y)
>>>b
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33],
       [40, 41, 42, 43]])
>>>b[2,3] #第2行,第3列
23
>>>b[0:5, 1] #等于b[ : ,1]  ,第0-5行,第1列
array([ 1, 11, 21, 31, 41])
>>>b[1:3, : ]  #1-3行,所有列
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

当索引值的数量少于数组的维度时,其他的索引值默认为分号

>>>b[-1] #相当于b[-1,:]
array([40, 41, 42, 43])


b[i]中的i代表i后面有足够多的:,用于表示其他维度的索引。你也可以使用点号...来表示。点号代表需要的足够多的列,用于使其他维度的索引值完整。例如,x是一个五维数组,那么

  • x[1,2,...] 相当于 x[1,2,:,:,:]

  • x[...,3] 相当于 x[:,:,:,:,3]

  • x[4,...,5,:] 相当于 x[4,:,:,5,:]

    c = np.array( [[[ 0, 1, 2], #构建一个三维数组 [ 10, 12, 13]], [[100,101,102], [110,112,113]]])

    c.shape (2, 2, 3)

    c[1,...] array([[100, 101, 102], [110, 112, 113]])

    c[...,2] array([[ 2, 13], [102, 113]])

多维数组中的迭代:

>>>for row in b:
>>>    print(row)
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

但是,如果你想迭代输出数组中的每一个元素,你可以使用flat属性实现,这个属性生成一个可以迭代所有元素的迭代器。

>>>for element in b.flat:
>>>    print(element)
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43

\

标签

NumPyPythonNumPy基础Numpy