Numpy快速入门

Numpy快速入门




前言

提示:本内容为官网中文翻译摘录供作者本人学习之用,如能帮助到您我很荣幸:


提示:以下是本篇文章正文内容,下面案例可供参考

一、Numpy是什么?

Numpy是什么?

(1)NumPy 是 Python 科学计算的基础包。它是一个 Python 库,提供了一个多维数组对象、各种派生对象(例如掩码数组和矩阵),以及用于对数组进行快速操作的各种例程,包括数学、逻辑、形状操作、排序、选择、I/O 、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。

(2)NumPy 包的核心是ndarray对象。这封装了同构数据类型的n维数组,许多操作在编译代码中执行以提高性能。NumPy 数组和标准 Python 序列之间有几个重要的区别

1.与 Python 列表(可以动态增长)不同,NumPy 数组在创建时具有固定大小。更改ndarray的大小将创建一个新数组并删除原始数组。

2. NumPy 数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。例外:可以有(Python,包括 NumPy)对象的数组,从而允许不同大小元素的数组。

3.NumPy 数组有助于对大量数据进行高级数学运算和其他类型的运算。通常,与使用 Python 的内置序列相比,此类操作的执行效率更高,代码更少。

4.越来越多的基于 Python 的科学和数学包正在使用 NumPy 数组;尽管这些通常支持 Python 序列输入,但它们在处理之前将此类输入转换为 NumPy 数组,并且通常输出 NumPy 数组。换句话说,为了有效地使用当今大部分(甚至大部分)基于 Python 的科学/数学软件,仅仅知道如何使用 Python 的内置序列类型是不够的——还需要知道如何使用 NumPy 数组。

(3)关于序列大小和速度的要点在科学计算中尤为重要:

作为一个简单的例子,考虑将一维序列中的每个元素与另一个相同长度序列中的相应元素相乘的情况。
如果数据存储在两个 Python 列表a和 中b,我们可以遍历每个元素:

c = []
for i in range(len(a)):
    c.append(a[i]*b[i])

这产生了正确的答案,但如果a和b每一个都包含数以百万计的数字,我们会付出代价Python中循环的效率低下。我们可以在 C 中通过编写更快地完成相同的任务(为清楚起见,我们忽略了变量声明和初始化、内存分配等)

for (i = 0; i < rows; i++): {
  c[i] = a[i]*b[i];
}

这节省了解释 Python 代码和操作 Python 对象所涉及的所有开销,但代价是从 Python 编码中获得的好处。此外,所需的编码工作随着数据维度的增加而增加。例如,在二维数组的情况下,C 代码(如前删节)扩展为

for (i = 0; i < rows; i++): {
  for (j = 0; j < columns; j++): {
    c[i][j] = a[i][j]*b[i][j];
  }
}

NumPy 为我们提供了两全其美的优势:当涉及ndarray时,逐元素操作是“默认模式” ,但逐元素操作由预编译的 C 代码快速执行。在 NumPy 中

c = a * b

以接近 C 的速度执行前面的示例所做的事情,但是我们期望基于 Python 的东西具有我们期望的代码简单性。事实上,NumPy 习惯用法更简单!最后一个示例说明了 NumPy 的两个功能,它们是其强大功能的基础:矢量化和广播

为什么Numpy快?

矢量化代码描述中没有任何显式循环、索引等 - 当然,这些事情只是在优化的预编译 C 代码中“在幕后”发生。矢量化代码有很多优点,其中包括:

1.矢量化代码更简洁易读

2.更少的代码行通常意味着更少的错误

代码更接近标准数学符号(通常更容易正确编码数学结构)

3.矢量化会产生更多的“Pythonic”(Pythonic 就是很 Python 的 Python 代码)代码。如果没有矢量化,我们的代码将充斥着低效且难以阅读的for循环。

广播是用于描述操作的隐式逐元素行为的术语;一般而言,在 NumPy 中,所有操作,不仅是算术运算,还有逻辑、按位、函数等,都以这种隐式的逐元素方式表现,即它们进行广播。此外,在上面的例子中,a和b可以是相同形状的多维数组,或者一个标量和一个数组,甚至两个不同形状的数组,前提是较小的数组可以“扩展”为较大的数组的形状结果广播是明确的。有关广播的详细“规则”,请参阅 basics.broadcasting

