NumPy是Python数值计算中最重要的基础包之一,其被设计为可以高效的处理大型数组的数据
基于Numpy的算法要比纯Python算法快10-100倍,且使用的内存更少
import numpy as np
#后文默认已导入NumPy
my_arr = np.arange(1_000_000)
%timeit my_arr2 = my_arr * 2
#1.1 ms ± 53.8 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
my_list = list(range(1_000_000))
%timeit my_list2 = my_list * 2
#12.7 ms ± 199 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.1多维数组对象
数组中的所有元素必须是同类型
4.1.1创建ndarray
array函数接收任意序列型对象,然后生成一个新的包含传入数据的NumPy数组
除非特意指定,否则np.array会尝试为新建的数组推断出合适的数据类型
#array函数接收任意序列型对象,然后生成一个新的包含传入数据的NumPy数组
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1)
arr1
#array([6. , 7.5, 8. , 0. , 1. ])
#嵌套序列
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
arr2
#array([[1, 2, 3, 4],
# [5, 6, 7, 8]])
arr1.dtype
#dtype('float64')
其他可用于创建数组的函数.zeros() .ones() .empty(),其他用于创建数组的函数自行搜索
#.zeros .ones分别创建全0和全1的数组,传递表示形状的元组可以创建多维数组
np.zeros(10)
#array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
np.ones((2,3))
#array([[1., 1., 1.],
# [1., 1., 1.]])
#.empty可以创建一个没有任何具体值的数组,但其返回的是未初始化的内存值,包含“垃圾”
#当给新数组填充数据时再用
np.empty((2,3,2))
#array([[[1.25658690e-311, 2.86558075e-322],
# [0.00000000e+000, 0.00000000e+000],
# [1.25622151e-311, 2.42336543e-057]],
#
# [[5.49704909e-090, 3.26696491e-032],
# [8.46379413e+164, 5.68709725e-062],
# [6.48224659e+170, 5.82471487e+257]]])
如果没有特别指明,因NumPy专注于数值计算,所以类型一般是float64
4.1.2ndarray的数据类型
dtype是一个特殊对象,它包含一个信息:解释NumPy数组在内存存储数据时数据类型的信息
arr1 = np.array([1, 2, 3], dtype = np.float64)
arr1.dtype
#dtype('float64')
.astype()方法可以将数组元素的数据类型转换为另一种数据类型(也存在不能转换的情况)
#将数组从一种数据类型转换或投射成另一种数据类型
arr = np.array([1, 2, 3, 4, 5])
arr.dtype
#dtype('int32')
float_arr = arr.astype(np.float64)
float_arr.dtype
#dtype('float64')
如果将浮点数转换成整数,则会发生截断,舍去小数点
numpy.string_类型(一个字符一个字节),Numpy的字符串数据是大小固定的,发生截断时不会警告
即使新的数据类型与旧的数据类型相同,astype也总创建一个新的数组
4.1.3数组的运算
对数组进行批量运算的操作可以成为向量化(个人理解:和向量的算术运算相似,事实也如此)
#大小相等的数组之间的任何算术运算都会转为元素级的运算
arr = np.array([[1., 2., 3.],[4., 5., 6.]])
arr * arr
#array([[ 1., 4., 9.],
# [16., 25., 36.]])
arr - arr
#array([[1. , 0.5 , 0.33333333],
# [0.25 , 0.2 , 0.16666667]])
#数组与标量的运算将标量值转播到数组中的每个元素
1 / arr
#array([[1. , 0.5 , 0.33333333],
# [0.25 , 0.2 , 0.16666667]])
arr ** 2
#array([[ 1., 4., 9.],
# [16., 25., 36.]])
#大小相同的数组进行布尔运算则会生成布尔值数组
arr2 = np.array([[0., 4., 1.],[7., 2., 12.]])
arr2 > arr
#arr2 > arr
#array([[False, True, False],
# [ True, False, True]])
4.1.4基本的索引和切片
数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会反映到原数组上
arr = np.arange(10)
arr_slice = arr[5:8]
arr_slice[1] = 1234
arr
#array([ 0, 1, 2, 3, 4, 5, 1234, 7, 8, 9])
因为NumPy的设计是为了处理大型数组,所以若也使用复制的方式,内存和性能方面会有很大问题
在一个二维数组中,索引位置上的元素不再是标量而是一维数组,下图为轴的示意:
arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
arr3d[0]
#array([[1, 2, 3],
# [4, 5, 6]])
#标量和数组都可以赋值给arr3d[0]
old_values = arr3d[0].copy()
arr3d[0] = 42
arr3d
#array([[[42, 42, 42],
# [42, 42, 42]],
#
# [[ 7, 8, 9],
# [10, 11, 12]]])
arr3d[0] = old_values
arr3d
#array([[[ 1, 2, 3],
# [ 4, 5, 6]],
#
# [[ 7, 8, 9],
# [10, 11, 12]]])
我意识到我写的太罗嗦了,所以从这里决定简写,挑比较易混淆等知识点写,给自己个提醒
进行切片时,如一个3 * 3的二维数组 arr[:,:1] 是针对二维层面去切的
切出来的将会是3 * 1形状的数组因为冒号意味着选取整个轴
arr2d = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
arr2d[:, :1]
#array([[1],
# [4],
# [7]])
4.1.5布尔型索引
布尔型数组的长度必须与被索引的轴的长度一致,布尔型数组可以选取目标数组中对应位置为True的元素或数组
names = np.array(["Bob", "Joe", "Will", "Bob", "Will", "Joe", "Joe"])
#在这里data是7*2的数组,轴0长度为7,布尔型数组长度也为7
data = np.array([[4, 7], [0, 2], [-5, 6], [0, 0], [1, 2],[-12, -4], [3, 4]])
data[names == "Bob", 1:]
#array([[7],
# [0]])
通过布尔型索引选取数组中的数据,并给结果复制新的变量,会创建数据的副本,即使返回一模一样的数组也如此
4.1.6花式索引--指用整数数组进行索引
一次传入多个索引数组,返回的是一维数组,按照对应位置元素组成索引去返回
#reshape不复制数组就可以改变数组形状
arr = np.arange(32).reshape((8, 4))
arr[[1, 5, 7, 2], [0, 3, 1, 2]]
#array([ 4, 23, 29, 10])
花式索引会将数据复制到新数组中
4.1.7数组转置和轴对换--不复制原地进行转置
4.2生成伪随机数
numpy.random模块用于从多种概率分布中有效地生成整个样本值数组
python内置的random模块只能一次生成一个样本值。若需要生成大量样本值,numpy.random快了很多
from random import normalvariate
N = 1_000_000
%timeit samples = [normalvariate(0, 1) for _ in range(N)]
#560 ms ± 5.25 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.random.standard_normal(N)
#18.5 ms ± 203 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.3通用函数:快速的元素级数组函数
numpy.modf可以返回多个数组,这个函数会返回浮点数数组的整数和小数部分
rng = np.random.default_rng(seed=12345)
arr = rng.standard_normal(7) * 5
remainder, whole_part = np.modf(arr)
remainder
#array([-0.11912518, 0.31864229, -0.35330869, -0.29586617, -0.37671654,
# -0.70442326, -0.83896351])
whole_part
#array([-7., 6., -4., -1., -0., -3., -6.])
4.4利用数组进行面向数组编程
NumPy数组将许多数据处理任务表述为简洁的数组表达式,而无须编写循环
4.4.4排序
NumPy也通过 sort 方法就地排序
arr = rng.standard_normal(6)
arr
#array([ 0.90219827, -0.46695317, -0.06068952, 0.78884434, -1.25666813,
# 0.57585751])
arr.sort()
arr
#array([-1.25666813, -0.46695317, -0.06068952, 0.57585751, 0.78884434,
# 0.90219827])
顶级方法np.sort()返回的是已排序数组的副本,而不是就地修改
书中剩下部分较容易理解,第4章就到这里了