Numpy 数组简介

NumPy是Python的最重要的扩展程序库之一,也是入门机器学习编程的必备工具。Numpy 是 Python 中科学计算的核心库,NumPy 这个词来源于 Numerical 和 Python 两个单词。它提供了一个高性能的多维数组对象,以及大量的库函数和操作,可以帮助程序员轻松地进行数值计算,广泛应用于机器学习模型、图像处理和计算机图形学、数学任务等领域。本文是对Numpy数组的简要介绍,学习更多Python & GIS的相关知识,请移步公众号GeodataAnalysis

1 Numpy数组的创建和属性

NumPy的数组类被称为ndarray,别名也称 arrayndarray对象的几个非常重要的属性如下所示:

  • ndarray.ndim - 数组的轴(维度)的个数。在Python中,维度的数量也被称为秩Rank
  • ndarray.shape - 数组的维度。这是一个整数的元组,表示每个维度中数组的大小。对于有 n 行和 m 列的矩阵,shape 将是 (n,m)。因此,shape 元组的长度就是Rank或维度的个数 ndim
  • ndarray.size - 数组元素的总数。这等于shape的所有元素的乘积。
  • ndarray.dtype - 一个描述数组中元素类型的对象。可以使用标准的Python类型创建或指定dtype。另外NumPy提供它自己的类型。例如numpy.int16numpy.int32numpy.float64,关于Numpy数据类型的详细介绍见Data type objects (dtype) — NumPy v1.21 Manual

Numpy数组的创建方法有很多,这里介绍几个常用的方法,以下代码均在IPython中演示。

可以使用np.array函数从常规Python列表或元组中创建数组,得到的数组的类型是从Python列表中元素的类型推导出来的。array 还可以在创建时显式指定数组的类型,也支持对原有数组进行类型转化,同时还支持将序列的序列转换成二维数组,将序列的序列的序列转换成三维数组,等等。

  In [1]: import numpy as np
  In [2]: a = np.array([3, 4, 5]) #定义数组
  In [3]: a
  Out[3]: array([3, 4, 5])
  In [4]: a.dtype # 查看数组的类型
  Out[4]: dtype('int32')
  In [5]: a.shape # 查看数组的形状
  Out[5]: (3,)
  In [6]: a.ndim # 查看数组轴的个数
  Out[6]: 1
  In [7]: a.size # 查看数组元素的个数
  Out[7]: 3
  In [8]: b = np.array((3.14, 5, 4))
  In [9]: b.dtype # 定义数组时Numpy自动推断数组类型
  Out[9]: dtype('float64')
  In [10]: c = np.array([3, 4, 5], dtype=np.float64) # 定义数组时指定数组类型
  In [11]: c.dtype
  Out[11]: dtype('float64')
  In [12]: c = c.astype(np.int32) # 转换数组类型
  In [13]: c.dtype
  Out[13]: dtype('int32')
  In [14]: d = np.array([(3, 4), (1, 2)]) # 定义二维数组
  In [15]: d
  Out[15]:
  array([[3, 4],
       [1, 2]])
  In [16]: d.shape
  Out[16]: (2, 2)
  In [17]: d.ndim
  Out[17]: 2
  In [18]: d.size
  Out[18]: 4
  In [19]: d.dtype
  Out[19]: dtype('int32')

通常,数组的元素最初是未知的,但它的大小是已知的。因此,NumPy提供了几个函数来创建具有初始占位符内容的数组。这就减少了数组增长的必要,因为数组增长的操作花费很大。NumPy提供了一个类似range的函数arange,该函数返回数组而不是列表,且支持步长为浮点数。当arange与浮点参数一起使用时,由于有限的浮点精度,通常不可能预测所获得的元素的数量。出于这个原因,通常最好使用linspace函数来接收我们想要的元素数量的函数,而不是步长。

  In [18]: np.arange(5)
  Out[18]: array([0, 1, 2, 3, 4])
  In [19]: np.arange(0, 1, 0.3)
  Out[19]: array([0. , 0.3, 0.6, 0.9])
  In [20]: np.linspace(0, 1, 5)
  Out[20]: array([0.  , 0.25, 0.5 , 0.75, 1.  ])

