教程较长, 故分三次写完.
NumPy 是Python中一个运行速度非常快的数学库, 主要用于数组计算.
那么为什么有了列表, 我们还需要使用numpy呢. 当然就是快, 非常快, 超级快!
Python中的列表, 要想找到列表中对应的元素需要先通过寻找该元素的地址才可以访问到元素, 因此速度会慢一些. 而Python中的numpy是存储在一片连续的地址空间上的, 因此访问元素速度更快.
import numpy as np
import time
import random
# 生成随机列表
a = []
start_time_1 = time.time()
for i in range(1000000):
a.append(random.randint(0, 1000000))
end_time_1 = time.time()
print(f"生成随机列表用时: {end_time_1 - start_time_1} 秒")
# 使用 Python 内置的 sum 函数求和
start_time_2 = time.time()
sum1 = sum(a)
end_time_2 = time.time()
print(f"Python 内置 sum 函数求和用时: {end_time_2 - start_time_2} 秒")
# 将列表转换为 NumPy 数组
b = np.array(a)
# 使用 NumPy 的 sum 函数求和
start_time_3 = time.time()
sum2 = np.sum(b)
end_time_3 = time.time()
print(f"NumPy 的 sum 函数求和用时: {end_time_3 - start_time_3} 秒")
# 验证结果是否一致
print(f"Python 内置 sum 函数结果: {sum1}")
print(f"NumPy 的 sum 函数结果: {sum2}")
输出结果
生成随机列表用时: 0.3070411682128906 秒
Python 内置 sum 函数求和用时: 0.011229515075683594 秒
NumPy 的 sum 函数求和用时: 0.0010001659393310547 秒
Python 内置 sum 函数结果: 499988072997
NumPy 的 sum 函数结果: 499988072997
由此可见numpy的效率更高.
NumPy
通常与SciPy
(Scientific Python)和 Matplotlib
(绘图库)一起使用, 这种组合广泛用于替代 MatLab, 是一个强大的科学计算环境, 有助于我们通过 Python 学习数据科学或者机器学习.
NumPy 官网: http://www.numpy.org/
NumPy 源代码: https://github.com/numpy/numpy
SciPy 官网: https://www.scipy.org/
SciPy 源代码: https://github.com/scipy/scipy
Matplotlib 官网: https://matplotlib.org/
Matplotlib 源代码: https://github.com/matplotlib/matplotlib
这里我们默认你具有一定的Python基础, 懂得一点Python面向对象和模块的使用.
NumPy 安装
使用 pip 安装
在终端输入
pip install numpy
便会自动下载numpy
库, 也可以从清华镜像站下载
pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
使用 conda 安装
如果使用的是 Anaconda 环境, 可以使用 conda 来安装 NumPy
conda install numpy
安装验证
可以通过以下代码令验证NumPy
是否安装成功
import numpy as np
print(np.__version__)
安装成功后会输出版本号.
Ndarray 对象
NumPy 最重要的一个特点是其 N 维数组对象 ndarray, 它是一系列同类型数据的集合, 以 0 下标为开始进行集合中元素的索引.
ndarray 对象是用于存放同类型元素的多维数组, ndarray 中的每个元素在内存中都有相同存储大小的区域.
ndarray 内部由以下内容组成:
- 一个指向数据(内存或内存映射文件中的一块数据)的指针;
- 数据类型或 dtype, 描述在数组中的固定大小值的格子;
- 一个表示数组形状(shape)的元组, 表示各维度大小的元组;
- 一个跨度元组(stride), 其中的整数指的是为了前进到当前维度下一个元素需要跨过的字节数.
跨度可以是负数, 这样会使数组在内存中后向移动, 切片中obj[::-1]
或 obj[:,::-1]
就是如此.
创建一个 ndarray 只需调用 NumPy 的 array
函数即可
numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
参数说明:
- `object`: 数组或嵌套的数列;
- `dtype`: 数组元素的数据类型, 可选;
- `copy`: 对象是否需要复制, 可选;
- `order`: 创建数组的样式, C为行方向, F为列方向, A为任意方向(默认);
- `subok`: 默认返回一个与基类类型一致的数组;
- `ndmin`: 指定生成数组的最小维度.
#创建一个一维数组
import numpy as np
a = np.array([1,2,3])
print (a)
'''
[1 2 3]
'''
# 多于一个维度
import numpy as np
a = np.array([[1, 2], [3, 4]]) #这里创建了一个二维数组
print (a)
'''
[[1 2]
[3 4]]
'''
# 最小维度
import numpy as np
a = np.array([1, 2, 3, 4, 5], ndmin = 2)
#这里设置为 2, 表示创建的数组至少是二维的. 因此原本一维的列表[1, 2, 3, 4, 5]会被转换为一个二维数组
print (a)
'''
原本一维的列表被转换为了一个形状为(1, 5)的二维数组, 即包含 1 行 5 列的数组
[[1 2 3 4 5]]
'''
# dtype 参数
import numpy as np
a = np.array([1, 2, 3], dtype = complex) #dtype指定为复数
print (a)
'''
[1.+0.j 2.+0.j 3.+0.j]
'''
NumPy 数据类型
数据类型对象
numpy 支持的数据类型比 Python 内置的类型要多很多, 基本上可以和 C 语言的数据类型对应上, 其中部分类型对应为 Python 内置的类型.
名称 | 描述 |
---|---|
bool_ | 布尔型数据类型(True 或者 False ) |
int_ | 默认的整数类型(类似于 C 语言中的 long, int32 或 int64) |
intc | 与 C 的 int 类型一样, 一般是 int32 或 int 64 |
intp | 用于索引的整数类型(类似于 C 的 ssize_t, 一般情况下仍然是 int32 或 int64) |
int8 | 字节(-128 to 127) |
int16 | 整数(-32768 to 32767) |
int32 | 整数(-2147483648 to 2147483647) |
int64 | 整数(-9223372036854775808 to 9223372036854775807) |
uint8 | 无符号整数(0 to 255) |
uint16 | 无符号整数(0 to 65535) |
uint32 | 无符号整数(0 to 4294967295) |
uint64 | 无符号整数(0 to 18446744073709551615) |
float_ | float64 类型的简写 |
float16 | 半精度浮点数, 包括: 1 个符号位, 5 个指数位, 10 个尾数位 |
float32 | 单精度浮点数, 包括: 1 个符号位, 8 个指数位, 23 个尾数位 |
float64 | 双精度浮点数, 包括: 1 个符号位, 11 个指数位, 52 个尾数位 |
complex_ | complex128 类型的简写, 即 128 位复数 |
complex64 | 复数, 表示双 32 位浮点数(实数部分和虚数部分) |
complex128 | 复数, 表示双 64 位浮点数(实数部分和虚数部分) |
numpy 的数值类型实际上是 dtype 对象的实例, 并对应唯一的字符, 包括 np.bool_, np.int32, np.float32等.
内建数据类型对象 (ndarray.dtype
)
数据类型对象(numpy.dtype
类的实例)用来描述与数组对应的内存区域是如何使用, 它描述了数据的以下几个方面:
- 数据的类型(整数浮点数或者 Python 对象);
- 数据的大小(例如整数使用多少个字节存储);
- 数据的字节顺序(小端法或大端法);
- 在结构化类型的情况下, 字段的名称,每个字段的数据类型和每个字段所取的内存块的部分;
- 如果数据类型是子数组, 那么它的形状和数据类型是什么.
字节顺序是通过对数据类型预先设定 <
或>
来决定的. <
意味着小端法(最小值存储在最小的地址, 即低位组放在最前面). `> 意味着大端法(最重要的字节存储在最小的地址, 即高位组放在最前面).
dtype
对象是使用以下语法构造的:
numpy.dtype(object, align, copy)
- object – 要转换为的数据类型对象;
- align – 如果为 true, 填充字段使其类似 C 的结构体;
- copy – 复制 dtype 对象, 如果为 false, 则是对内置数据类型对象的引用.
import numpy as np
# 使用标量类型
dt = np.dtype(np.int32)
print(dt) #输出int32
import numpy as np
# int8, int16, int32, int64 四种数据类型可以使用字符串 'i1', 'i2','i4','i8' 代替
dt = np.dtype('i4')
print(dt) #输出int32
import numpy as np
# 字节顺序标注
dt = np.dtype('<i4') #小端法
print(dt) #输出int32
下面展示结构化数据类型的使用, 类型字段和对应的实际类型将被创建
# 首先创建结构化数据类型
import numpy as np
dt = np.dtype([('age',np.int8)])
#这里传入的参数是一个列表, 列表中的元素是一个元组 ('age', np.int8)
#元组的第一个元素 'age' 是字段名, 表示这个数组中的每一个元素都有一个名为 age 的字段
#第二个元素 np.int8 是该字段的数据类型, np.int8 表示 8 位有符号整数
print(dt) #输出[('age', 'i1')]
# 将数据类型应用于 ndarray 对象
import numpy as np
dt = np.dtype([('age',np.int8)])
a = np.array([(10,),(20,),(30,)], dtype = dt)
#传入的第一个参数 [(10,), (20,), (30,)] 是一个包含多个元组的列表, 每个元组对应数组中的一个元素
print(a) #输出[(10,) (20,) (30,)]
# 类型字段名可以用于存取实际的 age 列
print(a['age']) #输出[10 20 30].
# 这表明我们成功地通过字段名 'age' 访问到了数组中每个元素的 age 字段的值
下面定义一个结构化数据类型 student, 包含字符串字段 name, 整数字段 age, 及浮点字段 marks, 并将这个 dtype 应用到 ndarray 对象.
import numpy as np
student = np.dtype([('name','S20'), ('age', 'i1'), ('marks', 'f4')])
print(student) #输出结果[('name', 'S20'), ('age', 'i1'), ('marks', 'f4')]
a = np.array([('abc', 21, 50),('xyz', 18, 75)], dtype = student)
print(a) #输出结果[('abc', 21, 50.0), ('xyz', 18, 75.0)]
每个内建类型都有一个唯一定义它的字符代码, 如下:
字符 | 对应类型 |
---|---|
b | 布尔型 |
i | (有符号)整型 |
u | 无符号整型 integer |
f | 浮点型 |
c | 复数浮点型 |
m | timedelta(时间间隔) |
M | datetime(日期时间) |
O | (Python) 对象 |
S, a | (byte-)字符串 |
U | Unicode |
V | 原始数据 (void) |
NumPy 数组属性
在 NumPy中, 每一个线性的数组称为是一个轴(axis), 也就是维度(dimensions). 比如说, 二维数组相当于是两个一维数组, 其中第一个一维数组中每个元素又是一个一维数组. 所以一维数组就是 NumPy 中的轴(axis), 第一个轴相当于是底层数组, 第二个轴是底层数组里的数组. 而轴的数量——秩, 就是数组的维数.
很多时候可以声明axis
. axis=0
, 表示沿着第 0 轴进行操作, 即对每一列进行操作; axis=1
, 表示沿着第1轴进行操作, 即对每一行进行操作.
NumPy 的数组中比较重要 ndarray 对象属性有:
属性 | 说明 |
---|---|
ndarray.ndim | 数组的秩(rank), 即数组的维度数量或轴的数量. |
ndarray.shape | 数组的维度, 表示数组在每个轴上的大小. 对于二维数组(矩阵), 表示其行数和列数. |
ndarray.size | 数组中元素的总个数, 等于 ndarray.shape 中各个轴上大小的乘积. |
ndarray.dtype | 数组中元素的数据类型. |
ndarray.itemsize | 数组中每个元素的大小, 以字节为单位. |
ndarray.flags | 包含有关内存布局的信息, 如是否为 C 或 Fortran 连续存储, 是否为只读等. |
ndarray.real | 数组中每个元素的实部(如果元素类型为复数). |
ndarray.imag | 数组中每个元素的虚部(如果元素类型为复数). |
ndarray.data | 实际存储数组元素的缓冲区, 一般通过索引访问元素, 不直接使用该属性. |
ndarray.ndim
ndarray.ndim
用于获取数组的维度数量(即数组的轴数)
import numpy as np
a=np.array([[1,2,3],
[4,5,6],
[7,8,9]]) #创建一个二维数组
print(a.ndim) #输出2
b=np.array([1,2,3]) #创建一个一维数组
print(b.ndim) #输出1
c=np.array(8)
print(c.ndim) #输出0
ndarray.shape
ndarray.shape
表示数组的维度, 返回一个元组, 这个元组的长度就是维度的个数, 即 ndim 属性(秩).比如, 一个二维数组, 其维度表示行数和列数.
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print (a.shape) #输出(2, 3)
b = np.array([1, 2, 3])
print(b.shape) # 输出(3,). 这里的逗号是必要的, 因为在 Python 中, 单元素元组需要在元素后面加上逗号来与普通括号区分.
c = np.array([[[1, 2], [3, 4], [5, 6]],
[[7, 8], [9, 10], [11, 12]],
[[13, 14], [15, 16], [17, 18]]])
print(c.shape) # 输出(3, 3, 2)
ndarray.shape
也可以用于调整数组大小.
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
a.shape = (3,2) #调整为3行2列的数组
print (a)
'''
[[1 2]
[3 4]
[5 6]]
'''
同时NumPy 也提供了reshape
函数来调整数组大小
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
b = a.reshape(3,2) #调整为3行2列的数组
print (b)
'''
[[1 2]
[3 4]
[5 6]]
'''
ndarray.itemsize
ndarray.itemsize
以字节的形式返回数组中每一个元素的大小.
例如一个元素类型为 float64 的数组 itemsize 属性值为 8(float64 占用 64 个 bits, 每个字节长度为 8, 所以 64/8, 占用 8 个字节), 又如一个元素类型为 complex32 的数组 item 属性为 4 (32/8)
import numpy as np
# 数组的 dtype 为 int8(一个字节)
x = np.array([1,2,3,4,5], dtype = np.int8)
print (x.itemsize) #输出1, 8/8
# 数组的 dtype 现在为 float64(八个字节)
y = np.array([1,2,3,4,5], dtype = np.float64)
print (y.itemsize) #输出8, 64/8
ndarray.flags
ndarray.flags
返回 ndarray 对象的内存信息, 包含以下属性
属性 | 描述 |
---|---|
C_CONTIGUOUS (C) | 数据是在一个单一的C风格的连续段中 |
F_CONTIGUOUS (F) | 数据是在一个单一的Fortran风格的连续段中 |
OWNDATA (O) | 数组拥有它所使用的内存或从另一个对象中借用它 |
WRITEABLE (W) | 数据区域可以被写入, 将该值设置为 False, 则数据为只读 |
ALIGNED (A) | 数据和所有元素都适当地对齐到硬件上 |
UPDATEIFCOPY (U) | 这个数组是其它数组的一个副本, 当这个数组被释放时, 原数组的内容将被更新 |
C风格和Fortran风格在编程领域有各自特点:
C风格:
- 语法:简洁灵活, 有丰富数据类型与运算符, 指针操作是特色, 需手动管理内存 .
- 编程习惯: 采用自顶向下设计, 以函数驱动为主, 通过头文件组织代码, 注重模块划分与代码复用.
- 应用场景: 常用于系统编程, 游戏开发, 高性能计算等对底层控制和性能要求高的领域.
Fortran风格:
- 语法: 数值计算能力强大, 数组和矩阵操作便捷, 语法相对严谨, 变量需先声明后使用.
- 编程习惯: 以数据为中心, 大量使用子程序和模块, 注重代码可读性和可维护性, 常通过注释等提高可读性.
- 应用场景: 主要用于科学计算, 工程计算以及气象和气候研究等需要处理大量数值计算的领域.
import numpy as np
x = np.array([1,2,3,4,5])
print (x.flags)
输出
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
NumPy 创建数组
ndarray 数组除了可以使用底层 ndarray 构造器来创建外, 也可以通过以下几种方式来创建.
numpy.empty
numpy.empty
方法用来创建一个指定形状(shape), 数据类型(dtype)且未初始化的数组.
numpy.empty(shape, dtype = float, order = 'C')
参数 | 描述 |
---|---|
shape | 数组形状 |
dtype | 数据类型, 可选 |
order | 有C和F两个选项,分别代表, 行优先和列优先, 在计算机内存中的存储元素的顺序. |
import numpy as np
x = np.empty([3,2], dtype = int)
print (x) #输出的数组元素为随机值, 因为它们未初始化
numpy.zeros
numpy.zeros
创建指定大小的数组, 数组元素以 0 来填充
numpy.zeros(shape, dtype = float, order = 'C')
参数 | 描述 |
---|---|
shape | 数组形状 |
dtype | 数据类型, 可选 |
order | C 用于 C 的行数组, 或者 F 用于 FORTRAN 的列数组 |
import numpy as np
# 默认为浮点数
x = np.zeros(5)
print(x)
# 设置类型为整数
y = np.zeros((5,), dtype = int)
print(y)
# 自定义类型'i4'为int32
z = np.zeros((2,2), dtype = [('x', 'i4'), ('y', 'i4')]) #创建一个2行2列的二维数组
print(z)
输出
[0. 0. 0. 0. 0.]
[0 0 0 0 0]
[[(0, 0) (0, 0)]
[(0, 0) (0, 0)]]
numpy.ones
numpy.ones
创建指定形状的数组, 数组元素以 1 来填充:
numpy.ones(shape, dtype = None, order = 'C')
参数 | 描述 |
---|---|
shape | 数组形状 |
dtype | 数据类型, 可选 |
order | C 用于 C 的行数组, 或者 F用于 FORTRAN 的列数组 |
import numpy as np
# 默认为浮点数
x = np.ones(5)
print(x)
# 自定义类型
x = np.ones([2,2], dtype = int)
print(x)
输出
[1. 1. 1. 1. 1.]
[[1 1]
[1 1]]
numpy.zeros_like
numpy.zeros_like
用于创建一个与给定数组具有相同形状的数组, 数组元素以 0 来填充.
numpy.zeros 和 numpy.zeros_like 都是用于创建一个指定形状的数组, 其中所有元素都是 0. 它们之间的区别在于, `numpy.zeros
可以直接指定要创建的数组的形状, 而 numpy.zeros_like
则是创建一个与给定数组具有相同形状的数组.
numpy.zeros_like(a, dtype=None, order='K', subok=True, shape=None)
参数 | 描述 |
---|---|
a | 给定要创建相同形状的数组 |
dtype | 创建的数组的数据类型 |
order | 数组在内存中的存储顺序, 可选值为 C(按行优先)或 F(按列优先), 默认为 K(保留输入数组的存储顺序) |
subok | 是否允许返回子类, 如果为 True, 则返回一个子类对象, 否则返回一个与 a 数组具有相同数据类型和存储顺序的数组 |
shape | 创建的数组的形状, 如果不指定, 则默认为 a 数组的形状 |
import numpy as np
# 创建一个 3x3 的二维数组
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 创建一个与 arr 形状相同的, 所有元素都为 0 的数组
zeros_arr = np.zeros_like(arr)
print(zeros_arr)
输出
[[0 0 0]
[0 0 0]
[0 0 0]]
numpy.ones_like
numpy.ones_like
用于创建一个与给定数组具有相同形状的数组, 数组元素以 1 来填充.
numpy.ones
和numpy.ones_like
都是用于创建一个指定形状的数组, 其中所有元素都是 1. 它们之间的区别在于, numpy.ones
可以直接指定要创建的数组的形状, 而 numpy.ones_like
则是创建一个与给定数组具有相同形状的数组.
numpy.ones_like(a, dtype=None, order='K', subok=True, shape=None)
参数 | 描述 |
---|---|
a | 给定要创建相同形状的数组 |
dtype | 创建的数组的数据类型 |
order | 数组在内存中的存储顺序, 可选值为C(按行优先)或 F(按列优先), 默认为K(保留输入数组的存储顺序) |
subok | 是否允许返回子类, 如果为 True, 则返回一个子类对象, 否则返回一个与 a 数组具有相同数据类型和存储顺序的数组 |
shape | 创建的数组的形状, 如果不指定, 则默认为 a 数组的形状 |
创建一个与 arr 形状相同的, 所有元素都为 1 的数组:
import numpy as np
# 创建一个 3x3 的二维数组
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 创建一个与 arr 形状相同的, 所有元素都为 1 的数组
ones_arr = np.ones_like(arr)
print(ones_arr)
输出:
[[1 1 1]
[1 1 1]
[1 1 1]]
NumPy 从已有的数组创建数组
numpy.asarray
numpy.asarray
类似 numpy.array
, 但 numpy.asarray
参数只有三个, 比 numpy.array
少两个
numpy.asarray(a, dtype = None, order = None)
参数 | 描述 |
---|---|
a | 任意形式的输入参数, 可以是, 列表, 列表的元组, 元组, 元组的元组, 元组的列表, 多维数组 |
dtype | 数据类型, 可选 |
order | 可选, 有C和F两个选项,分别代表, 行优先和列优先, 在计算机内存中的存储元素的顺序 |
将列表转换为 ndarray:
import numpy as np
x = [1,2,3]
a = np.asarray(x)
print (a) #输出[1 2 3]
将元组转换为 ndarray:
import numpy as np
x = (1,2,3)
a = np.asarray(x)
print (a) #[1 2 3]
将元组列表转换为 ndarray:
import numpy as np
x = [(1,2,3),(4,5)]
a = np.asarray(x)
print (a) #输出[(1, 2, 3) (4, 5)]
设置 dtype 参数:
import numpy as np
x = [1,2,3]
a = np.asarray(x, dtype = float)
print (a) #输出[ 1. 2. 3.]
numpy.frombuffer
numpy.frombuffer
用于实现动态数组.
numpy.frombuffer
接受 buffer 输入参数, 以流的形式读入转化成 ndarray 对象.
numpy.frombuffer(buffer, dtype = float, count = -1, offset = 0)
注意:buffer 是字符串的时候, Python3 默认 str 是 Unicode 类型, 所以要转成 bytestring 在原 str 前加上 b.
参数 | 描述 |
---|---|
buffer | 可以是任意对象, 会以流的形式读入 |
dtype | 返回数组的数据类型, 可选 |
count | 读取的数据数量, 默认为-1, 读取所有数据 |
offset | 读取的起始位置, 默认为0 |
import numpy as np
s = b'Hello World'
# b前缀表示创建的是一个字节字符串(bytes对象)
a = np.frombuffer(s, dtype = 'S1')
# S表示这是一个字符串类型,1 表示每个字符串元素的长度为 1 个字节. 因此这个数组中的每个元素都是一个长度为 1 字节的字符串.
print (a)
#输出[b'H' b'e' b'l' b'l' b'o' b' ' b'W' b'o' b'r' b'l' b'd']
numpy.fromiter
numpy.fromiter
方法从可迭代对象中建立 ndarray 对象, 返回一维数组.
numpy.fromiter(iterable, dtype, count=-1)
参数 | 描述 |
---|---|
iterable | 可迭代对象 |
dtype | 返回数组的数据类型 |
count | 读取的数据数量, 默认为-1, 读取所有数据 |
import numpy as np
# 使用 range 函数创建列表对象
list=range(5)
it=iter(list)
# 使用迭代器创建 ndarray
x=np.fromiter(it, dtype=float)
print(x)
#输出[0. 1. 2. 3. 4.]
NumPy 从数值范围创建数组
numpy.arange
numpy 包中的使用 arange 函数创建数值范围并返回 ndarray 对象, 函数格式如下:
numpy.arange(start, stop, step, dtype)
根据 start 与 stop 指定的范围以及 step 设定的步长, 生成一个 ndarray.
参数 | 描述 |
---|---|
start | 起始值, 默认为0 |
stop | 终止值(不包含) |
step | 步长, 默认为1 |
dtype | 返回ndarray 的数据类型, 如果没有提供, 则会使用输入数据的类型 |
生成 0 到 4 长度为 5 的数组:
import numpy as np
x = np.arange(5)
print (x)
#输出[0 1 2 3 4]
设置返回类型位 float:
import numpy as np # 设置了 dtype
x = np.arange(5, dtype = float)
print (x)
#输出结果[0. 1. 2. 3. 4.]
设置了起始值, 终止值及步长:
import numpy as np
x = np.arange(10,20,2)
print (x)
#输出[10 12 14 16 18]
numpy.linspace
numpy.linspace
函数用于创建一个一维数组, 数组是一个等差数列构成的. 格式如下:
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
参数 | 描述 |
---|---|
start | 序列的起始值 |
stop | 序列的终止值, 如果endpoint为true, 该值包含于数列中 |
num | 要生成的等步长的样本数量, 默认为50 |
endpoint | 该值为 true 时, 数列中包含stop值, 反之不包含, 默认是true. |
retstep | 如果为 true 时, 生成的数组中会显示间距, 反之不显示 |
dtype | ndarray的数据类型 |
以下实例用到三个参数, 设置起始点为 1 , 终止点为 10, 数列个数为 10.
import numpy as np
a = np.linspace(1,10,10)
print(a)
#输出[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
设置元素全部是1的等差数列:
import numpy as np
a = np.linspace(1,1,10)
print(a)
#输出[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
将 endpoint 设为 false, 不包含终止值:
import numpy as np
a = np.linspace(10, 20, 5, endpoint = False)
print(a)
#输出[10. 12. 14. 16. 18.]
#如果将 endpoint 设为 true, 则会包含 20.
设置间距.
import numpy as np
a =np.linspace(1,10,10,retstep= True)
print(a) # 拓展例子
b =np.linspace(1,10,10).reshape([10,1])
print(b)
'''
(array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]), 1.0)
[[ 1.]
[ 2.]
[ 3.]
[ 4.]
[ 5.]
[ 6.]
[ 7.]
[ 8.]
[ 9.]
[10.]]
'''
numpy.logspace
numpy.logspace
函数用于创建一个于等比数列(对数刻度上均匀分布的一维数组). 格式如下:
np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
base 参数意思是取对数的时候 log 的下标.
即生成$[base^{start},base^{end}]$的$num$个数字(endpoint的值决定有无$base^{end}$)
参数 | 描述 |
---|---|
start | 序列的起始值为: base ** start |
stop | 序列的终止值为: base ** stop. 如果endpoint为true, 该值包含于数列中 |
num | 要生成的等步长的样本数量, 默认为50 |
endpoint | 该值为 true 时, 数列中中包含stop 值, 反之不包含, 默认是True |
base | 对数 log 的底数 |
dtype | ndarray 的数据类型 |
import numpy as np # 默认底数是 10
a = np.logspace(1.0, 2.0, num = 10) #从10^1.0生成到10^2.0共10个数
print (a)
'''
[ 10. 12.91549665 16.68100537 21.5443469 27.82559402
35.93813664 46.41588834 59.94842503 77.42636827 100. ]
'''
将对数的底数设置为 2 :
import numpy as np
a = np.logspace(0,9,num = 10,base = 2) #从2^0生成到2^9共10个数
print (a)
#输出[ 1. 2. 4. 8. 16. 32. 64. 128. 256. 512.]
NumPy 切片和索引
ndarray对象的内容可以通过索引或切片来访问和修改, 与 Python 中 list 的切片操作一样.
ndarray 数组可以基于 0 – n 的下标进行索引, 切片对象可以通过内置的slice
函数, 并设置 start, stop 及 step 参数进行, 从原数组中切割出一个新数组.
import numpy as np
a = np.arange(10)
s = slice(2,7,2) # 从索引 2 开始到索引 7 停止, 间隔为2
print (a[s])
#输出[2 4 6]
以上实例中, 我们首先通过 arange() 函数创建 ndarray 对象. 然后分别设置起始, 终止和步长的参数为 2, 7 和 2.
我们也可以通过冒号分隔切片参数 start:stop:step来进行切片操作:
import numpy as np
a = np.arange(10)
b = a[2:7:2] # 从索引 2 开始到索引 7 停止, 间隔为 2
print(b)
输出[2 4 6]
冒号:
的解释: 如果只放置一个参数, 如 [2]
, 将返回与该索引相对应的单个元素. 如果为 [2:]
, 表示从该索引开始以后的所有项都将被提取. 如果使用了两个参数, 如 [2:7]
, 那么则提取两个索引(不包括停止索引)之间的项.
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(a[5]) #输出5
import numpy as np
a = np.arange(10)
print(a[2:])
#输出[2 3 4 5 6 7 8 9]
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(a[2:5])
#输出[2 3 4]
多维数组同样适用上述索引提取方法:
import numpy as np
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a) # 从某个索引处开始切割
print('从数组索引 a[1:] 处开始切割')
print(a[1:])
'''
[[1 2 3]
[3 4 5]
[4 5 6]]
从数组索引 a[1:] 处开始切割
[[3 4 5]
[4 5 6]]
'''
切片还可以包括省略号 …
, 来使选择元组的长度与数组的维度相同. 如果在行位置使用省略号, 它将返回包含行中元素的 ndarray.
import numpy as np
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a[...,1]) # 第2列元素
print(a[1,...]) # 第2行元素
print(a[...,1:]) # 第2列及剩下的所有元素
'''
[2 4 5]
[3 4 5]
[[2 3]
[4 5]
[5 6]]
'''
NumPy 高级索引
NumPy 比一般的 Python 序列提供更多的索引方式.
除了之前看到的用整数和切片的索引外, 数组可以由整数数组索引, 布尔索引及花式索引.
NumPy 中的高级索引指的是使用整数数组, 布尔数组或者其他序列来访问数组的元素. 相比于基本索引, 高级索引可以访问到数组中的任意元素, 并且可以用来对数组进行复杂的操作和修改.
整数数组索引
整数数组索引是指使用一个数组来访问另一个数组的元素. 这个数组中的每个元素都是目标数组中某个维度上的索引值.
以下实例获取数组中 (0,0), (1,1) 和 (2,0) 位置处的元素.
import numpy as np
x = np.array([[1, 2], [3, 4], [5, 6]])
y = x[[0,1,2], [0,1,0]]
print (y)
#输出[1 4 5]
以下实例获取了 4X3 数组中的四个角的元素. 行索引是 [0,0] 和 [3,3], 而列索引是 [0,2] 和 [0,2].
import numpy as np
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
print ('我们的数组是: ' )
print (x)
print ('\n')
rows = np.array([[0,0],[3,3]])
cols = np.array([[0,2],[0,2]])
y = x[rows,cols]
print ('这个数组的四个角元素是: ')
print (y)
输出
我们的数组是:
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
这个数组的四个角元素是:
[[ 0 2]
[ 9 11]]
返回的结果是包含每个角元素的 ndarray 对象.
可以借助切片 :
或 …
与索引数组组合:
import numpy as np
a = np.array([[1,2,3], [4,5,6],[7,8,9]])
b = a[1:3, 1:3]
c = a[1:3,[1,2]]
d = a[...,1:]
print(b)
print(c)
print(d)
输出:
[[5 6]
[8 9]]
[[5 6]
[8 9]]
[[2 3]
[5 6]
[8 9]]
布尔索引
我们可以通过一个布尔数组来索引目标数组.
布尔索引通过布尔运算(如比较运算符)来获取符合指定条件的元素的数组.
以下实例获取大于 5 的元素:
import numpy as np
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
print ('数组是: ')
print (x)
print ('\n') # 现在我们会打印出大于 5 的元素
print ('大于 5 的元素是: ')
print (x[x > 5])
输出:
数组是:
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
大于 5 的元素是:
[ 6 7 8 9 10 11]
以下实例使用了 ~
(取补运算符)来过滤 NaN(Not a Number).
import numpy as np
a = np.array([np.nan, 1,2,np.nan,3,4,5])
print (a[~np.isnan(a)])
#np.isnan()是numpy库中的一个函数, 用于判断数组中的每个元素是否为NaN.
#它会返回一个与数组a形状相同的布尔数组, 其中True表示对应位置的元素是NaN.
#输出[ 1. 2. 3. 4. 5.]
以下实例演示如何从数组中过滤掉非复数元素.
import numpy as np
a = np.array([1, 2+6j, 5, 3.5+5j])
print (a[np.iscomplex(a)])
#输出[2.0+6.j 3.5+5.j]
花式索引
花式索引指的是利用整数数组进行索引.
花式索引根据索引数组的值作为目标数组的某个轴的下标来取值.
对于使用一维整型数组作为索引, 如果目标是一维数组, 那么索引的结果就是对应位置的元素, 如果目标是二维数组, 那么就是对应下标的行.
花式索引跟切片不一样, 它总是将数据复制到新数组中.
一维数组
一维数组只有一个轴 axis = 0, 所以一维数组就在 axis = 0 这个轴上取值:
import numpy as np
x = np.arange(9)
print(x)
# 一维数组读取指定下标对应的元素
print("-------读取下标对应的元素-------")
x2 = x[[0, 6]] # 使用花式索引
print(x2)
print(x2[0])
print(x2[1])
输出:
[0 1 2 3 4 5 6 7 8]
-------读取下标对应的元素-------
[0 6]
0
6
二维数组
- 传入顺序索引数组
import numpy as np
x=np.arange(32).reshape((8,4))
print(x) # 二维数组读取指定下标对应的行
print("-------读取下标对应的行-------")
print (x[[4,2,1,7]])
print (x[[4,2,1,7]])
输出下表为 4, 2, 1, 7
对应的行, 输出结果为:
[[ 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 30 31]]
-------读取下标对应的行-------
[[16 17 18 19]
[ 8 9 10 11]
[ 4 5 6 7]
[28 29 30 31]]
- 传入倒序索引数组
import numpy as np
x=np.arange(32).reshape((8,4))
print (x[[-4,-2,-1,-7]])
输出结果为:
[[16 17 18 19]
[24 25 26 27]
[28 29 30 31]
[ 4 5 6 7]]
- 传入多个索引数组(要使用
np.ix_
)
np.ix_
函数就是输入两个数组, 产生笛卡尔积的映射关系.
笛卡尔乘积是指在数学中, 两个集合$X$和$Y$的笛卡尔积(Cartesian product), 又称直积, 表示为$X\times Y={(x,y)|x∈A\wedge y∈B}$, 第一个对象是X的成员而第二个对象是 Y 的所有可能有序对的其中一个成员.
例如 $A={a,b}, B={0,1,2}$, 则:
$$
A\times B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}\
B\times A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}
$$
import numpy as np
x=np.arange(32).reshape((8,4))
print (x[np.ix_([1,5,7,2],[0,3,1,2])])
输出:
[[ 4 7 5 6]
[20 23 21 22]
[28 31 29 30]
[ 8 11 9 10]]
NumPy 广播(Broadcast)
广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行.
如果两个数组 a 和 b 形状相同, 即满足 a.shape == b.shape
, 那么 a*b
的结果就是 a 与 b 数组对应位相乘. 这要求维数相同, 且各维度的长度相同.
import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])
c = a * b
print (c)
#输出[ 10 40 90 160]
当运算中的 2 个数组的形状不同时, numpy 将自动触发广播机制:
import numpy as np
a = np.array([[0 ,0 ,0 ],
[10,10,10],
[20,20,20],
[30,30,30]])
b = np.array([0,1,2])
print(a + b)
'''
[[ 0 1 2]
[10 11 12]
[20 21 22]
[30 31 32]]
'''
4×3 的二维数组与长为 3 的一维数组相加, 等效于把数组 b 在二维上重复 4 次再运算:
import numpy as np
a = np.array([[0,0,0],
[10,10,10],
[20,20,20],
[30,30,30]])
b = np.array([0,1,2])
bb = np.tile(b, (4, 1)) # 重复 b 的各个维度
print(a + bb)
'''
[[ 0 1 2]
[10 11 12]
[20 21 22]
[30 31 32]]
'''
广播的规则:
- 让所有输入数组都向其中形状最长的数组看齐, 形状中不足的部分都通过在前面加 1 补齐.
- 输出数组的形状是输入数组形状的各个维度上的最大值.
- 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时, 这个数组能够用来计算, 否则出错.
- 当输入数组的某个维度的长度为 1 时, 沿着此维度运算时都用此维度上的第一组值.
**简单理解: **对两个数组, 分别比较他们的每一个维度(若其中一个数组没有当前维度则忽略), 满足:
- 数组拥有相同形状.
- 当前维度的值相等.
- 当前维度的值有一个是 1.
若条件不满足, 抛出 ValueError: frames are not aligned
异常.
NumPy 迭代数组
NumPy 迭代器对象 numpy.nditer 提供了一种灵活访问一个或者多个数组元素的方式.
迭代器最基本的任务的可以完成对数组元素的访问.
接下来我们使用 arange() 函数创建一个 2X3 数组, 并使用 nditer 对它进行迭代.
import numpy as np
a = np.arange(6).reshape(2,3)
print ('原始数组是: ')
print (a)
print ('\n')
print ('迭代输出元素: ')
for x in np.nditer(a):
print (x, end=", " )
输出::
原始数组是:
[[0 1 2]
[3 4 5]]
迭代输出元素:
0, 1, 2, 3, 4, 5,
以上实例不是使用标准 C 或者 Fortran 顺序, 选择的顺序是和数组内存布局一致的, 这样做是为了提升访问的效率, 默认是行序优先.
这反映了默认情况下只需访问每个元素, 而无需考虑其特定顺序. 我们可以通过迭代上述数组的转置来看到这一点, 并与以 C 顺序访问数组转置的 copy 方式做对比, 如下:
import numpy as np
a = np.arange(6).reshape(2,3)
for x in np.nditer(a.T):
print (x, end=", " )
print ('\n')
for x in np.nditer(a.T.copy(order='C')):
print (x, end=", " )
print ('\n')
输出:
0, 1, 2, 3, 4, 5,
0, 3, 1, 4, 2, 5,
a 和 a.T 的遍历顺序是一样的, 也就是他们在内存中的存储顺序也是一样的, 但是 a.T.copy(order = 'C')
的遍历结果是不同的, 那是因为它和前两种的存储方式是不一样的, 默认是按行访问.
控制遍历顺序
for x in np.nditer(a, order='F'):
Fortran order, 即是列序优先;for x in np.nditer(a.T, order='C'):
C order, 即是行序优先;
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是: ')
print (a)
print ('\n')
print ('原始数组的转置是: ')
b = a.T
print (b)
print ('\n')
print ('以 C 风格顺序排序: ')
c = b.copy(order='C')
print (c)
for x in np.nditer(c):
print (x, end=", " )
print ('\n')
print ('以 F 风格顺序排序: ')
c = b.copy(order='F')
print (c)
for x in np.nditer(c):
print (x, end=", " )
输出:
原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
原始数组的转置是:
[[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]]
以 C 风格顺序排序:
[[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]]
0, 20, 40, 5, 25, 45, 10, 30, 50, 15, 35, 55,
以 F 风格顺序排序:
[[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]]
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55,
可以通过显式设置, 来强制 nditer 对象使用某种顺序:
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是: ')
print (a)
print ('\n')
print ('以 C 风格顺序排序: ')
for x in np.nditer(a, order = 'C'):
print (x, end=", " )
print ('\n')
print ('以 F 风格顺序排序: ')
for x in np.nditer(a, order = 'F'):
print (x, end=", " )
输出:
原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
以 C 风格顺序排序:
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55,
以 F 风格顺序排序:
0, 20, 40, 5, 25, 45, 10, 30, 50, 15, 35, 55,
修改数组中元素的值
nditer 对象有另一个可选参数 op_flags. 默认情况下, nditer 将视待迭代遍历的数组为只读对象(read-only), 为了在遍历数组的同时, 实现对数组元素值的修改, 必须指定 readwrite 或者 writeonly 的模式.
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是: ')
print (a)
print ('\n')
for x in np.nditer(a, op_flags=['readwrite']):
x[...]=2*x
print ('修改后的数组是: ')
print (a)
输出结果为:
原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
修改后的数组是:
[[ 0 10 20 30]
[ 40 50 60 70]
[ 80 90 100 110]]
使用外部循环
nditer 类的构造器拥有 flags 参数, 它可以接受下列值:
参数 | 描述 |
---|---|
c_index | 可以跟踪 C 顺序的索引 |
f_index | 可以跟踪 Fortran 顺序的索引 |
multi_index | 每次迭代可以跟踪一种索引类型 |
external_loop | 给出的值是具有多个值的一维数组, 而不是零维数组 |
迭代器遍历对应于每列, 并组合为一维数组:
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是: ')
print (a)
print ('\n')
print ('修改后的数组是: ')
for x in np.nditer(a, flags = ['external_loop'], order = 'F'):
print (x, end=", " )
输出:
原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
修改后的数组是:
[ 0 20 40], [ 5 25 45], [10 30 50], [15 35 55],
广播迭代
如果两个数组是可广播的, nditer 组合对象能够同时迭代它们. 假设数组 a 的维度为$3\times 4$, 数组 b 的维度为$1\times4$, 则使用以下迭代器(数组 b 被广播到 a 的大小).
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('第一个数组为: ')
print (a)
print ('\n')
print ('第二个数组为: ')
b = np.array([1, 2, 3, 4], dtype = int)
print (b)
print ('\n')
print ('修改后的数组为: ')
for x,y in np.nditer([a,b]):
print ("%d:%d" % (x,y), end=", " )
输出:
第一个数组为:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
第二个数组为:
[1 2 3 4]
修改后的数组为:
0:1, 5:2, 10:3, 15:4, 20:1, 25:2, 30:3, 35:4, 40:1, 45:2, 50:3, 55:4,