介绍

NumPy 是一个运行速度非常快的数学库,主要用于数组计算,包含:

  • 一个强大的N维数组对象 ndarray
  • 广播功能函数
  • 整合 C/C++/Fortran 代码的工具
  • 线性代数、傅里叶变换、随机数生成等功能

另外,NumPy 还会经常与 SciPy(Scientific Python)和 Matplotlib(绘图库)一起使用。

安装

pip安装:

pip install numpy

导入模块

导入之后通常会取个别名:

import numpy as np

数组

数组是一个值网格,所有类型都是相同类型的。和原生的列表(list)比较,列表里的每个元素可以是不同的类型。

原生数组

python也是有数组的:

>>> import array  # 导入模块
>>> a1 = array.array('i')  # 创建了一个数组,类型是i,就是int
>>> a1.append(1)  # 添加一个元素
>>> a1.append(2)
>>> a1
array('i', [1, 2])
>>> len(a1)
2

这部分不是重点,就提一下。

创建数组

从列表生成数组

>>> l1 = [1, 2, 3, 4, 5]
>>> l2 = np.array(l1)
>>> l2
array([1, 2, 3, 4, 5])
>>> type(l1)
<class 'list'>
>>> type(l2)
<class 'numpy.ndarray'>

全0的数组

>>> np.zeros(3)
array([ 0.,  0.,  0.])
>>> np.zeros(3, dtype=int)  # 默认是浮点数,可以指定类型
array([0, 0, 0])

全1的数组

>>> np.ones(3, int)
array([1, 1, 1])

多维数组
生成数组时,第一个参数,是可以指定维度的:

>>> np.ones((2, 4), int)
array([[1, 1, 1, 1],
       [1, 1, 1, 1]])
>>> np.ones((3, 3), int)
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])
>>> 

创建时填充其他值

>>> np.full((2,3), 'x')
array([['x', 'x', 'x'],
       ['x', 'x', 'x']],
      dtype='<U1')
>>> 

按一个数组为模板创建数组
维度和元素类型都是一样的,所以1.23被自动转成了整数:

>>> t1 = np.zeros((3, 2), int)  # 3*2 的int类型的数组
>>> np.full_like(t1, 1.23)
array([[1, 1],
       [1, 1],
       [1, 1]])
>>> 

生成单位矩阵

>>> np.eye(3, dtype=int)
array([[1, 0, 0],
       [0, 1, 0],
       [0, 0, 1]])
>>> np.eye(3, k=1, dtype=int)
array([[0, 1, 0],
       [0, 0, 1],
       [0, 0, 0]])
>>> np.eye(3, 2, dtype=int)
array([[1, 0],
       [0, 1],
       [0, 0]])

在次对角线上生成全1的矩阵:

>>> np.eye(3, dtype=int)[::-1]
array([[0, 0, 1],
       [0, 1, 0],
       [1, 0, 0]])

随机数数组

原生的random库也能生成随机数,不过NumPy库的随机数功能更强大

原生random模块

>>> random.random()  # 原生函数没有参数,只能生成1个数
0.3145225040001337
>>> np.random.random(3)  # 生成随机数组
array([ 0.5298384 ,  0.34748761,  0.52159409])

生成随机数数组

随机整数

>>> np.random.randint(1, 100, 2)
array([99, 77])

指定维度

>>> np.random.random((3, 3))
array([[ 0.72420303,  0.31726755,  0.39925357],
       [ 0.57047567,  0.96920481,  0.06743519],
       [ 0.76589897,  0.9075956 ,  0.22296356]])
>>> np.random.randint(1, 10, (2, 5))
array([[7, 5, 2, 4, 6],
       [2, 8, 3, 6, 8]])

取值范围

np.arange
与原生的range差不多,可以指定起始、结束、步长,三个参数:

>>> np.arange(2)
array([0, 1])
>>> np.arange(3)
array([0, 1, 2])
>>> np.arange(1, 9)
array([1, 2, 3, 4, 5, 6, 7, 8])
>>> np.arange(1, 9, 3)
array([1, 4, 7])
>>> np.arange(1.0, 5.0, 0.7)
array([ 1. ,  1.7,  2.4,  3.1,  3.8,  4.5])

np.linspace
给定起始和结束的值,自动计算中间的间隔,默认生成50个数:

>>> np.linspace(1, 2)
array([ 1.        ,  1.02040816,  1.04081633,  1.06122449,  1.08163265,
        1.10204082,  1.12244898,  1.14285714,  1.16326531,  1.18367347,
        1.20408163,  1.2244898 ,  1.24489796,  1.26530612,  1.28571429,
        1.30612245,  1.32653061,  1.34693878,  1.36734694,  1.3877551 ,
        1.40816327,  1.42857143,  1.44897959,  1.46938776,  1.48979592,
        1.51020408,  1.53061224,  1.55102041,  1.57142857,  1.59183673,
        1.6122449 ,  1.63265306,  1.65306122,  1.67346939,  1.69387755,
        1.71428571,  1.73469388,  1.75510204,  1.7755102 ,  1.79591837,
        1.81632653,  1.83673469,  1.85714286,  1.87755102,  1.89795918,
        1.91836735,  1.93877551,  1.95918367,  1.97959184,  2.        ])

指定总数:

>>> np.linspace(1, 10, 5, dtype=int)
array([ 1,  3,  5,  7, 10])

不包括结尾的数:

>>> np.linspace(1, 10, 5, endpoint=True, dtype=int)
array([ 1,  3,  5,  7, 10])
>>> np.linspace(1, 10, 5, endpoint=False, dtype=int)
array([1, 2, 4, 6, 8])