函数zeros创建一个由0组成的数组;函数 ones创建一个由1组成的数组;函数empty 创建一个空数组,其初始值是随机的,取决于内存的状态;函数full可以创建一个指定内容的数组。默认情况下,创建的数组的dtypefloat64 类型的,但也可以使用dtype参数指定类型。

  In [21]: np.zeros((3, 4))
  Out[21]:
  array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
  In [22]: np.zeros((3, 4), dtype=np.int32)
  Out[22]:
  array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]])
  In [23]: np.empty((2, 4))
  Out[23]:
  array([[0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
        0.00000000e+000],
       [0.00000000e+000, 4.36754031e-321, 8.34446410e-308,
        4.00513295e-307]])
  In [24]: np.ones((2, 3))
  Out[24]:
  array([[1., 1., 1.],
       [1., 1., 1.]])
  In [25]: np.full((2, 4), 5)
  Out[25]:
  array([[5, 5, 5, 5],
       [5, 5, 5, 5]])

2 Numpy数组的索引、切片和迭代

一维的数组可以进行索引、切片和迭代操作的,就像列表和其他Python序列类型一样。

In [1]: import numpy as np
In [2]: a = np.arange(10)
In [3]: a
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [4]: a[0], a[3], a[-1], a[-3]
Out[4]: (0, 3, 9, 7)
In [5]: a[:3]
Out[5]: array([0, 1, 2])
In [6]: a[:-2]
Out[6]: array([0, 1, 2, 3, 4, 5, 6, 7])
In [7]: a[:6:2]
Out[7]: array([0, 2, 4])
In [8]: a[::-1]
Out[8]: array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

多维的数组每个轴可以有一个索引,这些索引以逗号分隔的元组给出。当提供的索引少于轴的数量时,缺失的索引被认为是完整的切片。三个点( ... )表示产生完整索引元组所需的冒号。例如,如果 xndim为5的数组(即,它具有5个轴),则:

  • x[1,2,...] 相当于 x[1,2,:,:,:]
  • x[...,3] 等效于 x[:,:,:,:,3]
  • x[4,...,5,:] 等效于 x[4,:,:,5,:]
In [9]: b = np.array([[0,  1,  2,  3], [10, 11, 12, 13], [20, 21, 22, 23]])
In [10]: b
Out[10]:
array([[ 0,  1,  2,  3],
   [10, 11, 12, 13],
   [20, 21, 22, 23]])
In [11]: b[0, 1], b[1, 3]
Out[11]: (1, 13)
In [12]: b[1, :]
Out[12]: array([10, 11, 12, 13])
In [13]: b[1:, 1:]
Out[13]:
array([[11, 12, 13],
   [21, 22, 23]])
In [14]: b[:-1, :-1]
Out[14]:
array([[ 0,  1,  2],
   [10, 11, 12]])
In [15]: b[-1] # 当提供的索引少于轴的数量时,缺失的索引被认为是完整的切片
Out[15]: array([20, 21, 22, 23])
In [16]: b[-1, ...]
Out[16]: array([20, 21, 22, 23])
In [17]: b[-1] = 5
In [18]: b
Out[18]:
array([[ 0,  1,  2,  3],
   [10, 11, 12, 13],
   [ 5,  5,  5,  5]])

无论是多维数组还是一维数组,迭代方式都与Python的列表相同。

In [19]: for i in a[:3]:
...:     print(i)
...:
0
1
2
In [20]: for row in b:
...:     print(row)
...:
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]

3 NumPy数组的计算:通用函数

NumPy 数组的计算有时非常快,有时也非常慢。使 NumPy 变快的关键是利用向量化操作,通常在 NumPy 的通用函数(ufunc)中实现。本节将介绍 NumPy 通用函数的重要性——它可以提高数组元素的重复计算的效率;然后,将会介绍很多 NumPy 包中常用且有用的数学通用函数。

NumPy 通用函数的使用方式非常自然,因为它用到了 Python 原生的算术运算符,标准的加、减、乘、除都可以使用,下面介绍以下NumPy实现的算术运算符。

