Numpy,一篇足以

发布时间 2023-08-01 12:09:34作者: 码农要战斗

numpy

用于数值计算

  • ndarray, 一个有效的多维数组,能提供以数组为导向的快速数值计算和灵活的广播功能(broadcasting)
  • 便利的数学函数
  • 用于读取/写入(reading/writing)数据到磁盘的便利工具
  • 线性代数,随机数生成,傅里叶变换能力
  • 可以用C API来写C,C++,或FORTRAN

ndarray

N-dimensional array object(n维数组对象)

# 生成一个随机数组
import numpy as np
data = np.random.randn(2, 3)
print(data.shape) # shape表示维度
print(data.dtype) # data type, output:dtype('float64')

创建

# list类型转化
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1) # 自动分配data type
# 嵌套的list也可
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
print(arr2.ndim) # 输出维度
# 其他创建方式
np.zeros(10)
np.zeros((3, 6))
np.empty((2, 3, 4)) # 其中内容相当于未初始化的数组,而非0
np.arange(15) # output:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

数据类型

arr1 = np.array([1, 2, 3], dtype=np.float64) # 指定为浮点型
# astype:类型转化,总是会返回一个新的数组
arr = np.array([1, 2, 3, 4, 5])
float_arr = arr.astype(np.float64) # 用astype转化类型
numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
numeric_strings.astype(float) # string也能转
# 使用其他数组的dtype定制类型
int_array = np.arange(10)
calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
print(int_array.astype(calibers.dtype))

计算

不需要for,任何两个大小相等的数组,运算都是点对点

element-wise 点对点,就是指两个数组的运算,在同一位置的元素间才会进行运算。

这种算数操作如果涉及标量(scalar)的话,会涉及到数组的每一个元素:

arr = np.array([[1., 2., 3.], [4., 5., 6.]])
arr * arr # array([[  1.,   4.,   9.], [ 16.,  25.,  36.]])
arr - arr # array([[ 0.,  0.,  0.], [ 0.,  0.,  0.]])
1 / arr # array([[ 1.        ,  0.5       ,  0.33333333], [ 0.25      ,  0.2       ,  0.16666667]])
arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
arr2 > arr # array([[False,  True, False], [ True, False,  True]], dtype=bool)

基本索引和切片

array的切片后的结果只是一个views(视图),用来代表原有array对应的元素,而不是创建了一个新的array。但list里的切片是产生了一个新的list

# 和列表的区别:broadcasted(广式转变)
arr = np.arange(10)
arr1[5:8] = 12 # 如果是列表,会报错:TypeError: can only assign an iterable
# 数组切片的结果类似于指针
arr_slice = arr[5:8]
arr_slice[:] = 64 # 赋值给arr[5:8]的所有元素 array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])
# 二维数组单一索引指一维数组
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[2] # array([7, 8, 9])
# 访问单一元素
arr2d[0][2]
arr2d[0, 2]
# 多维数组: 返回一个低维度的多维数组
arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
arr3d[0] # np.array([[1, 2, 3], [4, 5, 6]])
# 用标量赋值
old_values = arr3d[0].copy()
arr3d[0] = 42 # array([[[42, 42, 42],[42, 42, 42]], [[ 7,  8,  9],[10, 11, 12]]])
# 用数组赋值
arr3d[0] = old_values # array([[[ 1,  2,  3],[ 4,  5,  6]], [[ 7,  8,  9],[10, 11, 12]]])

切片后返回的数组都是views

用切片索引

# 选中前两行
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[:2] # array([[1, 2, 3],[4, 5, 6]])
# 区别
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
list2d = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 由于数组更兼容的访问方式
arr2d[1,1] # 5,但list2d[1,1]会报错
# 因此数组可以通过下列方式切片,但列表不能
arr2d[:, 1:] # array([[2,3], [5,6], [8,9]])

bool索引

通过布尔值作为数组的索引

score = np.array([45, 68, 95, 35, 77, 44, 60])
reward = np.random.randn(7, 3)
reward[score > 60] # 输出reward的2,3,5,7行
reward[score > 60, 1] # 输出reward的2,3,5,7行2列的一维数组
布尔运算 符号 说明
取反 !=~
& and无效
` `

用布尔索引总是会返回一份新创建的数据,原本的数据不会被改变。