还有谁在使用Numpy

NumPy 完全支持面向对象的方法,再次从ndarray 开始。例如,ndarray是一个类,拥有许多方法和属性。它的许多方法由最外层 NumPy 命名空间中的函数镜像,允许程序员以他们喜欢的任何范式进行编码。这种灵活性使 NumPy 数组方言和 NumPy ndarray类成为Python 中使用的多维数据交换的事实上的语言。

二、Numpy快速入门

先决条件:

您需要了解一点 Python。如需复习,请参阅Python 教程

要运行这些示例,除了 NumPy 之外,您还需要安装matplotlib(matplotlib是Python语言及其数值计算库NumPy的绘图库)。

学习简介

这是 NumPy 中数组的快速概览。它演示了 n 维 () 数组被表示并且可以被操作。特别是,如果您不知道如何将常用函数应用于 n 维数组(不使用 for 循环),或者如果您想了解 n 维数组的轴和形状属性,这篇文章可能会有所帮助。

学习目标

阅读后,您应该能够:

理解NumPy中一维、二维和n维数组的区别;

了解如何在不使用 for 循环的情况下将一些线性代数运算应用于 n 维数组;

了解 n 维数组的轴和形状属性。

基础知识

NumPy 的主要对象是同构多维数组。它是一个元素表(通常是数字),所有类型都相同,由非负整数元组索引。在 NumPy 中,维度称为轴。

例如,3D 空间中一个点的坐标[1, 2, 1]只有一个轴。该轴有 3 个元素,因此我们说它的长度为 3。在下图中的示例中,数组有 2 个轴。第一个轴的长度为 2,第二个轴的长度为 3。

[[1., 0., 0.],
 [0., 1., 2.]]

NumPy 的数组类称为ndarray。它也被称为别名 array。请注意,numpy.array这与标准 Python 库类不同array.array,后者仅处理一维数组并提供较少的功能。ndarray对象更重要的属性是:

ndarray.ndim
数组的轴数(维度)。

ndarray.shape
数组的维度。这是一个整数元组,指示每个维度中数组的大小。对于具有n行和m列的矩阵,shape将为(n,m)。shape因此元组的长度 是轴的数量,ndim。

数组大小size
数组的元素总数。这等于shape中元素的乘积。

ndarray.dtype
描述数组中元素类型的对象。可以使用标准 Python 类型创建或指定 dtype。此外,NumPy 提供了自己的类型。numpy.int32、numpy.int16 和 numpy.float64 是一些示例。

ndarray.itemsize
数组每个元素的大小(以字节为单位)。例如,一个类型元素的数组float64有itemsize8 个(=64/8),而一个类型的元素complex32有itemsize4 个(=32/8)。它相当于ndarray.dtype.itemsize。

ndarray.data
包含数组实际元素的缓冲区。通常,我们不需要使用此属性,因为我们将使用索引工具访问数组中的元素。

一个例子

>>>import numpy as np				     //将numpy模块导入为np
>>>a = np.arange(15).reshape(3, 5)		 //创建numpy数组对象a
>>>a									 //输出ndarray
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>>a.shape								//输出ndarray的维度
(3, 5)
>>>a.ndim								//输出ndarray的轴数
2
>>>a.dtype.name							//输出ndarray中元素的类型名称
'int64'
>>>a.itemsize							//输出ndarray中每个元素的大小(以字节为单位)
8
>>>a.size								//输出ndarray的元素总数
15
>>>type(a)								//输出数组对象a的类型
<class 'numpy.ndarray'>
>>>b = np.array([6, 7, 8])				//创建numpy数组对象b
>>>b									//输出ndarray
array([6, 7, 8])
>>>type(b)								//输出数组对象b的类型
<class 'numpy.ndarray'>

如何创建数组

有几种方法可以创建数组。

例如,您可以使用该array函数从常规 Python 列表或元组创建一个数组。结果数组的类型是从序列中元素的类型推导出来的