扫描二维码关注公众号,回复: 14884559 查看本文章
In [1]: import numpy as np
In [2]: a = np.arange(4)
 ...: print("a =", a)
 ...: print("-a = ", -a)
 ...: print("a + 3 =", a + 5)
 ...: print("a - 3 =", a - 5)
 ...: print("a * 3 =", a * 2)
 ...: print("a / 2 =", a / 2)
 ...: print("a // 2 =", a // 2)
 ...: print("a % 2 = ", a % 2)
 ...: print("a ** 2 = ", a ** 2)
a = [0 1 2 3]
-a =  [ 0 -1 -2 -3]
a + 3 = [5 6 7 8]
a - 3 = [-5 -4 -3 -2]
a * 3 = [0 2 4 6]
a / 2 = [0.  0.5 1.  1.5]
a // 2 = [0 0 1 1]
a % 2 =  [0 1 0 1]
a ** 2 =  [0 1 4 9]
In [3]: (a + 3) * 2 - 6
Out[3]: array([0, 2, 4, 6])

除了算术运算符,Numpy还提供了很多大量好用的通用函数,下面简要介绍一些其中常用的。

In [4]: a = np.array([-2, -1, 0, 1, 2])
In [5]: a
Out[5]: array([-2, -1,  0,  1,  2])
In [6]: abs(a) # 用Python的工厂函数求绝对值
Out[6]: array([2, 1, 0, 1, 2])
In [7]: np.absolute(a) # Numpy的求绝对值的函数,不建议用abs,因为np.absolute更快且功能更强大
Out[7]: array([2, 1, 0, 1, 2])
In [8]: np.abs(a) # absolute函数的别名
Out[8]: array([2, 1, 0, 1, 2])
In [9]: a = np.linspace(0, np.pi, 3) # np.pi相当于数学上的π
In [10]: print("a = ", a) # Numpy支持三角函数运算
  ...: print("sin(a) = ", np.sin(a))
  ...: print("cos(a) = ", np.cos(a))
  ...: print("tan(a) = ", np.tan(a))
a =  [0.         1.57079633 3.14159265]
sin(a) =  [0.0000000e+00 1.0000000e+00 1.2246468e-16]
cos(a) =  [ 1.000000e+00  6.123234e-17 -1.000000e+00]
tan(a) =  [ 0.00000000e+00  1.63312394e+16 -1.22464680e-16]
In [11]: a = np.array([-1, 0, 1])
  ...: print("a = ", a) # 同样也支持反三角函数运算
  ...: print("arcsin(a) = ", np.arcsin(a))
  ...: print("arccos(a) = ", np.arccos(a))
  ...: print("arctan(a) = ", np.arctan(a))
a =  [-1  0  1]
arcsin(a) =  [-1.57079633  0.          1.57079633]
arccos(a) =  [3.14159265 1.57079633 0.        ]
arctan(a) =  [-0.78539816  0.          0.78539816]
In [12]: a = [1, 2, 3]
  ...: print("a =", a) # Numpy支持指数运算
  ...: print("e^a =", np.exp(a))
  ...: print("2^a =", np.exp2(a))
  ...: print("3^a =", np.power(3, a))
a = [1, 2, 3]
e^a = [ 2.71828183  7.3890561  20.08553692]
2^a = [2. 4. 8.]
3^a = [ 3  9 27]
In [13]: a = [1, 2, 4, 8]
  ...: print("a =", a) # Numpy同样也支持指数运算的逆运算,即对数运算
  ...: print("ln(a) =", np.log(a))
  ...: print("log2(a) =", np.log2(a))
  ...: print("log10(a) =", np.log10(a))
a = [1, 2, 4, 8]
ln(a) = [0.         0.69314718 1.38629436 2.07944154]
log2(a) = [0. 1. 2. 3.]
log10(a) = [0.         0.30103    0.60205999 0.90308999]

当面对大量的数据时,第一个步骤通常都是计算相关数据的概括统计值,在Numpy中这被称为聚合。常用的聚合方法除了求最大值max、最小值min、总和sum、平均值mean,还有方差var、标准差std、中位数median等,且都支持通过数组对象直接调用这些方法。

In [1]: import numpy as np
In [2]: a = np.array([-1, 10, 2, 3, 7, 2, 5, -3])
In [3]: np.max(a), np.min(a), np.sum(a), np.mean(a) # 通过Numpy函数调用
Out[3]: (10, -3, 25, 3.125)
In [4]: a.max(), a.min(), a.sum(), a.mean() # 通过数组对象的方法调用
Out[4]: (10, -3, 25, 3.125)

多维数组的聚合方式与一维不同,一种常用的聚合操作是沿着一行或一列聚合。

In [5]: b = np.array([[0,  1,  2,  3], [10, 11, 12, 13], [20, 21, 22, 23]])
In [6]: b.sum()
Out[6]: 138
In [7]: b.sum(axis=0) # 通过指定 axis=0 求每一列的和
Out[7]: array([30, 33, 36, 39])
In [8]: b.sum(axis=1) # 通过指定 axis=1 求每一行的和
Out[8]: array([ 6, 46, 86])

4 Numpy数组的计算:广播

我们在前一节中介绍了 NumPy 如何通过通用函数的向量化操作来减少缓慢的 Python 循环,另外一种向量化操作的方法是利用 NumPy 的广播功能。广播可以简单理解为用于不同大小数组的二进制通用函数(加、减、乘等)的一组规则。

对于同样大小的数组,二进制操作是对相应元素逐个计算。广播允许这些二进制操作可以用于不同大小的数组。例如,可以简单地将一个标量(可以认为是一个零维的数组)和一个数组相加。同样也可以将这个原理扩展到更高维度的数组。

In [1]: import numpy as np
In [2]: a = np.array([0, 1, 2])
   ...: b = np.array([5, 5, 5])
In [3]: a + b
Out[3]: array([5, 6, 7])
In [4]: a - b
Out[4]: array([-5, -4, -3])
In [5]: a * b
Out[5]: array([ 0,  5, 10])
In [6]: a / b
Out[6]: array([0. , 0.2, 0.4])
In [7]: a + 5 # 相当于a + [5, 5, 5],即5被广播了
Out[7]: array([5, 6, 7])
In [8]: a * 3
Out[8]: array([0, 3, 6])
In [9]: b = np.eye(3)
In [10]: b
Out[10]:
array([[1., 0., 0.],
     [0., 1., 0.],
     [0., 0., 1.]])
In [11]: b + a # 这里一维数组a就被扩展或者广播了
Out[11]:
array([[1., 1., 2.],
     [0., 2., 2.],
     [0., 1., 3.]])

5 Numpy数组的形状操纵

一个数组的形状是由每个轴的元素数量决定的,可以使用各种命令更改数组的形状。

In [1]: import numpy as np
In [2]: a = np.arange(12)
In [3]: a.shape
Out[3]: (12,)
In [4]: a
Out[4]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
In [5]: a.reshape(3, 4) # 可将数组修改为特定的形状
Out[5]:
array([[ 0,  1,  2,  3],
     [ 4,  5,  6,  7],
     [ 8,  9, 10, 11]])
In [6]: a.reshape(3, 4).shape
Out[6]: (3, 4)
In [7]: a.reshape(3, 4).T # 返回数组的转置
Out[7]:
array([[ 0,  4,  8],
     [ 1,  5,  9],
     [ 2,  6, 10],
     [ 3,  7, 11]])
In [8]: a.reshape(3, 4).T.shape
Out[8]: (4, 3)
In [9]: a.reshape(3, 4).flatten() # 多维数组转一维数组
Out[9]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
In [10]: a # 以上三个更改数组形状的方法均不改变数组本身
Out[10]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
In [11]: a.resize(3, 4) # resize函数会更改数组本身
In [12]: a 
Out[12]:
array([[ 0,  1,  2,  3],
     [ 4,  5,  6,  7],
     [ 8,  9, 10, 11]])

Numpy还能实现将几个数组沿不同的轴堆叠在一起。

In [1]: import numpy as np
In [2]: a = np.arange(3)
   ...: b = np.array([2, 1, 3])
   ...: c = np.arange(4, 10).reshape(2, 3)
   ...: d = np.arange(1, 7).reshape(2, 3)
In [3]: print("a = \n", a)
   ...: print("b = \n", b)
   ...: print("c = \n", c)
   ...: print("d = \n", d)
a =
 [0 1 2]
b =
 [2 1 3]
c =
 [[4 5 6]
 [7 8 9]]
d =
 [[1 2 3]
 [4 5 6]]
In [4]: np.vstack((a, b))
Out[4]:
array([[0, 1, 2],
     [2, 1, 3]])
In [5]: np.hstack((a, b))
Out[5]: array([0, 1, 2, 2, 1, 3])
In [6]: np.vstack((a, c))
Out[6]:
array([[0, 1, 2],
     [4, 5, 6],
     [7, 8, 9]])
In [7]: np.vstack((c, d))
Out[7]:
array([[4, 5, 6],
     [7, 8, 9],
     [1, 2, 3],
     [4, 5, 6]])
In [8]: np.hstack((c, d))
Out[8]:
array([[4, 5, 6, 1, 2, 3],
     [7, 8, 9, 4, 5, 6]])
In [9]: np.concatenate((a, b), axis=0) # concatenate函数要求输入的数组必需具有相同地形状
Out[9]: array([0, 1, 2, 2, 1, 3])
In [10]: np.concatenate((c, d), axis=0)
Out[10]:
array([[4, 5, 6],
     [7, 8, 9],
     [1, 2, 3],
     [4, 5, 6]])
In [11]: np.concatenate((c, d), axis=1)
Out[11]:
array([[4, 5, 6, 1, 2, 3],
     [7, 8, 9, 4, 5, 6]])

Numpy还支持将一个数组拆分成几个较小的数组。

In [1]: import numpy as np
In [2]: a = np.arange(3*4).reshape(3, 4)
In [3]: a
Out[3]:
array([[ 0,  1,  2,  3],
     [ 4,  5,  6,  7],
     [ 8,  9, 10, 11]])
In [4]: np.hsplit(a, 4)
Out[4]:
[array([[0],
      [4],
      [8]]),
 array([[1],
      [5],
      [9]]),
 array([[ 2],
      [ 6],
      [10]]),
 array([[ 3],
      [ 7],
      [11]])]
In [5]: np.vsplit(a, 3)
Out[5]: [array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8,  9, 10, 11]])]
In [6]: np.split(a, 3, axis=0)
Out[6]: [array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]]), array([[ 8,  9, 10, 11]])]
In [7]: np.split(a, 4, axis=1)
Out[7]:
[array([[0],
      [4],
      [8]]),
 array([[1],
      [5],
      [9]]),
 array([[ 2],
      [ 6],
      [10]]),
 array([[ 3],
      [ 7],
      [11]])]

