NumPy

官网

NumPy是Python数值计算的基石,它提供多种数据结构、算法以及大部分涉及Python数值计算所需的接口

概括如下:

  • 快速、高效的多维数组对象ndarray
  • 基于元素的数组计算或数组间数学操作函数
  • 用于读写硬盘中基于数组的数据集的工具
  • 线性代数操作、傅里叶变换以及随机数生成
  • 成熟的C语言API,允许Python拓展和本地的C或C++代码访问NumPy的数据结构和计算设施

创建ndarray

import numpy as np

# 创建数组,下面几种效果一样
a = np.array([1, 2, 3, 4, 5])
b = np.asarray([1, 2, 3, 4, 5])     # np.asarray和np.array的区别:如果输入已经是ndarray,则不再复制
c = np.array(range(1, 6))
d = np.arange(1, 6)
e = np.linspace(1, 5, 5)  # 开始、结束、数量

# 生成三行四列全为0或1或空的数组
a = np.zeros((3, 4))
b = np.ones((3, 4))
b = np.empty((3, 4))

# 根据a的形状,生成0或1或空的数组
a = np.arange(1, 13).reshape(3, 4)
print(np.zeros_like(a))
print(np.ones_like(a))
print(np.empty_like(a))

# 创建3阶单位矩阵,以下2个函数等效
a = np.eye(3)
a = np.identity(3)

# 根据形状和数据,创建ndarray
# [
#   [1,2,3],
#   [1,2,3]
# ]
a = np.full((2, 3), [1, 2, 3])

# 根据给定数组的形状和数据,创建ndarray
# [
#   [1,1,1],
#   [1,1,1]
# ]
b = np.full_like(a, 1)

数据类型


# 数组的类型 <class 'numpy.ndarray'>
a = np.array([1, 2, 3, 4, 5])
type(a)

# 数据的类型 int64
a.dtype

# 指定创建数据的类型
a = np.array([1, 0, 1, 1, 0, 2], dtype='?')
a = np.array([1, 0, 1, 1, 0, 2], dtype=bool)

# 修改数据的类型
a.astype('int8')
a.astype('i1')
a.astype(np.int8)
类型 类型代码 描述
int8、uint8 i1、u1 有符号、无符号的8位(1个字节)整型
int16、uint16 i2、u2 有符号、无符号的16位(2个字节)整型
int32、uint32 i4、u4 有符号、无符号的32位(4个字节)整型
int64、uint64 i8、u8 有符号、无符号的64位(8个字节)整型
float16 f2 半精度浮点数
float32 f4或f 标准的单精度浮点数。与C的floaat兼容
float64 f8或d 标准的双精度浮点数。与C的double和python的float对象兼容
float128 f16或g 扩展精度浮点数
complex64、complex128、complex256 c8、c16、c32 分别用两个32位、64位或128位浮点数表示的复数
bool ? 存储True、False值的布尔类型

数组的形状

reshape有个order参数,是指以什么样的顺序读写元素(一般用默认的即可)

  • C:默认参数,使用类似C-like语言(行优先)中的索引方式进行读写
  • F:使用类似Fortran-like语言(列优先)中的索引方式进行读写
  • A:原数组如果是按照C的方式存储数组,则用C的索引对数组进行reshape,否则使用F的索引方式
import numpy as np

# 一维数组,输出(5,),表示数组中有5列(5维行向量)
a = np.array([1, 2, 3, 4, 5])
print(a.shape)

# 二维数组,输出(3,2),表示3行2列
a = np.array([[1, 2], [3, 4], [5, 6]])
print(a.shape)

# 三维数组,输出(2,3,2),表示2块3行2列
a = np.array([
    [[1, 2], [3, 4], [5, 6]],
    [[7, 8], [9, 10], [11, 12]]
])
print(a.shape)

# 修改数组的形状
# 一维、二维、三维可以动态切换形状的(前提是元素数量能对上),输出(3,4)
a = np.arange(1, 13).reshape(3, 4)
print(a.shape)

# 展开数组
# 不管几维,通通转换成一维,输出(12,)
a = np.arange(1, 13).reshape(2, 3, 2)
print(a.flatten().shape)

# shape这个属性在深度学习中也经常使用,比如我们要获取数据(B,W,H,C)(一个batchsize为B的(W,H,C)数据)的形状(宽高)
input_data.shape[1:3]

