Numpy入门
以下只是简单地记录下上课时学习Numpy的一些例子
# 导入numpy包,并取别名为np
import numpy as np
# 查看numpy版本
numpy.__version__
# 整型数组
np.array([1, 4, 2, 5, 3])
# 如果类型不匹配,Numpy会向上转换(如果可行的话,指的是向更大类型的数据转换)
# 此处是整型被转换为浮点型
np.array([3.14, 4, 2, 3])
# dtype关键字可以设置数组的数据类型
np.array([1, 2, 3, 4], dtype="float32")
# 嵌套列表构成多维数组
np.array([range(i, i + 3) for i in [2, 4, 6]])
# 创建一个长度为10的全0数组
np.zeros(10, dtype=int)
# 创建一个3*5的值全为1的浮点型数组
np.ones((3, 5), dtype=float)
# 创建一个3*5,值全相同的数组
np.full((3, 5), 3.14)
# 从0开始,到20结束(不包括20),步长为2
np.arange(0, 20, 2)
# 创建一个5元素数组,这五个值均匀分配到0-1(包括1)
np.linspace(0, 1, 5)
# 创建一个3*3的、在0-1均匀分布的随机数组组成的数组
np.random.random((3, 3))
# 创建一个3*3,均值为0,方差为1的正态分布的随机数组
np.random.normal(0, 1, (3, 3))
# 创建一个3*3的,[0,10)区间的随机整型数组
np.random.randint(0, 10, (3, 3))
# 创建一个3*3的单位矩阵
np.eye(3)
# 创建一个由3个数组成的未初始化的数组,数组里的值是内存空间中的任意值
np.empty(3)
基本的数组操作
import numpy as np
np.random.seed(42) # 设置随机数种子,相同的随机数种子在相同版本的python、numpy下生成的随机数是相同的, np.random.RandomState(42)也和种子的效果一样
x1 = np.random.randint(10, size=6) # 一维数组,默认从0开始(随机数从0-10中抽取,数组大小为6)
x2 = np.random.randint(10, size=(3, 4)) # 3*4的二维数组
x3 = np.random.randint(10, size(3, 4, 5)) # 3*4*5的三位数组
print("x3 ndim: ", x3.ndim) # 显示数组的维度
print("x3 shape: ", x3.shape) # 显示数组每个维度的大小,也就是数组的形状,此处是(3, 4, 5)
print("x3 size: ", x3.size) # 显示数组的总大小,此处是3*4*5=60
print("dtype: ", x3.dtype) # 显示数组的数据类型
数组索引,获取单个元素(与列表的操作类似)
x1 = np.array([5, 0, 3, 3, 7, 9])
x1[0] # 索引为0的元素,此处为5
x1[-1] # 最后一个元素,此处为9
x1[-2] # 倒数第二个元素,此处为7
x2 = np.array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2[0,0] # 获取坐标为0,0的元素,此处为3
x2[2,-1] # 第二行最后一列元素,此处为7
x2[0,0] = 12 # 将第0行第0列元素修改为12
# 当我们试图将一个浮点值插入到一个整型数组中,浮点值会被截取成整型,向下截取,比如3.14被截取成3
数组切片:获取子数组
x = np.arange(10)
x[:5] # 索引为0-4的元素,共5个
x[5:] # 索引从5开始到最后一个
x[4:7] # 索引为4、5、6的元素
x[::2] # 从0开始,步长为2,此处结果为0,2,4,6,8
x[::-1] # 所有元素逆序
x[::-2] # 所有元素逆序后以步长为2取元素,此处结果为9,7,5,3,1
多维切片也采用同样的方式处理,用冒号分隔
x2 = np.array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2[:2, :3] # 选取0、1行的0、1、2列
x2[:3, ::2] # 选取0、1、2行的0,2列
x2[::-1, ::-1] # 选取2,1,0行的3,2,1,0列(逆序了)
x2[0, :] # 第0行的所有列
x2[0] # 与x2[0, :]等价
x2[:, 0] # 所有行的第0列
# 以下是需要注意的地方,由浅复制引起的
x2_sub = x2[:2, :2] # x2_sub与x2指向同一个地址,操作x2_sub等于操作了x2
x2_sub_copy = x2[:2, :2].copy() # x2_sub_copy与x2指向不同的地址,为深复制
数组的变形
x = np.arange(1, 10) # array([1, 2, 3, 4, 5, 6, 7, 8, 9])
grid = x.reshape((3, 3)) # array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
x[np.newaxis, :] # 本质上就是在x的形状左边补1,此处得到的shape变成(1, 9)
x[:, np.newaxis] # 本质上就是在x的形状右边补1,此处得到的shape变成(9, 1)
数组拼接和分裂
x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
np.concatenate([x, y]) # array([1, 2, 3, 3, 2, 1])
grid = np.array([[1, 2, 3], [4, 5, 6]])
np.concatenate([grid, grid]) # 默认沿第一个轴拼接,此处第一个轴是行,所以按行拼接,此处结果为array([[1, 2, 3], [4, 5, 6],[1, 2, 3], [4, 5, 6]])
np.concatenate([grid, grid], axis = 1) # 沿第二个轴拼接(axis=0表示沿第一个轴),此处结果为array([[1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6]])
x = np.array([1, 2, 3])
grid = np.array([[9, 8, 7], [6, 5, 4]])
np.vstack([x, grid]) # 垂直栈数组,此处可理解为按行拼接,排列成矩阵正好是垂直拼接
y = np.array([[99], [99]])
np.hstack([grid, y]) # 水平栈数组,此处可理解为按列拼接,排列成矩阵正好是水平拼接
数组的分裂
x = [1, 2, 3, 99, 99, 3, 2, 1]
x1, x2, x3 = np.split(x, [3, 5]) # 从索引为3和5的地方来对x进行分裂,此处结果为[1 2 3] [99 99] [3 2 1]
grid = np.arange(16).reshape((4, 4))
upper, lower = np.vsplit(grid, [2]) # 从索引为2的行开始分裂,此处结果为[[0 1 2 3] [4 5 6 7]] [[8 9 10 11] [12 13 14 15]]
left, right = np.hsplit(grid, [2]) # 从索引为2的列开始分裂,此处结果为[[0 1] [4 5] [8 9] [12 13]] [[2 3] [6 7] [10 11] [14 15]]
通用函数
import numpy as np
np.random.seed(0)
def compute_reciprocals(values):
output = np.empty(len(values))
for i in range(len(values)):
output[i] = 1.0 / values[i]
return output
big_array = np.random.randint(1, 100, size = 1000000)
%timeit compute_reciprocals(big_array) # %timeit会计算其右方函数运行七次的时间,此次的一个可能结果是 1.44 s ± 8.87 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit (1.0 / big_array) # 一个可能的结果是:3.29 ms ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# 二者的前后对比可以看出python循环所花费的时间明显更多,所以使用这种解释性语言,尽量少用循环,多用函数包里内嵌的一些方法与特性来解决问题,这样可以提高性能与效率
# 多维数组运算
x = np.arange(9).reshape((3, 3))
2 ** x # 结果是array([[1, 2, 4],[8, 16, 32],[64, 128, 256]], dtype=int32)
运算符与对应的通用函数
运算符 | 对应的通用函数 | 描述 |
---|---|---|
+ | np.add | 加法运算 |
- | np.subtract | 减法运算 |
- | np.negative | 负数运算 |
* | np.multiply | 乘法运算 |
/ | np.divide | 除法运算 |
// | np.floor_divide | 整除法 |
** | np.power | 指数运算 |
% | np.mod | 取模或取余 |
np.absolute(x) # 对x取绝对值
theta = np.linspace(0, np.pi, 3)
np.sin(theta)
np.cos(theta)
np.tan(theta)
x = [-1, 0, 1]
np.arcsin(x)
np.arccos(x)
np.arctan(x)
x = [1, 2, 3]
np.exp(x) # e^x
np.exp2(2) # 2^x
np.power(3, x) # 3^x
np.log(x) # ln(x)
np.log2(x) # log2(x)
np.log10(x) # lg(x)
# 指定输出
x = np.arange(5)
y = np.empty(5)
np.multiply(x, 10, out=y) # 将x与10相乘,得出来的值赋给y
print(y) # [0 10 20 30 40]
y = np.zeros(10)
np.power(2, x, out=y[::2]) # 计算2^x,得出来的值按步长为2从0开始赋值给y
print(y) # [1 0 2 0 4 0 8 0 16 0]
聚合
x = np.arange(1, 6)
np.add.reduce(x) # reduce可以计算最终结果,此处输出为15
np.multiply.reduce(x) # 计算6!,输出为120
np.add.accumulate(x) # accumulate可以存储每次计算的中间结果,此处结果为[1 3 6 10 15]
np.multiply.accumulate(x) # 此处结果为[1 2 6 24 120]
np.multiply.outer(x, x) # 计算外积
# 以下为两种求和方式的对比
big_array = np.random.rand(1000000)
%timeit sum(big_array) # 69.4 ms ± 440 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.sum(big_array) # 439 µs ± 4.07 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# 以下为两种方式求最值的对比
%timeit min(big_array) # 45.1 ms ± 1.67 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.min(big_array) # 499 µs ± 1.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# np.min(big_array)等价于big_array.min(),max、sum可以类比
M = np.random.random((3, 4))
M.min(axis = 0) # 按列求最小值
M.max(axis = 1) # 按行求最大值
函数名称 | NaN安全版本 | 描述 |
---|---|---|
np.sum | np.nansum | 计算元素的和 |
np.prod | np.nanprod | 计算元素的积 |
np.mean | np.nanmean | 计算元素的标准差 |
np.std | np.nanstd | 计算元素的标准差 |
np.var | np.nanvar | 计算元素的方差 |
np.min | np.nanmin | 找出最小值 |
np.max | np.nanmax | 找出最大值 |
np.argmin | np.nanargmin | 找出最小值的索引 |
np.argmax | np.nanargmax | 找出最大值的索引 |
np.median | np.nanmedian | 计算元素的中位数 |
np.percentile | np.nanpercentile | 计算基于元素排序的统计值 |
np.any | 验证是否存在元素为真 | |
np.all | 验证是否所有元素都为真 |
广播
规则1:如果两个数组的维度数不相同,那么小维度数组的形状将会在最左边补1
规则2:如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度为1的维度扩展以匹配另一个数组的形状
规则3:如果两个数组的形状在任何一个维度上都不匹配,且没有任何一个维度等于1,那么会引发异常
M = np.ones((2, 3))
a = np.arange(3)
M + a # array([[1., 2., 3.], [1.,2.,3.]])
a = np.arange(3).reshape((3, 1))
b = np.arange(3)
a + b # array([[0, 1, 2],[1, 2, 3], [2, 3, 4]])
M = np.ones((3, 2))
a = np.arange(3)
掩码(Mask)
x = np.array([1, 2, 3, 4, 5])
x < 3 # array([True, True, False, False, False])
(2 * x) == (x ** 2) # array([False, True, False, False, False])
rng = np.random.RandomState(42) # 和种子的效果差不多
x = rng.randint(10, size=(3, 4)) # array([[6, 3, 7, 4], [6, 9, 2, 6], [7, 4, 3, 7]])
x < 6 # array([[False, True, False, True], [False, False, True, False], [False, True, True, False]])
np.count_nonzero(x < 6) # 计算有多少值小于6,在python中,0为False,其他非零数为True
np.sum(x < 6) # 由于True的默认取值为1,所以就相当于计算x<6的个数
np.sum(x < 6, axis = 1) # 计算每行有多少个值小于6
np.any(x > 8) # 是否存在值大于8
np.all(x < 10) # 是否所有值都小于10
x = np.array([[5, 0, 3, 3],[7, 9, 3, 5],[2, 4, 7, 6]])
x[x < 5] # array([0, 3, 3, 3, 2, 4])
花哨的索引
rand = np.random.RandomState(42)
x = rand.randint(100, size=10) # [51 92 14 71 60 20 82 86 74 74]
[x[3],x[7],x[2]] # [71, 86, 14]
ind = [3, 7, 4]
x[ind] # 索引为3、7、4的元素,array([71, 86, 60])
# 利用花哨的索引,结果的形状与索引数组的形状一致,而不是与被索引数组的形状一致
ind = np.array([[3, 7], [4, 5]])
x[ind] # array([[71, 86], [60, 20]])
x = np.arange(12).reshape((3, 4))
row = np.array([0, 1, 2])
col = np.array([2, 1, 3])
x[row, col] # 取坐标为(0, 2),(1, 1),(2, 3)的元素,结果为array([2, 5, 11])
x[2, [2, 0, 1]] # 可以用广播来理解,取坐标为(2, 2),(2, 0),(2, 1)的元素,结果为array([10, 8, 9])
x[1:, [2, 0, 1]] # 取第1、2行的第2、0、1列,结果为array([6, 4, 5],[10, 8, 9])
# 将花哨的索引和掩码组合使用
mask = np.array([1, 0, 1, 0], dtype=bool)
row = np.array([[0], [1], [2]])
x[row, mask] # 将第0、1、2行的第1、3列元素取出,结果为array([0, 2], [4, 6], [8, 10])
# 用花哨的索引修改值
x = np.arange(10)
i = np.array([2, 1, 8, 4])
x[i] = 99 # [0 99 99 3 99 5 6 7 99 9]
x[i] -= 10 # [0 89 89 3 89 5 6 7 89 9]
数组的排序
x = np.array([2, 1, 4, 3, 5])
np.sort(x) # 将x的结果递增排序,但不会改变x本身,结果为array([1, 2, 3, 4, 5])
x,sort() # 将x的结果递增排序,但会改变x本身, 结果为array([1, 2, 3, 4, 5])
x = np.array([2, 1, 4, 3, 5])
i = np.argsort(x) # 将x中的元素正序排列后,返回每个数据原来所在的索引位置到数组中,此次结果为[1 0 3 2 4]
# 此时使用下列语句可以返回x排列后的数值结果
x[i] # array([1, 2, 3, 4, 5])
rand = np.random.RandomState(42)
x = rand.randint(0, 10, (4, 6))
np.sort(x, axis=0) # 对每一列进行排序
np.sort(x, axis=1) # 对每一行进行排序
# 部分排序: 分隔
# np.partition 函数的输入是数组和数字 K, 输出结果是一个新数组, 最左边是第 K 小的值, 往右是任意顺序的其他值,二者都不排序
x = np.array([7, 2, 3, 1, 6, 5, 4])
np.partition(x, 3) # 数组中前三个值是数组中最小的三个值, 剩下的位置是原始数组剩下的值