显示步长:

>>> np.linspace(1, 10, 5, retstep=True)
(array([  1.  ,   3.25,   5.5 ,   7.75,  10.  ]), 2.25)

下面先设置了输出小数和输出精度,然后计算了将一个圆周切分n份的横坐标(sin值)和纵坐标(cos值),最后再把之前的设置调回默认值:

>>> np.set_printoptions(precision=3, suppress=True)
>>> np.sin(np.linspace(0, np.pi*2, 8, endpoint=False))
array([ 0.   ,  0.707,  1.   ,  0.707,  0.   , -0.707, -1.   , -0.707])
>>> np.cos(np.linspace(0, np.pi*2, 8, endpoint=False))
array([ 1.   ,  0.707,  0.   , -0.707, -1.   , -0.707, -0.   ,  0.707])
>>> np.set_printoptions(edgeitems=3,infstr='inf', linewidth=75, nanstr='nan', precision=8, suppress=False, threshold=1000, formatter=None)

操作数组

访问元素

访问元素可以使用传统的多维数组访问方式来访问:

>>> a2 = np.zeros((3, 3))
>>> a2
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
>>> a2[0][0] = 2  # 传统的访问方式
>>> a2[0, 0]  # 看着和上面一样
2.0

不过上面 [0, 0] 这种形式的功能更加强大,因为这样支持数组切片:

>>> a2[1:,1:] = 5
>>> a2
array([[ 2.,  0.,  0.],
       [ 0.,  5.,  5.],
       [ 0.,  5.,  5.]])

比较一下两个访问形式在多维数组情况时的区别:

>>> a2[1:, 1:]
array([[ 5.,  5.],
       [ 5.,  5.]])
>>> a2[1:][1:]
array([[ 0.,  5.,  5.]])

传统的方式是一种链式操作,是在前一次切片的基础上再执行切片:

>>> tmp = a2[1:]
>>> tmp[1:]
array([[ 0.,  5.,  5.]])
>>> a2[1:][1:]
array([[ 0.,  5.,  5.]])

数组属性

维度
继续使用上面的二维数组:

>>> a2.ndim
2
>>> a2[1].ndim  # 取出其中一个值,就是个一维数组了
1

形状

>>> a2.shape
(3, 3)
>>> a2[1:,].shape
(2, 3)

元素总数

>>> a2.size
9
>>> a2[1:, 1:].size
4

元素类型

>>> a2.dtype
dtype('float64')

占用的字节数

>>> a2.itemsize  # 每个用上占用的字节数
8
>>> a2.nbytes  # 整个数组占用的字节数
72

float64类型,占8字节。类型不同占用的字节数就不一样,另外字符串的情况还更复杂的样子。不过主要用来做数学计算的:

>>> np.array([1, 2, 3]).itemsize
4
>>> np.array(['a']).itemsize
4
>>> np.array(['a', 'ij', 'xyz']).itemsize
12
>>> np.array(['a', 'ij', 'xyz']).nbytes
36

数组操作

变形
变形前后要求元素的总数size要一样:

>>> np.eye(4 ,4)
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])
>>> np.eye(4, 4).reshape(2, 8)
array([[ 1.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  1.]])

排序

>>> sort0 = np.random.randint(0, 10, (3, 3))
>>> sort0
array([[0, 5, 9],
       [7, 7, 7],
       [7, 3, 6]])
>>> np.sort(sort0)  # 默认横向排序
array([[0, 5, 9],
       [7, 7, 7],
       [3, 6, 7]])
>>> np.sort(sort0, axis=None)
array([0, 3, 5, 6, 7, 7, 7, 7, 9])
>>> np.sort(sort0, axis=0)  # 0是纵向排序
array([[0, 3, 6],
       [7, 5, 7],
       [7, 7, 9]])

拼接

>>> c1 = np.array((1, 2, 3))
>>> c2 = np.array((9, 8, 7))
>>> np.concatenate((c1, c2, c1))
array([1, 2, 3, 9, 8, 7, 1, 2, 3])

axis只有在多维数组拼接时候可以设置为1,拼接的效果也不同:

>>> c3 = [[1, 2], [3, 4]]
>>> c4 = [[5, 6], [7, 8]]
>>> np.concatenate((c3, c4), axis=0)
array([[1, 2],
       [3, 4],
       [5, 6],
       [7, 8]])
>>> np.concatenate((c3, c4), axis=1)
array([[1, 2, 5, 6],
       [3, 4, 7, 8]])

统计
简单的求和,和原生数组没差别:

>>> sum(np.arange(1, 101))
5050

以二维数组举例,可以对横向纵向每个维度进行求和:

>>> np.array([i+1 for i in range(9)]).reshape(3, 3)
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>> a3 = np.array([i+1 for i in range(9)]).reshape(3, 3)
>>> np.sum(a3)
45
>>> np.sum(a3, axis=0)
array([12, 15, 18])
>>> np.sum(a3, axis=1)
array([ 6, 15, 24])

转置

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

反转
python的反转很方便,所以没有专门的方法:

>>> [1, 2, 3][::-1]
[3, 2, 1]

旋转
配合转置和反转就可以实现:

>>> a4 = np.array([i+1 for i in range(12)]).reshape(3, 4)
>>> a4
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> np.transpose(a4[::-1])
array([[ 9,  5,  1],
       [10,  6,  2],
       [11,  7,  3],
       [12,  8,  4]])
>>> np.transpose(a4[:, ::-1])
array([[ 4,  8, 12],
       [ 3,  7, 11],
       [ 2,  6, 10],
       [ 1,  5,  9]])