>>>import numpy as np
>>>a = np.array([2, 3, 4])
>>>a
array([2, 3, 4])
>>>a.dtype									//输出ndarray中元素的类型名称
dtype('int64')
>>>b = np.array([1.2, 3.5, 5.1])
>>>b.dtype
dtype('float64')

一个常见的错误在于array使用多个参数调用,而不是提供单个序列作为参数

>>>a = np.array(1, 2, 3, 4)   				 # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>>a = np.array([1, 2, 3, 4])  				# RIGHT

array 将序列序列转换为二维数组,将序列序列序列转换为三维数组,依此类推。

>>>b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>>b
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

数组的类型也可以在创建时显式指定:

>>>c = np.array([[1, 2], [3, 4]], dtype=complex)
>>>c
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])

通常,数组的元素最初是未知的,但其大小是已知的。因此,NumPy 提供了几个函数来创建具有初始占位符内容的数组。这些最大限度地减少了增长阵列的必要性,这是一项昂贵的操作。

函数zeros创建一个全零数组,该函数 ones创建一个全1数组,该函数empty 创建一个初始内容随机且取决于内存状态的数组。默认情况下,创建的数组的 dtype 是 float64,但可以通过关键字参数 指定dtype。

>>>np.zeros((3, 4))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
np.ones((2, 3, 4), dtype=np.int16)
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int16)
np.empty((2, 3))
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260],  # may vary
       [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])

为了创建数字序列,NumPy 提供了arange类似于 Python 内置的函数range,但返回一个数组。

>>>np.arange(10, 30, 5)
array([10, 15, 20, 25])	 
>>>np.arange(0, 2, 0.3)  # it accepts float arguments
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

当arange与浮点参数一起使用时,由于浮点精度有限,通常无法预测获得的元素数量。出于这个原因,通常最好使用linspace接收我们想要的元素数量作为参数的函数。

>>>from numpy import pi
>>>np.linspace(0, 2, 9)                   //获得从029个数
array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])
>>>x = np.linspace(0, 2 * pi, 100)        # useful to evaluate function at lots of points
f = np.sin(x)

相关函数另见

array
zeros
zeros_like
ones
ones_like
empty
empty_like
arange
linspace

打印数组

打印数组时,NumPy 以类似于嵌套列表的方式显示它,但具有以下布局:

最后一个轴从左到右打印,

倒数第二个是从上到下打印的,

其余的也从上到下打印,每个切片与下一个切片用空行分隔。

然后将一维数组打印为行,将二维打印为矩阵,将三维打印为矩阵列表

>>>a = np.arange(6)                    # 1d array
>>>>print(a)
[0 1 2 3 4 5]
>>>b = np.arange(12).reshape(4, 3)     # 2d array
>>>print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>c = np.arange(24).reshape(2, 3, 4)  # 3d array
>>>print(c)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

如果数组太大而无法打印,NumPy 会自动跳过数组的中心部分,只打印角落