# 改变所有行或列
reward[score > 70] = 100 # 改变3,5行的所有值
reward[:, 条件] = 100

花式索引

会套两层[[]],得到的是一个新的array

arr = np.empty((8, 4))
for i in range(8):
    arr[i] = i
# 按一定顺序选出几行
arr[[4, 3, 0, 6]] # array([[ 4.,  4.,  4.,  4.], [ 3.,  3.,  3.,  3.], [ 0.,  0.,  0.,  0.], [ 6.,  6.,  6.,  6.]])
arr[[-3, -5, -7]] # array([[ 5.,  5.,  5.,  5.],[ 3.,  3.,  3.,  3.],[ 1.,  1.,  1.,  1.]])
arr = np.arange(32).reshape((8, 4))
arr[[1, 5, 7, 2], [0, 3, 1, 2]] # array([ 4, 23, 29, 10])

可以看到[ 4, 23, 29, 10]分别对应(1, 0), (5, 3), (7, 1), (2, 2)。不论数组有多少维,fancy indexing的结果总是一维。

arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]] # array([[ 4,  7,  5,  6], [20, 23, 21, 22], [28, 31, 29, 30], [ 8, 11,  9, 10]])

上面的意思是,先从arr中选出[1, 5, 7, 2]这四行:

  array([[ 4,  5,  6,  7],
[20, 21, 22, 23],
[28, 29, 30, 31],
[ 8,  9, 10, 11]])

然后[:, [0, 3, 1, 2]]表示选中所有行,但是列的顺序要按0,3,1,2来排。于是得到:

array([[ 4,  7,  5,  6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11,  9, 10]])

数组转置和轴交换

常用于矩阵乘法

转置也是返回一个view,而不是新建一个数组。有两种方式,一个是transpose方法,一个是T属性

# T属性
arr = np.arange(15).reshape((3, 5))
'''
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])'''
arr.T
'''output
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])'''
# transpose会接受由轴数字组成的tuple,来交换轴:
arr = np.arange(16).reshape((2, 2, 4))
'''
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])'''
arr.transpose((1, 0, 2))
'''output
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])'''
# swapaxes方法:取两个axis值,并交换这两个轴:
arr.swapaxes(1, 2) # 直交换second axis和last axis
'''
array([[[ 0,  4],
        [ 1,  5],
        [ 2,  6],
        [ 3,  7]],

       [[ 8, 12],
        [ 9, 13],
        [10, 14],
        [11, 15]]])'''

通用函数

快速点对点函数

一元通用函数

需要一个数组

arr = np.arange(10)
np.sqrt(arr) # 开方
np.exp(arr) # e^x
函数名 描述
np.abs(x) 返回数组的绝对值
np.fabs(x) 返回数组的绝对值(忽略复数)
np.sqrt(x) 返回数组的平方根
np.square(x) 返回数组的平方
np.exp(x) 返回数组的指数值 \(e^x\)
np.log(x) 返回数组的自然对数
np.log10(x) 返回数组的以10为底的对数
np.log2(x) 返回数组的以2为底的对数
np.sign(x) 返回数组的符号值,1表示正数,-1表示负数,0表示零
np.ceil(x) 返回数组的上限值,即大于等于每个元素的最小整数
np.floor(x) 返回数组的下限值,即小于等于每个元素的最大整数
np.rint(x) 返回数组的四舍五入值,保留到最近的整数
np.modf(x) 将数组拆分为小数部分和整数部分
np.isnan(x) 返回一个布尔数组,指示每个元素是否为 NaN
np.isinf(x) 返回一个布尔数组,指示每个元素是否为无穷大
np.cos(x) 返回数组中每个元素的余弦值
np.sin(x) 返回数组中每个元素的正弦值
np.tan(x) 返回数组中每个元素的正切值
np.arccos(x) 返回数组中每个元素的反余弦值
np.arcsin(x) 返回数组中每个元素的反正弦值
np.arctan(x) 返回数组中每个元素的反正切值
np.degrees(x) 将数组中每个元素从弧度转换为角度
np.radians(x) 将数组中每个元素从角度转换为弧度
np.exp2(x) 返回数组的指数值 \(2^x\)
np.expm1(x) 返回数组的指数值 \(e^x-1\)
np.log1p(x) 返回数组的对数值 \(log_e(x+1)\)
np.deg2rad(x) 将数组中每个元素从角度转换为弧度
np.rad2deg(x) 将数组中每个元素从弧度转换为角度