数组的计算

  • 与值计算,numpy具有广播机制,加减乘除会被计算到所有元素上
  • 与数组计算,后缘维度(从末尾开始算起的维度)的轴相等或其中一方的长度为1,则广播兼容。广播会在缺失或长度为1的维度上进行
    • 结构完全相同,对应位置一一相加
    • 结构完全对不上,直接报错
    • 列相同,行=1,ndarray a=(3, 4),ndarray b=(1, 4),则:a每行的元素+对应位置的b元素
    • 行相同,列=1,ndarray a=(3, 4),ndarray b=(3, 1),则:a每列的元素+对应位置的b元素
import numpy as np

# 与值计算
a = np.arange(1, 7).reshape(2, 3)
print(a + 1)

# 结构相同
a = np.arange(1, 7)
b = np.arange(11, 17)
print(a + b)

# 结构对不上
a = np.arange(1, 13).reshape(3, 4)
b = np.arange(1, 13).reshape(2, 6)
print(a + b)

# 列相同,行=1
a = np.arange(1, 13).reshape(3, 4)
print(a)
b = np.arange(1, 5).reshape(1, 4)
print(b)
print(a + b)

# 行相同,列=1
a = np.arange(1, 13).reshape(3, 4)
print(a)
b = np.arange(1, 4).reshape(3, 1)
print(b)
print(a + b)

numpy中的转置

import numpy as np

# 以下效果一样
a = np.arange(1, 13).reshape(3, 4)  # type:np.ndarray
print(a.transpose())    # 方法
print(a.T)  # 属性
print(a.swapaxes(0, 1)) # 交换0轴和1轴
print(a.swapaxes(1, 0)) # 交换1轴和0轴

# 三维数组的转置
a = np.arange(16).reshape((2, 2, 4))
b = a.copy()
c = a.copy()
'''
[[[ 0  1  2  3]
  [ 4  5  6  7]]

 [[ 8  9 10 11]
  [12 13 14 15]]]
'''
print(a)

# 以下几种效果一致,0轴和1轴互换,2轴不变
'''
0(0,0,0)    1(0,0,1)    2(0,0,2)     3(0,0,3)
4(0,1,0)    5(0,1,1)    6(0,1,2)     7(0,1,3)
8(1,0,0)    9(1,0,1)    10(1,0,2)    11(1,0,3)
12(1,1,0)   13(1,1,1)   14(1,1,2)    15(1,1,3)

交换0轴和1轴后,变成:

0(0,0,0)    1(0,0,1)    2(0,0,2)     3(0,0,3)
8(1,0,0)    9(1,0,1)    10(1,0,2)    11(1,0,3)
4(0,1,0)    5(0,1,1)    6(0,1,2)     7(0,1,3)
12(1,1,0)   13(1,1,1)   14(1,1,2)    15(1,1,3)
'''
print(a.transpose((1, 0, 2)))
print(b.swapaxes(0, 1))
print(c.swapaxes(1, 0))

# 0轴不变,1轴和2轴参照二维数组的转置
print(b.swapaxes(1, 2))

索引和切片

大部分场景用到的是一维数组和二维数组,所以下面以一维数组和二维数组举例

切片和range一样,都是含头不含尾的 [0,2)

下面提到的第x行第x列都是以索引的方式计算,即:从0开头

二维数组只要记住逗号前面的是行后面的是列就行了,其他跟一维数组类似

import numpy as np

# 一维数组
a = np.arange(1, 13)
print(a[0])  # 输出第0个元素,结果:1
print(a[4:6])  # 输出第4个和第5个元素,结果:[5 6]
print(a[:])  # 输出全部元素,结果:[ 1  2  3  4  5  6  7  8  9 10 11 12]
print(a[9:])  # 输出第9个元素到末尾,结果:[10 11 12]
print(a[-1])  # 输出最后个元素,结果:12
print(a[-2:])  # 输出最后2个元素,结果:[11 12]
print(a[-2:-1])  # 输出倒数第2个元素(含头不含尾),结果:[11]
print(a[::2])  # 调整步长为2,输出:[ 1  3  5  7  9 11]
print(a[0:13:2])  # 调整步长为2,输出:[ 1  3  5  7  9 11]