>>>print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>print(np.arange(10000).reshape(100, 100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]

要禁用此行为并强制 NumPy 打印整个数组,您可以使用set_printoptions.

np.set_printoptions(threshold=sys.maxsize)  # sys module should be imported

基本操作

数组上的算术运算符按元素应用。创建一个新数组并填充结果。

>>>a = np.array([20, 30, 40, 50])
>>>b = np.arange(4)
>>>b
array([0, 1, 2, 3])
>>>c = a - b
>>>c
array([20, 29, 38, 47])
>>>b**2
array([0, 1, 4, 9])
>>>10 * np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>>a < 35
array([ True,  True, False, False])

与许多矩阵语言不同,乘积运算符*在 NumPy 数组中按元素进行运算。可以使用@运算符(在python>=3.5中)或dot函数或方法来执行矩阵乘积:

>>>A = np.array([[1, 1],
              [0, 1]])
>>>B = np.array([[2, 0],
              [3, 4]])
>>>A * B     # elementwise product
array([[2, 0],
       [0, 4]])
>>>A @ B     # matrix product
array([[5, 4],
       [3, 4]])
>>>A.dot(B)  # another matrix product
array([[5, 4],
       [3, 4]])

某些操作,例如+=and *=,会修改现有数组而不是创建新数组。

>>>rg = np.random.default_rng(1)  //创建默认随机数生成器
>>>a = np.ones((2, 3), dtype=int)	//创建int数据类型的23列内容元素都为1的二维数组
>>>b = rg.random((2, 3))
>>>a *= 3
>>>a
array([[3, 3, 3],
       [3, 3, 3]])
>>>b += a
>>>b
array([[3.51182162, 3.9504637 , 3.14415961],
       [3.94864945, 3.31183145, 3.42332645]])
>>>a += b  # b is not automatically converted to integer type
//操作对象元素类型不同

Traceback (most recent call last):
    ...
numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'

当处理不同类型的数组时,结果数组的类型对应于更一般或更精确的类型(一种称为向上转换的行为)。

>>>a = np.ones(3, dtype=np.int32)	//创建数据类型为numpy.int32的元素个数为3的内容都为11维数组
>>>b = np.linspace(0, pi, 3)			//获得从0到PI的3个数
>>>b.dtype.name
'float64'
>>>c = a + b
>>>c
array([1.        , 2.57079633, 4.14159265])
c.dtype.name
'float64'
>>>d = np.exp(c * 1j)
>>>d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
       -0.54030231-0.84147098j])
>>>d.dtype.name
'complex128'

许多一元运算,例如计算数组中所有元素的总和,都是作为ndarray类的方法实现的。

>>>a = rg.random((2, 3))
>>>a
array([[0.82770259, 0.40919914, 0.54959369],
       [0.02755911, 0.75351311, 0.53814331]])
>>>a.sum()
3.1057109529998157
>>>a.min()
0.027559113243068367
>>>a.max()
0.8277025938204418

默认情况下,这些操作适用于数组,就好像它是一个数字列表,无论其形状如何。但是,通过指定axis 参数,您可以沿数组的指定轴应用操作:

>>>b = np.arange(12).reshape(3, 4)
>>>b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>b.sum(axis=0)     # sum of each column
array([12, 15, 18, 21])
>>>b.min(axis=1)     # min of each row
array([0, 4, 8])
>>>b.cumsum(axis=1)  # cumulative sum along each row
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]])

通用函数

NumPy 提供了熟悉的数学函数,例如 sin、cos 和 exp。在 NumPy 中,这些被称为“通用函数”(ufunc)。在 NumPy 中,这些函数对数组进行元素操作,生成一个数组作为输出。

>>>B = np.arange(3)
>>>B
array([0, 1, 2])
>>>np.exp(B)
array([1.        , 2.71828183, 7.3890561 ])
>>>np.sqrt(B)
>>>array([0.        , 1.        , 1.41421356])
>>>C = np.array([2., -1., 4.])
>>>np.add(B, C)
array([2., 0., 6.])

另:all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, invert, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace, transpose, var, vdot, vectorize, where

索引、切片和迭代

一维数组可以被索引、切片和迭代,就像 列表 和其他 Python 序列一样。

>>>a = np.arange(10)**3
>>>a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>>a[2]
8
>>>a[2:5]
array([ 8, 27, 64])
# equivalent to a[0:6:2] = 1000;
# from start to position 6, exclusive, set every 2nd element to 1000
>>>a[:6:2] = 1000
>>>a
array([1000,    1, 1000,   27, 1000,  125,  216,  343,  512,  729])
>>>a[::-1]  # reversed a
array([ 729,  512,  343,  216,  125, 1000,   27, 1000,    1, 1000])
>>>for i in a:
...    print(i**(1 / 3.))
...
9.999999999999998
1.0
9.999999999999998
3.0
9.999999999999998
4.999999999999999
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998

多维数组的每个轴可以有一个索引。这些索引在用逗号分隔的元组中给出:

>>>def f(x, y):
...    return 10 * x + y
...
>>>b = np.fromfunction(f, (5, 4), dtype=int)
>>>b
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33],
       [40, 41, 42, 43]])
>>>b[2, 3]
23
>>>b[0:5, 1]  # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>>b[:, 1]    # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>>b[1:3, :]  # each column in the second and third row of b
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