二元通用函数

需要两个数组

x = np.random.randn(8)
y = np.random.randn(8)
np.maximum(x, y) # 最大值
arr = np.array([3.14, 2.718, 1.414, 0.618])
# 使用 np.modf 将数组拆分为整数部分和小数部分
fractional, integral = np.modf(arr)
# 打印结果
print("原始数组:", arr)
print("小数部分:", fractional)
print("整数部分:", integral)
# ufunc能接受一个可选参数作为输出,这样可以直接更改原有的数组:
np.sqrt(arr) # 没有改变原有的arr
np.sqrt(arr, arr) # 改变了原有的arr
函数名 描述
np.add(x1, x2) 返回两个数组的逐元素和
np.subtract(x1, x2) 返回两个数组的逐元素差
np.multiply(x1, x2) 返回两个数组的逐元素乘积
np.divide(x1, x2) 返回两个数组的逐元素除法
np.power(x1, x2) 返回两个数组的逐元素幂运算结果
np.mod(x1, x2) 返回两个数组的逐元素取模结果
np.remainder(x1, x2) 返回两个数组的逐元素求余结果
np.fmax(x1, x2) 返回两个数组的逐元素最大值
np.fmin(x1, x2) 返回两个数组的逐元素最小值
np.maximum(x1, x2) 返回两个数组的逐元素最大值,其中 NaN 被视为无限大
np.minimum(x1, x2) 返回两个数组的逐元素最小值,其中 NaN 被视为无限小
np.copysign(x1, x2) 返回 x1 的符号与 x2 相同的数组
np.greater(x1, x2) 返回一个布尔数组,指示 x1 中的每个元素是否大于 x2 中的对应元素
np.greater_equal(x1, x2) 返回一个布尔数组,指示 x1 中的每个元素是否大于等于 x2 中的对应元素
np.less(x1, x2) 返回一个布尔数组,指示 x1 中的每个元素是否小于 x2 中的对应元素
np.less_equal(x1, x2) 返回一个布尔数组,指示 x1 中的每个元素是否小于等于 x2 中的对应元素
np.equal(x1, x2) 返回一个布尔数组,指示 x1 中的每个元素是否等于 x2 中的对应元素
np.not_equal(x1, x2) 返回一个布尔数组,指示 x1 中的每个元素是否不等于 x2 中的对应元素
np.logical_and(x1, x2) 返回一个布尔数组,指示 x1 和 x2 中的对应元素是否都为 True
np.logical_or(x1, x2) 返回一个布尔数组,指示 x1 和 x2 中的对应元素是否至少有一个为 True
np.logical_xor(x1, x2) 返回一个布尔数组,指示 x1 和 x2 中的对应元素是否恰好有一个为 True
np.arctan2(x1, x2) 返回两个数组中每个元素的反正切值

数组导向编程

网格

meshgrid函数用两个坐标轴上的点在平面上画网格。用法:

  • [X,Y]=meshgrid(x,y)

  • [X,Y]=meshgrid(x)[X,Y]=meshgrid(x,x)是等同的

  • [X,Y,Z]=meshgrid(x,y,z)生成三维数组,可用来计算三变量的函数和绘制三维立体图

[X,Y] = meshgrid(x,y) 将向量x和y定义的区域转换成矩阵X和Y,其中矩阵X的行向量是向量x的简单复制,而矩阵Y的列向量是向量y的简单复制(注:下面代码中X和Y均是数组,在文中统一称为矩阵了)。

假设x是长度为m的向量,y是长度为n的向量,则最终生成的矩阵X和Y的维度都是 nm

X, Y = np.meshgrid(x, y) # X = [x, x, .., x] Y = [y变为列向量拼接而成]
m, n = (5, 3)
x = np.linspace(0, 1, m) # 生成5个[0,1]之间的等间隔数字序列
y = np.linspace(0, 1, n)
X, Y = np.meshgrid(x, y)
X.shape, y.shape # (3, 5) (3, 5)
# 绘制网格
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')