此外,Numpy还支持对数组进行维度的转换。

In [1]: import numpy as np
In [2]: a = np.arange(3*4*5).reshape(3, 4, 5)
In [3]: a.shape
Out[3]: (3, 4, 5)
In [4]: a = np.transpose(a, axes=[1, 2, 0]) # 将原本的第1、2维转为第0、1维,第1维转为第2维
In [5]: a.shape
Out[5]: (4, 5, 3)

6 Numpy数组的比较、掩码和布尔逻辑

前面介绍了通用函数,并且特别关注了算术运算符。除用+-*/和其他一些运算符实现了数组的逐元素操作外,NumPy 还实现了如 <(小于)和 >(大于)的逐元素比较的通用函数。这些比较运算的结果是一个布尔数据类型的数组,一共有 6 种标准的比较操作,如下面代码所示。

In [1]: import numpy as np
In [2]: a = np.arange(5)
In [3]: a
Out[3]: array([0, 1, 2, 3, 4])
In [4]: a > 2
Out[4]: array([False, False, False,  True,  True])
In [5]: a < 3
Out[5]: array([ True,  True,  True, False, False])
In [6]: a >= 2
Out[6]: array([False, False,  True,  True,  True])
In [7]: a <= 3
Out[7]: array([ True,  True,  True,  True, False])
In [8]: a == 3
Out[8]: array([False, False, False,  True, False])
In [9]: a != 3
Out[9]: array([ True,  True,  True, False,  True])