# 二维数组
a = np.arange(1, 13).reshape(3, 4)
print(a)
print(a[0][0])  # 输出第0行第0列,结果:1
print(a[0])  # 输出第0行,结果:[1 2 3 4]
print(a[:, 0])  # 输出第0列,结果:[1 5 9]
print(a[[2, 0]])  # 输出第2行和第0行
print(a[[-1, -2]])  # 输出倒数第1行和倒数第2行(-1表示最后一行(列))
print(a[0:3])  # 输出第0行到第2行(含头不含尾)
print(a[:, [3, 1]])  # 输出第3列和第1列
print(a[:, 0:3])  # 输出第0列到第2列(含头不含尾)
print(a[:, 0:3:2])  # 输出第0列到第2列且步长为2(含头不含尾)

# 取指定元素
a = np.arange(32).reshape(8, 4)
print(a[[1, 5, 7, 2], [0, 3, 1, 2]])    # [ 4 23 29 10]

# 取指定区域(先取行再取列)
# [[ 4  7  5  6]
# [20 23 21 22]
# [28 31 29 30]
# [ 8 11  9 10]]
print(a[[1, 5, 7, 2]][:, [0, 3, 1, 2]])

布尔索引

布尔索引不使用andornot,而是使用&|~

import numpy as np

# 正数的个数
arr = np.random.randn(10)
print((arr > 0).sum())

# 有一个True则返回True
print((arr > 0).any())

# 全为True才返回True
print((arr > 0).all())

a = np.arange(12).reshape(3,4)
b = np.arange(0,24,2).reshape(3,4)

# 只有第一个是True,其他全为False
print(a==b)
# 只有第一个是False,其他全为True,下面2种写法等价
print(a != b)
print(~(a == b))

数组的赋值

import numpy as np

a = np.arange(1, 13).reshape(3, 4)  # type:np.ndarray

# 正常赋值
a[:, 2:4] = 0
a[2, 2] = 2
a[2][2] = 2
a[0] = 0

# 利用bool索引进行赋值
a[a < 3] = 0  # 小于3的值全部替换为0
a = np.where(a < 3, 0, 1)  # 三元运算,小于3替换为0,其他替换为1

# 裁剪,小于等于10的全部替换为10,大于等于11的全部替换为11
a = a.clip(10, 11)

numpy的复制

numpy被设计成适合处理非常大的数组,所以能用视图的地方就不会用复制

  • 视图:可以简单理解为浅拷贝
    • 修改原数组,视图会受到影响,赋值、切片、view()、reshape()等
  • 复制:可以简单理解为深拷贝
    • 修改原数组,副本不会受到影响,神奇索引(如:arr[[1,2]])、arr.copy()、布尔索引等

数组数据结构信息区中有numpy数组的形状(shape)以及数据类型(data-type)等信息

而数据存储区则是用于存储数组的数据,numpy数组中的数据可以指向其它数组中的数据,这样多个数组可以共用同一个数据

import numpy as np

a = np.arange(12)
b = a.reshape((3, 4))
c = a.reshape((4, 3))

# a = [ 0  1  2  3  4  5  6  7  8  9 10 11] 
# a.base = None
# a.flags.owndata = True
print(a, a.base, a.flags.owndata)
# b = 3行4列数组
# b.base = [ 0  1  2  3  4  5  6  7  8  9 10 11] 
# b.flags.owndata = False
print(b, b.base, b.flags.owndata)
# c = 4行3列数组
# c.base = [ 0  1  2  3  4  5  6  7  8  9 10 11] 
# c.flags.owndata = False
print(c, c.base, c.flags.owndata)

numpy中的nan和inf

  • nan:not a number
    • 当类型为float时,读取文件有缺失或者赋值为None,会变成nan
    • 两个nan是不相等的
    • 对nan的处理,如:删除、替换为0、替换为均值或中值,根据实际场景来
  • inf:无穷大
    • 当类型为float时,正数除以0会变成inf,负数除以0会变成-inf
import numpy as np

b = np.arange(1, 13).astype(np.float32).reshape(3, 4)  # type:np.ndarray
# inf
b[2, 2] = b[2][2] / 0
# nan
b[2, 2] = None
# nan不会被裁剪到
print(b.clip(9, 10))
# 统计非零值(包括False)的个数,利用nan!=nan的特性,可以统计nan的个数
print(np.count_nonzero(b != b))
# nan和任何值计算还是nan
b = b * 2
# 把nan替换为0,下面2个等效
b[np.isnan(b)] = 0
b[b != b] = 1