plt.plot(X, Y, marker='.', color='blue', linestyle='none')
# 用zip得到网格平面的坐标数据
z = [i for i in zip(X.flat, Y.flat)] # .flat:将多维数组展成一维,zip:将可迭代对象打包为元组

python中可迭代的对象:

  • 列表(List)
  • 元组(Tuple)
  • 字符串(String)
  • 集合(Set)
  • 字典(Dictionary)
  • 文件对象(File)
  • range 对象(Range)
  • 迭代器对象(Iterator)
  • 生成器对象(Generator)

数组-逻辑条件

numpy.where函数是一个向量版的三相表达式,x if condition else y

# where 第二个参数和第三个参数不必是数组
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = np.where(cond, xarr, yarr) # array([ 1.1,  2.2,  1.3,  1.4,  2.5])

数学和统计方法

函数 描述
numpy.sum 计算数组或某个轴向上的元素总和
numpy.cumsum 计算数组或某个轴向上的元素累积和
numpy.mean 计算数组或某个轴向上的元素平均值
numpy.std 计算数组或某个轴向上的元素标准差
numpy.var 计算数组或某个轴向上的元素方差
numpy.min 找出数组或某个轴向上的最小值
numpy.max 找出数组或某个轴向上的最大值
numpy.argmin 找出数组或某个轴向上的最小值的索引值
numpy.argmax 找出数组或某个轴向上的最大值的索引值
numpy.median 计算数组或某个轴向上的元素中位数
numpy.percentile 计算数组或某个轴向上的元素在指定百分位数处的值
numpy.any 判断数组或某个轴向上是否有任意一个元素为 True
numpy.all 判断数组或某个轴向上是否所有元素都为 True
numpy.ptp 计算数组或某个轴向上的元素峰值-峰值距离

轴向参数:axis=0 | 1

布尔数组

有两个其他方法,any和all,对于布尔数组特别有用。any检测数组中只要有一个ture返回就是true,而all检测数组中都是true才会返回true。

arr = np.random.randn(100)
(arr > 0).sum() # Number of positive values

排序

# sort 二维
arr.sort(0) # 排列向量,默认是0
arr.sort(1) # 排行向量
# np.sort 不改变原有数组
np.sort(a, axis=-1, kind=None, order=None)

单一性

# unqiue 排序&不重复
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names) # array(['Bob', 'Joe', 'Will'], dtype='<U4')
# 一个数组中是否存在另一个数组的值
values = np.array([6, 0, 0, 3, 2, 5, 6])
np.in1d(values, [2, 3, 6]) # array([ True, False, False,  True,  True, False,  True], dtype=bool)

运算

运算 用例 \(a*b\) np.multiple(a,b) np.matmul(a,b) np.dot(a,b) a@b
向量 \(\begin{bmatrix} 0&1\end{bmatrix} \begin{bmatrix}1&2\end{bmatrix}\) \(\begin{bmatrix}0&2\end{bmatrix}\) \(\begin{bmatrix}0&2\end{bmatrix}\) 2 2 2
不同维度的多维数组(np.array) \(\begin{bmatrix}1&2\\3&4\\5&6\end{bmatrix} \begin{bmatrix}1\\2\end{bmatrix}\) ValueError ValueError \(\begin{bmatrix}5\\11\\17\end{bmatrix}\) \(\begin{bmatrix}5\\11\\17\end{bmatrix}\) \(\begin{bmatrix}5\\11\\17\end{bmatrix}\)
同维的多维数组(np.array) $\begin{bmatrix}0&1\2&3\end{bmatrix} \begin{bmatrix}2&3\4&5\end{bmatrix} $ \(\begin{bmatrix}0&3\\8&15\end{bmatrix}\) \(\begin{bmatrix}0&3\\8&15\end{bmatrix}\) \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\) \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\) \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\)
矩阵(np.array) $\begin{bmatrix}0&1\2&3\end{bmatrix} \begin{bmatrix}2&3\4&5\end{bmatrix} $ \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\) \(\begin{bmatrix}0&3\\8&15\end{bmatrix}\) \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\) \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\) \(\begin{bmatrix}4&5\\16&21\end{bmatrix}\)

推荐

  • 对应项相乘用np.multiple(a,b)
  • 矩阵乘法用np.dot(a,b)np.matmul(a,b)