当提供的索引少于轴数时,缺失的索引被视为完整切片:

>>>b[-1]   # the last row. Equivalent to b[-1, :]
array([40, 41, 42, 43])

括号中的表达式b[i]被视为i 后跟:代表其余轴所需的尽可能多的实例。NumPy 还允许您使用点作为 .b[i, …]

的点(…根据需要,以产生一个完整的索引元组)表示为许多冒号。例如,如果x是一个有 5 个轴的数组,则

x[1, 2, …]相当于,x[1, 2, :, :, :]

x[…, 3]到和x[:, :, :, :, 3]

x[4, …, 5, :]到。x[4, :, :, 5, :]

>>>c = np.array([[[  0,  1,  2],  # a 3D array (two stacked 2D arrays)
               [ 10, 12, 13]],
              [[100, 101, 102],
               [110, 112, 113]]])
>>>c.shape
(2, 2, 3)
>>>c[1, ...]  # same as c[1, :, :] or c[1]
array([[100, 101, 102],
       [110, 112, 113]])
>>>c[..., 2]  # same as c[:, :, 2]
array([[  2,  13],
       [102, 113]])

迭代多维数组是相对于第一个轴完成的:

>>>for row in b:
...    print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

但是,如果要对数组中的每个元素执行操作,可以使用flat属性,它是 数组所有元素的 迭代器:

>>>for element in b.flat:
...    print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43

索引参考:
newaxis
ndenumerate
indices

形状操作

改变数组的形状

数组的形状由沿每个轴的元素数量决定:

>>>a = np.floor(10 * rg.random((3, 4)))//创建与10相乘的34列取整的随机数组
>>>a
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])
>>>a.shape
(3, 4)

可以使用各种命令更改数组的形状。注意下面三个命令都返回一个修改过的数组,但不改变原来的数组:

>>>a.ravel()  # returns the array, flattened
array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
>>>a.reshape(6, 2)  # returns the array with a modified shape
array([[3., 7.],
       [3., 4.],
       [1., 4.],
       [2., 2.],
       [7., 2.],
       [4., 9.]])
>>>a.T  # returns the array, transposed(转置)
array([[3., 1., 7.],
       [7., 4., 2.],
       [3., 2., 4.],
       [4., 2., 9.]])
>>>a.T.shape
(4, 3)
>>>a.shape
(3, 4)

产生的数组中元素的顺序ravel通常是“C风格”,即最右边的索引“变化最快”,所以后面的元素是。如果数组被重新整形为其他形状,则数组再次被视为“C 风格”。NumPy 通常创建按此顺序存储的数组,因此通常不需要复制其参数,但如果该数组是通过获取另一个数组的切片或使用不寻常的选项创建的,则可能需要复制它。也可以使用可选参数指示函数和使用 FORTRAN 样式数组,其中最左边的索引变化最快。a[0, 0]a[0, 1]ravelravelreshape
该reshape函数以修改后的形状返回其参数,而该 ndarray.resize方法修改数组本身:

>>>a
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])
>>>a.resize((2, 6))
>>>a
array([[3., 7., 3., 4., 1., 4.],
       [2., 2., 7., 2., 4., 9.]])

如果-1在重塑操作中给出了一个维度,则会自动计算其他维度:

>>>a.reshape(3, -1)
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])

其他函数:
ndarray.shape, reshape, resize, ravel
该处使用的url网络请求的数据。

将不同的数组堆叠到一起

多个数组可以沿不同的轴堆叠在一起:

>>>a = np.floor(10 * rg.random((2, 2)))
>>>a
array([[9., 7.],
       [5., 2.]])
>>>b = np.floor(10 * rg.random((2, 2)))
>>>b
array([[1., 9.],
       [5., 1.]])
>>>np.vstack((a, b))
array([[9., 7.],
       [5., 2.],
       [1., 9.],
       [5., 1.]])
>>>np.hstack((a, b))
array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])

函数column_stack将一维数组作为列堆叠到二维数组中。它等效于hstack仅用于 2D 数组:

from numpy import newaxis
>>>np.column_stack((a, b))  # with 2D arrays
array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])
>>>a = np.array([4., 2.])
>>>b = np.array([3., 8.])
>>>np.column_stack((a, b))  # returns a 2D array
array([[4., 3.],
       [2., 8.]])
>>>np.hstack((a, b))        # the result is different
array([4., 2., 3., 8.])
>>>a[:, newaxis]  # view `a` as a 2D column vector
array([[4.],
       [2.]])
>>>np.column_stack((a[:, newaxis], b[:, newaxis]))
array([[4., 3.],
       [2., 8.]])
>>>np.hstack((a[:, newaxis], b[:, newaxis]))  # the result is the same
array([[4., 3.],
       [2., 8.]])

另一方面,该函数row_stack等效vstack 于任何输入数组。实际上,row_stack是vstack的别名:

>>>np.column_stack is np.hstack
False
>>>np.row_stack is np.vstack
True

通常,对于具有多于两个维度的数组, hstack沿着它们的第二个轴vstack堆叠,沿着它们的第一个轴堆叠,并concatenate 允许可选参数给出应该发生串联的轴的编号。

笔记

在复杂的情况下,r_并且c_是用于通过沿一个轴堆叠号码创建阵列有用的。它们允许使用范围文字:。

>>>np.r_[1:4, 0, 4]
array([1, 2, 3, 0, 4])

当与数组作为参数使用的, r_和c_是类似于 vstack和 hstack在它们的默认行为,但允许一个可选参数给轴沿其来连接的数量。


以上函数具体用法: hstack vstack column_stack concatenate c_ r_ ### 将一个数组拆分成几个较小的数组 使用hsplit,您可以通过指定要返回的形状相同的数组的数量,或指定应在其后进行除法的列,沿其水平轴拆分数组:
>>>a = np.floor(10 * rg.random((2, 12)))
>>>a
array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
       [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])
# Split `a` into 3
>>>np.hsplit(a, 3)
[array([[6., 7., 6., 9.],
       [8., 5., 5., 7.]]), array([[0., 5., 4., 0.],
       [1., 8., 6., 7.]]), array([[6., 8., 5., 2.],
       [1., 8., 1., 0.]])]
# Split `a` after the third and the fourth column
>>>np.hsplit(a, (3, 4))
[array([[6., 7., 6.],
       [8., 5., 5.]]), array([[9.],
       [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.],
       [1., 8., 6., 7., 1., 8., 1., 0.]])]

vsplit沿垂直轴拆分,并array_split允许指定沿哪个轴拆分。

副本和视图

在操作和操作数组时,它们的数据有时会复制到新数组中,有时不会。这通常是初学者困惑的根源。有以下三种情况:

没有复制

简单赋值不会复制对象或其数据

>>>a = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])
>>>b = a            # no new object is created
>>>b is a           # a and b are two names for the same ndarray object
True

Python 将可变对象作为引用传递,因此函数调用不会进行复制。

>>>def f(x):
...    print(id(x))
...
>>>id(a)  # id is a unique identifier of an object
148293216  # may vary
>>>f(a)
148293216  # may vary

视图或浅拷贝

不同的数组对象可以共享相同的数据。该view方法创建一个查看相同数据的新数组对象。

>>>c = a.view()
>>>c is a
False
>>>c.base is a            # c is a view of the data owned by a
True
>>>c.flags.owndata
False
>>>
>>>c = c.reshape((2, 6))  # a's shape doesn't change
>>>a.shape
(3, 4)
>>>c[0, 4] = 1234         # a's data changes
>>>+a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])

切片数组返回它的视图

>>>s = a[:, 1:3]
>>>s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>>a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

深拷贝

该copy方法制作数组及其数据的完整副本。

>>>d = a.copy()  # a new array object with new data is created
>>>d is a
False
>>>d.base is a  # d doesn't share anything with a
False
>>>d[0, 0] = 9999
>>>a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

copy如果不再需要原始数组,有时应该在切片后调用。例如,假设a是一个巨大的中间结果,而最终结果b只包含 的一小部分a,则在b使用切片构造时应进行深拷贝:

>>>a = np.arange(int(1e8))
>>>b = a[:100].copy()
>>>del a  # the memory of ``a`` can be released.

如果改为使用,则被引用并且即使被执行也会保留在内存中。b = a[:100]abdel a

函数和方法概述

以下是按类别排序的一些有用的 NumPy 函数和方法名称的列表。有关完整列表,请参阅例程。

数组创建
arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r_, zeros, zeros_like

转化次数
ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

操作
array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack

问题
all, any, nonzero, where

订购
argmax, argmin, argsort, max, min, ptp, searchsorted, sort

操作
choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum

基本统计
cov, mean, std, var

基本线性代数
cross, dot, outer, linalg.svd, vdot

不太基础

广播规则

广播允许通用函数以有意义的方式处理不具有完全相同形状的输入。

广播的第一条规则是,如果所有输入数组的维数不相同,则会在较小数组的形状前重复添加“1”,直到所有数组的维数相同。

广播的第二条规则确保沿特定维度大小为 1 的数组的行为就像它们具有沿该维度具有最大形状的数组的大小一样。假设数组元素的值沿“广播”数组的维度相同。

应用广播规则后,所有数组的大小必须匹配。更多细节可以在广播中找到。

高级索引和索引技巧

NumPy 提供了比常规 Python 序列更多的索引工具。除了通过整数和切片索引,正如我们之前看到的,数组可以通过整数数组和布尔数组索引。

使用索引数组进行索引

>>>a = np.arange(12)**2  # the first 12 square numbers
>>>i = np.array([1, 1, 3, 8, 5])  # an array of indices
>>>a[i]  # the elements of `a` at the positions `i`//i的元素在a处(i内元素和a被定义一样被平方)
array([ 1,  1,  9, 64, 25])
>>>j = np.array([[3, 4], [9, 7]])  # a bidimensional array of indices
>>>a[j]  # the same shape as `j`
array([[ 9, 16],
       [81, 49]])

当索引数组a是多维时,单个索引数组指的是a 的第一维。以下示例通过使用调色板将标签图像转换为彩色图像来展示此行为。

>>>palette = np.array([[0, 0, 0],         # black
                    [255, 0, 0],       # red
                    [0, 255, 0],       # green
                    [0, 0, 255],       # blue
                    [255, 255, 255]])  # white
>>>image = np.array([[0, 1, 2, 0],  # each value corresponds to a color in the palette
                  [0, 3, 4, 0]])
>>>palette[image]  # the (2, 4, 3) color image
array([[[  0,   0,   0],
        [255,   0,   0],
        [  0, 255,   0],
        [  0,   0,   0]],

       [[  0,   0,   0],
        [  0,   0, 255],
        [255, 255, 255],
        [  0,   0,   0]]])

我们还可以为多个维度提供索引。每个维度的索引数组必须具有相同的形状。

>>>a = np.arange(12).reshape(3, 4)
>>>a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>i = np.array([[0, 1],  # indices for the first dim of `a`
              [1, 2]])
>>>j = np.array([[2, 1],  # indices for the second dim
              [3, 3]])

>>>a[i, j]  # i and j must have equal shape
array([[ 2,  5],
       [ 7, 11]])
>>>
a[i, 2]
array([[ 2,  6],
       [ 6, 10]])
>>>
a[:, j]
array([[[ 2,  1],
        [ 3,  3]],

       [[ 6,  5],
        [ 7,  7]],

       [[10,  9],
        [11, 11]]])

在 Python 中,与 - 完全相同,因此我们可以将和放入 a 中,然后使用它进行索引。arr[i, j]arr[(i, j)]ijtuple

>>>l = (i, j)
# equivalent to a[i, j](相当于)
>>>a[l]
array([[ 2,  5],
       [ 7, 11]])

但是,我们不能通过将i和j放入数组来做到这一点,因为该数组将被解释为索引a 的第一维。