数组的拼接

import numpy as np

a = np.arange(1, 13).reshape(3, 4)  # type:np.ndarray
b = a.copy()
# 竖直拼接
print(np.vstack((a, b)))
# 水平拼接
print(np.hstack((a, b)))

数组的行列交换

import numpy as np

a = np.arange(1, 13).reshape(3, 4)  # type:np.ndarray
# 行交换,第一行和第二行交换
a[[1, 2], :] = a[[2, 1], :]
# 列交换,第一列和第二列交换
a[:, [1, 2]] = a[:, [2, 1]]

常用统计函数

import numpy as np

a = np.arange(1, 13).reshape(3, 4)  # type:np.ndarray

# 默认返回多维数组的全部的统计结果,如果指定axis则返回当前轴方向上的聚合结果
print(a.sum())  # 求和
print(a.mean())  # 均值
print(np.median(a))  # 中值,奇数:中间的值,偶数:中间2个值的均值
print(a.max())  # 最大值
print(a.min())  # 最小值
print(a.argmax())  # 最大值的位置
print(a.argmin())  # 最小值的位置
print(a.ptp())  # 极值,最大值和最小值的差
print(a.std())  # 标准差(默认分母是n)
print(a.var())  # 方差(默认分母是n)
print(a.cumsum())  # 迭代累加
print(a.cumprod())  # 迭代累乘

指定轴的话,是以指定轴的方向进行聚合,我们可以将同一个轴上的数据看做同一个单位,那聚合的时候,我们只需要在同级别的单位上进行聚合就可以了

import numpy as np


a = np.arange(18).reshape(3, 2, 3)  # type:np.ndarray

'''
[[[ 0  1  2]
  [ 3  4  5]]

 [[ 6  7  8]
  [ 9 10 11]]

 [[12 13 14]
  [15 16 17]]]
'''
print(a)
print("*" * 100)

'''
0轴方向的单位:
[[ 0  1  2]
  [ 3  4  5]]
...

结果是一个(2, 3)的数组:
[ 
    [sum(a_000, a_100, a_200), sum(a_001, a_101, a_201), sum(a_002, a_102, a_202)],
    [sum(a_010, a_110, a_210), sum(a_011, a_111, a_211), sum(a_012, a_112, a_212)] 
]
[[18 21 24]
 [27 30 33]]
'''
print(a.sum(axis=0))
print("*" * 100)

'''
1轴方向的单位
[ 0  1  2]
...

结果是一个(3, 3)的数组:
[
    [sum(a_000, a_010), sum(a_001, a_011), sum(a_002, a_012)],
    [sum(a_100, a_110), sum(a_101, a_111), sum(a_102, a_112)],
    [sum(a_200, a_210), sum(a_201, a_211), sum(a_202, a_212)],
]
[[ 3  5  7]
 [15 17 19]
 [27 29 31]]
'''
print(a.sum(axis=1))
print("*" * 100)

'''
2轴方向的单位
0 1 2 ...

结果是一个(3, 2)的数组:
[
    [sum(a_000, a_001, a_002), sum(a_010, a_011, a_012)],
    [sum(a_100, a_101, a_102), sum(a_110, a_111, a_112)],
    [sum(a_200, a_201, a_202), sum(a_210, a_211, a_212)],
]
[[ 3 12]
 [21 30]
 [39 48]]
'''
print(a.sum(axis=2))
print("*" * 100)

常用随机函数

  • seed:向随机数生成器传递随机状态种子
  • permutation:返回一个序列的随机排列,或者返回一个乱序的整数范围序列
  • shuffle:随机排列一个序列
  • rand:从均匀分布中抽取样本
  • randint:根据给定的由低到高的范围抽取随机整数
  • randn:从均值0方差1的正态分布中抽取样本(MATLAB型接口)
  • binomial:从二项分布中抽取样本
  • normal:从正态(高斯)分布中抽取样本
  • beta:从beta分布中抽取样本
  • chisquare:从卡方分布中抽取样本
  • gamma:从伽马分布中抽取样本
  • uniform:从均匀[0,1)分布中抽取样本
import numpy as np