此外,利用复合表达式实现对两个数组的逐元素比较也是可行的。和算术运算通用函数一样,这些比较运算通用函数也可以用于任意形状、大小的数组。如果需要统计布尔数组中True或False记录的个数,可以使用np.count_nonzero函数实现。对于多维数组,这种操作还支持沿指定轴进行统计。

In [10]: b = np.array([4, 0, 5, 2, 1])
In [11]: a > b
Out[11]: array([False,  True, False,  True,  True])
In [12]: c = np.arange(2*3).reshape(2, 3)
In [13]: c
Out[13]:
array([[0, 1, 2],
     [3, 4, 5]])
In [14]: c >= 3
Out[14]:
array([[False, False, False],
     [ True,  True,  True]])
In [15]: np.count_nonzero(c > 3)
Out[15]: 2
In [16]: c.size - np.count_nonzero(c > 3)
Out[16]: 4
In [17]: np.count_nonzero(c > 3, axis=0)
Out[17]: array([0, 1, 1], dtype=int64)
In [18]: np.count_nonzero(c > 3, axis=1)
Out[18]: array([0, 2], dtype=int64)

同标准的算术运算符一样,NumPy用通用函数重载了Python的逐位逻辑运算符(bitwise logic operator)&、|、^ 和 ~ ,这样可以实现数组的逐位运算(通常是布尔运算)。