>>>s = np.array([i, j])
# not what we want
>>>a[s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 3
# same as `a[i, j]`
>>>a[tuple(s)]
array([[ 2,  5],
       [ 7, 11]])

数组索引的另一个常见用途是搜索时间相关序列的最大值:

time = np.linspace(20, 145, 5)  # time scale
data = np.sin(np.arange(20)).reshape(5, 4)  # 4 time-dependent series
time
array([ 20.  ,  51.25,  82.5 , 113.75, 145.  ])
data
array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],
       [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
       [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
       [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
       [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
# index of the maxima for each series
ind = data.argmax(axis=0)
ind
array([2, 0, 3, 1])
# times corresponding to the maxima
time_max = time[ind]
>>>data_max = data[ind, range(data.shape[1])]  # => data[ind[0], 0], data[ind[1], 1]...
time_max
array([ 82.5 ,  20.  , 113.75,  51.25])
data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
np.all(data_max == data.max(axis=0))
True

您还可以将索引与数组一起用作分配给的目标:

>>>a = np.arange(5)
>>>a
array([0, 1, 2, 3, 4])
>>>a[[1, 3, 4]] = 0
>>>a
array([0, 0, 2, 0, 0])

然而,当索引列表包含重复时,赋值会进行多次,留下最后一个值:

>>>a = np.arange(5)
>>>a[[0, 0, 2]] = [1, 2, 3]
>>>a
array([2, 1, 3, 3, 4])

这是合理的,但是如果您想使用 Python 的 +=构造,请注意,因为它可能不会达到您的预期:

>>>a = np.arange(5)
>>>a[[0, 0, 2]] += 1
>>>a
array([1, 1, 3, 3, 4])

即使 0 在索引列表中出现两次,第 0 个元素也只增加一次。这是因为 Python 需要等效于.a += 1a = a + 1

使用布尔数组索引

当我们使用(整数)索引数组索引数组时,我们提供了要选择的索引列表。对于布尔索引,方法是不同的;我们明确地选择数组中的哪些项目是我们想要的,哪些是我们不需要的。

对于布尔索引,人们可以想到的最自然的方法是使用与原始数组具有相同形状的布尔数组:

>>>a = np.arange(12).reshape(3, 4)
>>>b = a > 4
b  # `b` is a boolean with `a`'s shape
array([[False, False, False, False],
       [False,  True,  True,  True],
       [ True,  True,  True,  True]])
>>>a[b]  # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11])

这个属性在赋值中非常有用:

>>>a[b] = 0  # All elements of `a` higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
       [4, 0, 0, 0],
       [0, 0, 0, 0]])

您可以查看以下示例以了解如何使用布尔索引生成Mandelbrot 集的图像:

import numpy as np
import matplotlib.pyplot as plt
def mandelbrot(h, w, maxit=20, r=2):
    """Returns an image of the Mandelbrot fractal of size (h,w)."""
    x = np.linspace(-2.5, 1.5, 4*h+1)
    y = np.linspace(-1.5, 1.5, 3*w+1)
    A, B = np.meshgrid(x, y)
    C = A + B*1j
    z = np.zeros_like(C)
    divtime = maxit + np.zeros(z.shape, dtype=int)

    for i in range(maxit):
        z = z**2 + C
        diverge = abs(z) > r                    # who is diverging
        div_now = diverge & (divtime == maxit)  # who is diverging now
        divtime[div_now] = i                    # note when
        z[diverge] = r                          # avoid diverging too much

    return divtime
plt.imshow(mandelbrot(400, 400))

在这里插入图片描述
使用布尔值进行索引的第二种方式更类似于整数索引;对于数组的每个维度,我们给出一个一维布尔数组,选择我们想要的切片:

>>>a = np.arange(12).reshape(3, 4)
>>>b1 = np.array([False, True, True])         # first dim selection
>>>b2 = np.array([True, False, True, False])  # second dim selection
>>>a[b1, :]                                   # selecting rows
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>a[b1]                                      # same thing
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>a[:, b2]                                   # selecting columns
array([[ 0,  2],
       [ 4,  6],
       [ 8, 10]])
>>>a[b1, b2]                                  # a weird thing to do
array([ 4, 10])

请注意,一维布尔数组的长度必须与要切片的维度(或轴)的长度一致。在前面的例子中,b1具有长度为3(的数目的行中a),和 b2(长度4)适合于索引的第二轴线(列) a。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值