# 生成3行4列的均匀分布的随机数(0到1之间)
a = np.random.rand(3, 4)
# 生成3行4列的标准正态分布的随机数(平均数0,标准差1)
b = np.random.randn(3, 4)
# 生成3行4列1到99的整数随机数
c = np.random.randint(1, 100, (3, 4))
# 生成3行4列的均匀分布的随机数(1-99之间)
d = np.random.uniform(1, 100, (3, 4))
# 生成3行4列的标准正态分布的随机数(平均数50,标准差2)
e = np.random.normal(50, 2, (3, 4))
# 随机种子,生成相同的随机数
np.random.seed(3)
f = np.random.randint(1, 100, (3, 4))

排序函数

如何理解NumPy中axis的使用?

  • axis=0代表跨行(实际上就是按列)
  • axis=1代表跨列(实际上就是按行)
  • 没有指定axis,默认axis=-1(按照数组最后一个轴来排序)
  • axis=None,以扁平化的方式作为一个向量进行排序
a = np.random.randint(1, 100, (3, 4))  # type:numpy

# 默认为:快排、升序、最后一个轴
print(np.sort(a, axis=-1, kind='quicksort', order=None))
# 轴为None,扁平排序,降序排序(numpy中的排序没有reverse参数)
print(abs(np.sort(-a, axis=None)))
# 0轴排序
print(np.sort(a, axis=0))
# 1轴排序,结果与默认相同,因为二维数组,1就是最后一个轴
print(np.sort(a, axis=1))

奇进偶舍