In [19]: (c > 2) & (c < 5) # 且,相当于Python的and
Out[19]:
array([[False, False, False],
     [ True,  True, False]])
In [20]: (c < 2) | (c > 4) # 或,相当于Python的or
Out[20]:
array([[ True,  True, False],
     [False, False,  True]])
In [21]: ~(c > 3) # 非,相当于Python的not
Out[21]:
array([[ True,  True,  True],
     [ True, False, False]])
In [22]: (c < 2) ^ (c > 3) # 异或,不同为真,相同为假
Out[22]:
array([[ True,  True, False],
     [False,  True,  True]])

在前面的小节中,我们看到了如何直接对布尔数组进行聚合计算。一种更强大的模式是使用布尔数组作为掩码,通过该掩码选择数据的子数据集。

In [24]: c[c > 3]
Out[24]: array([4, 5])
In [25]: c[c < 2]
Out[25]: array([0, 1])
In [26]: c[(c >1) & (c < 4)]
Out[26]: array([2, 3])

7 Numpy中的一些特殊值

为了满足不同情况下进行数据分析的需要,Numpy定义了一些特殊值,下面让我们看一下都有哪些特殊值和他们的具体用法:

  • np.pi:一个浮点数,相当于数学上的π。
  • np.inf:一个特殊的浮点类型,整表示正无穷大,同样地,-np.inf表示负无穷大。
  • np.nan:也是一个特殊的浮点数,语义却是“not a number”,即不是一个数,是唯一一个自己不等于自己的类型,通常用来表示数据中的缺失值。
In [1]: import numpy as np
In [2]: np.pi
Out[2]: 3.141592653589793
In [3]: radius = 2
In [4]: area = np.pi * radius ** 2
In [5]: area
Out[5]: 12.566370614359172
In [6]: np.cos(np.pi)
Out[6]: -1.0
In [7]: np.inf
Out[7]: inf
In [8]: np.log(0) # 运算过程中会除0,因此会输出警告
<ipython-input-8-f6e7c0610b57>:1: RuntimeWarning: divide by zero encountered in log
np.log(0)
Out[8]: -inf
In [9]: np.array([-1, 1])/0
<ipython-input-9-ab9c802df68b>:1: RuntimeWarning: divide by zero encountered in true_divide
np.array([-1, 1])/0
Out[9]: array([-inf, inf])
In [10]: a = np.array([2, np.inf, 0, -np.inf])
In [11]: np.isinf(a)
Out[11]: array([False,  True, False,  True])
In [12]: np.isposinf(a)
Out[12]: array([False,  True, False, False])
In [13]: np.isneginf(a)
Out[13]: array([False, False, False,  True])
In [14]: np.nan
Out[14]: nan
In [15]: np.array([0, 1, 2])/0 # 注意,0/0得nan
<ipython-input-15-e410e7e24e5f>:1: RuntimeWarning: divide by zero encountered in true_divide
np.array([0, 1, 2])/0
<ipython-input-15-e410e7e24e5f>:1: RuntimeWarning: invalid value encountered in true_divide
np.array([0, 1, 2])/0
Out[15]: array([nan, inf, inf])
In [16]: a = np.array([0, 1, 7, np.nan, 2, -1, np.nan])
In [17]: np.nan == np.nan # nan自己不等于自己
Out[17]: False
In [18]: np.isnan(a) # 判断Numpy数组中的nan只能用np.isnan
Out[18]: array([False, False, False,  True, False, False,  True])
In [19]: np.max(a), np.min(a), np.sum(a), np.mean(a) # 聚合方法中nan值默认参与运算
Out[19]: (nan, nan, nan, nan)
In [20]: np.nanmax(a), np.nanmin(a), np.nansum(a), np.nanmean(a) # nan安全函数
Out[20]: (7.0, -1.0, 9.0, 1.8)
In [21]: a + 1 # nan不参与算术运算
Out[21]: array([ 1.,  2.,  8., nan,  3.,  0., nan])
In [22]: a * 5
Out[22]: array([ 0.,  5., 35., nan, 10., -5., nan])
In [23]: a / 6
Out[23]:
array([ 0.        ,  0.16666667,  1.16666667,         nan,  0.33333333,
   -0.16666667,         nan])

8 Numpy掩码数组

在许多情况下,数据集可能不完整或因无效数据的存在而受到污染。 例如,传感器可能无法记录数据或记录无效值。 numpy.ma 模块通过引入掩码数组提供了一种解决此问题的便捷方法。

掩码数组是标准 numpy.ndarray 和掩码的组合。 掩码是一个布尔数组,用于确定关联数组的每个元素的值是否有效。 当掩码的元素为 False 时,关联数组的相应元素是有效的,并且被称为未掩码。 当掩码的元素为 True 时,关联数组的相应元素称为掩码(无效)。

创建掩码数组:

In [1]: import numpy as np
In [2]: import numpy.ma as ma
In [3]: x = np.array([1, 2, 3, -1, 5])
In [4]: mx = ma.masked_array(x, mask=[0, 0, 0, 1, 0])
In [5]: mx
Out[5]:
masked_array(data=[1, 2, 3, --, 5],
             mask=[False, False, False,  True, False],
       fill_value=999999)
In [6]: mx = ma.masked_array(x, mask=x>2)
In [7]: mx
Out[7]:
masked_array(data=[1, 2, --, -1, --],
             mask=[False, False,  True, False,  True],
       fill_value=999999)

MaskedArray作为numpy.ndarray的子类,继承了numpy.ndarray索引、切片、广播、形状操纵的机制。但在通用函数和比较操作时有所不同,MaskedArray会忽略掩码,因此使用掩码数组时无需考虑无效值。

In [8]: mx.mean()
Out[8]: 0.6666666666666666
In [9]: mx > 1
Out[9]:
masked_array(data=[False, True, --, False, --],
             mask=[False, False,  True, False,  True],
       fill_value=999999)

可以通过多种方式访问​​掩码数组的基础数据,一般使用其data属性或getdata方法。如果某些条目被标记为无效,则这些方法都不是完全令人满意的。作为一般规则,在需要不带任何掩码条目的数组表示的情况下,建议使用该filled方法填充数组。掩码数组的掩码可通过其mask属性访问。我们必须记住,掩码中的True条目表示无效数据,False表示有效数据。某些情况下,如果掩码数组没有无效条目,mask属性会输出一个布尔值False,而不是一个全为False的数组。

In [10]: mx.data
Out[10]: array([ 1,  2,  3, -1,  5])
In [11]: mx.filled(0)
Out[11]: array([ 1,  2,  0, -1,  0])
In [12]: mx.mask
Out[12]: array([False, False,  True, False,  True])
In [13]: mx[~mx.mask] # 仅访问有效元素
Out[13]:
masked_array(data=[1, 2, -1],
             mask=[False, False, False],
       fill_value=999999)

将掩码数组的一个或多个特定条目标记为无效的推荐方法是masked为它们分配特殊值;第二种可能性是对mask属性直接修改,但不鼓励这种用法。要取消掩码一个或多个特定条目,我们可以为它们分配一个或多个新的有效值。要取消掩码掩码数组的所有掩码条目(假设掩码不是硬掩码),最简单的解决方案是将nomask赋给掩码。

In [14]: mx[0] = ma.masked
In [15]: mx
Out[15]:
masked_array(data=[--, 2, --, -1, --],
             mask=[ True, False,  True, False,  True],
       fill_value=999999)
In [16]: mx[0] = 100
In [17]: mx
Out[17]:
masked_array(data=[100, 2, --, -1, --],
             mask=[False, False,  True, False,  True],
       fill_value=999999)
In [18]: mx.mask = ma.nomask
In [19]: mx
Out[19]:
masked_array(data=[100, 2, 3, -1, 5],
             mask=[False, False, False, False, False],
       fill_value=999999)

猜你喜欢

转载自blog.csdn.net/weixin_44785184/article/details/128746400
今日推荐