奇进偶舍,又称为四舍六入五成双规则、银行进位法(Banker's Rounding),是一种计数保留法,是一种数值修约规则

从统计学的角度,“奇进偶舍”比“四舍五入”更为精确:在大量运算时,因为舍入后的结果有的变大,有的变小,更使舍入后的结果误差均值趋于零

而不是像四舍五入那样逢五就进位,导致结果偏向大数,使得误差产生积累进而产生系统误差。“奇进偶舍”使测量结果受到舍入误差的影响降到最低

  • 保留位数的后一位如果是4,则舍
  • 保留位数的后一位如果是6,则入
  • 保留位数的后一位如果是5(且5是最后一位),保留位数是奇数则入,保留位数是偶则舍
  • 保留位数的后一位如果是5(5后面还有数),则入
import numpy as np
from decimal import Decimal

# 奇进偶舍
a = np.array([1.345, 1.3451, 1.335])
a = np.round(a, 2)
print(a)

# 四舍五入
print(Decimal('1.345').quantize(Decimal("0.01"), rounding="ROUND_HALF_UP"))

一元通用函数

  • abs、fabs:逐元素地计算整数、浮点数或复数的绝对值
  • sqrt:计算每个元素的平方根(与arr ** 0.5相等)
  • square:计算每个元素的平方(与arr ** 2相等)
  • exp:计算每个元素的自然指数值exe^x
  • log、log10、log2、log1p:分别对应自然对数(e为底)、对数10为底、对数2为底、log(1+x)
  • sign:计算每个元素的符号值,1(正数)、0(0)、-1(负数)
  • ceil:向上取整
  • floor:向下取整
  • rint:将元素保留到整数位,并保持dtype
  • modf:分别将数组的小数部分和整数部分按数组形式返回
  • isnan:返回数组中的元素是否是一个NaN(不是一个数值),形式为布尔值数组
  • isfinite,isinf:分别返回数组中的元素是否有限(非inf、非NaN)、是否无限的,形式为布尔值数组
  • cos、cosh、sin、sinh、tan、tanh:常规的双曲三角函数
  • arccos、arccosh、arcsin、arcsinh、arctan、arctanh:反三角函数
  • logical_not:对数组的元素按位取反(与 ~ arr 效果一致)

二元通用函数

  • add:跟直接用加号一致
  • subtract:跟直接用减号一致
  • multiply:跟直接用乘号一致
  • divide、floor_divide:除或整除(放弃余数)
  • power:将第二个数组的元素作为第一个数组对应元素的幂次方
  • maximum、fmax:逐个元素计算最大值,fmax忽略NaN
  • minimum、fmin:逐个元素计算最小值,fmin忽略NaN
  • mod:按元素的求模计算(即求除法的余数)
  • copysign:将第一个数组的符号值改为第二个数组的符号值
  • greater、greater_equal、less、less_equal、equal、not_equal:>、>=、<、<=、==、!=
  • logical_and、logical_or、logical_xor:&、|、^

数组合并

import numpy as np

arr1 = np.arange(12).reshape(3, 4)
arr2 = np.arange(13, 25).reshape(3, 4)
arr = np.concatenate([arr1, arr2])
'''
沿着0轴方向合并(默认)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [13 14 15 16]
 [17 18 19 20]
 [21 22 23 24]]
'''
print(arr)
arr = np.concatenate([arr1, arr2], axis=1)
'''
沿着1轴方向合并
[[ 0  1  2  3 13 14 15 16]
 [ 4  5  6  7 17 18 19 20]
 [ 8  9 10 11 21 22 23 24]]
'''
print(arr)

扩充维度

扩维操作在深度学习中也经常用到,PyTorch中的函数名叫unsqueeze()(取消挤压),TensorFlow与NumPy名称相同,直接使用tf.newaxis就可以了

import numpy as np

arr = np.arange(3)
# [0 1 2]
print(arr)
arr = arr[:, np.newaxis]
'''
扩充维度
[[0]
 [1]
 [2]]
'''
print(arr)

线性代数相关函数

numpy中的矩阵相乘是矩阵的逐元素相乘,跟线性代数里面的点乘有所区别

如果想用点乘,可以使用如下2种方式(或np.linalg库)

  • ndarray1 @ ndarray2
  • ndarray1.dot(ndarray2)

np.linalg库相关函数

  • diag:将一个方阵的对角(或非对角)元素作为一维数组返回,或者将一维数组转换成一个方阵,并且在非对角线上有零点
  • dot:矩阵点乘
  • trace:计算对角元素和
  • det:计算矩阵的行列式
  • eig:计算方阵的特征值和特征向量
  • inv:计算方阵的逆矩阵
  • pinv:计算矩阵的Moore-Penrose伪逆
  • qr:计算QR分解
  • svd:计算奇异值分解(SVD)
  • solve:求解x的线性系统Ax=b,其中A是方阵
  • lstsq:计算Ax=b的最小二乘解

一维集合相关函数

  • unique:去重并排序,np.unique(a)
  • intersect1d:交集并排序,np.intersect1d(a, b)
  • union1d:并集并排序,np.union1d(a, b)
  • in1d:计算a中的元素是否包含在b中,返回一个bool数组,np.in1d(a, b)
  • setdiff1d:差集并排序(在a中但不在b中的a的元素),np.setdiff1d(a, b)
  • setxor1d:异或集并排序(在a或b中,但不属于a、b交集的元素),np.setxor1d(a, b)

其他常用函数

import numpy as np

a = np.arange(1, 13).reshape(3, 4)

print(len(a))  # 3行,输出:3
print(a.size)  # 12个元素,输出:12
print(a.ndim)  # 二维,输出:2
print(a.itemsize)  # 每个元素的字节大小

points = np.arange(3)
'''
返回两个矩阵,第一个元素是X轴的取值,第二个元素是Y轴的取值
[array([[0, 1, 2],
       [0, 1, 2],
       [0, 1, 2]]), array([[0, 0, 0],
       [1, 1, 1],
       [2, 2, 2]])]
'''
print(np.meshgrid(points, points))

随机漫步

python实现

import random

position = 0
walk = []
steps = 100

for i in range(steps):
    step = 1 if random.randint(0, 1) else -1
    position += step
    walk.append(position)

print(walk)

numpy实现

  • 效率更高:一次生成100个投币结果
  • 方便统计:walk.min()、walk.max()、np.abs(walk)>=10等等
import numpy as np

nsteps = 100

draws = np.random.randint(0, 2, size=nsteps)

steps = np.where(draws > 0, 1, -1)

walk = steps.cumsum()

print(walk)

掩码数组

import numpy as np
from numpy import ma

# 创建一个普通的NumPy数组
arr = np.array([1, 2, 3, 4, 5])

# 创建一个MaskedArray,将索引为1和3的元素标记为缺失
mask = np.array([False, True, False, True, False])
masked_arr = ma.array(arr, mask=mask)

print("原始数组:", arr)
print("MaskedArray:", masked_arr)
print("缺失元素的掩码:", masked_arr.mask)

results matching ""

    No results matching ""