NumPy 快速入门#
先决条件#
您需要了解一点 Python。如需复习,请参阅Python 教程。
matplotlib
要运行这些示例,除了 NumPy 之外,您还需要安装。
学习者简介
这是 NumPy 中数组的快速概述。它演示了如何n维(\(n>=2\)) 数组被表示并且可以被操作。特别是,如果您不知道如何将常用函数应用于 n 维数组(不使用 for 循环),或者如果您想了解 n 维数组的轴和形状属性,那么本文可能会有所帮助。
学习目标
阅读后,您应该能够:
了解 NumPy 中一维、二维和 n 维数组的区别;
了解如何在不使用 for 循环的情况下将一些线性代数运算应用于 n 维数组;
了解 n 维数组的轴和形状属性。
基础#
NumPy 的主要对象是齐次多维数组。它是一个元素表(通常是数字),所有元素都具有相同的类型,由非负整数元组索引。在 NumPy 中,维度称为轴。
例如,3D 空间中一点的坐标数组 ,
有一个轴。该轴有 3 个元素,因此我们说它的长度为 3。在下图的示例中,该数组有 2 个轴。第一个轴的长度为 2,第二个轴的长度为 3。[1, 2, 1]
[[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
。- ndarray 大小
数组元素的总数。这等于 的元素的乘积
shape
。- ndarray.dtype
描述数组中元素类型的对象。可以使用标准 Python 类型创建或指定数据类型。此外,NumPy 还提供了自己的类型。 numpy.int32、numpy.int16 和 numpy.float64 是一些示例。
- ndarray.itemsize
数组中每个元素的大小(以字节为单位)。例如,一个 type 元素数组
float64
有itemsize
8 个(=64/8),而一个 type 元素complex32
有itemsize
4 个(=32/8)。它相当于ndarray.dtype.itemsize
.- ndarray.数据
包含数组实际元素的缓冲区。通常,我们不需要使用此属性,因为我们将使用索引设施访问数组中的元素。
一个例子#
>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'>
数组创建#
创建数组的方法有多种。
例如,您可以使用该array
函数从常规 Python 列表或元组创建数组。结果数组的类型是根据序列中元素的类型推断出来的。
>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
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
创建一个全是 0 的数组,该函数
ones
创建一个全是 1 的数组,该函数empty
创建一个初始内容是随机的且取决于内存状态的数组。默认情况下,创建的数组的数据类型是
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) # 9 numbers from 0 to 2
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.random.Generator.rand ,
numpy.random.Generator.randn ,
fromfunction
,
fromfile
打印数组#
当您打印数组时,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]]]
请参阅下文以获取有关 的更多详细信息reshape
。
如果数组太大而无法打印,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]])
某些操作(例如+=
和*=
)会就地修改现有数组,而不是创建新数组。
>>> rg = np.random.default_rng(1) # create instance of default random number generator
>>> a = np.ones((2, 3), dtype=int)
>>> 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)
>>> b = np.linspace(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 # may vary
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
也可以看看
ndarrays 上的索引、
索引例程(
参考)
newaxis
、、、
ndenumerate
indices
形状操纵#
改变数组的形状#
数组的形状由沿每个轴的元素数量给出:
>>> a = np.floor(10 * rg.random((3, 4)))
>>> 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]
ravel
ravel
reshape
该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.]])
也可以看看
将不同的数组堆叠在一起#
多个数组可以沿着不同的轴堆叠在一起:
>>> 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
仅对于二维数组:
>>> 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]
a
b
del a
函数和方法概述#
以下是一些有用的 NumPy 函数和方法名称的列表,按类别排序。请参阅例程以获取完整列表。
- 数组创建
arange
,,,,,,,,,,,,,,,,,,,,,,,,,,array
copy
empty
empty_like
eye
fromfile
fromfunction
identity
linspace
logspace
mgrid
ogrid
ones
ones_like
r_
zeros
zeros_like
- 转换
- 操纵
array_split
,,,,,,,,,,,,,,,,,,,,,,,,,,,,column_stack
concatenate
diagonal
dsplit
dstack
hsplit
hstack
ndarray.item
newaxis
ravel
repeat
reshape
resize
squeeze
swapaxes
take
transpose
vsplit
vstack
- 问题
- 订购
argmax
,,,,,,,,,,argmin
argsort
max
min
ptp
searchsorted
sort
- 运营
choose
,,,,,,,,,,,,,,,,compress
cumprod
cumsum
inner
ndarray.fill
imag
prod
put
putmask
real
sum
- 基础统计
- 基础线性代数
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`
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 中,与 — 完全相同,因此我们可以将and放入 a 中,然后用它进行索引。arr[i, j]
arr[(i, j)]
i
j
tuple
>>> 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 += 1
a = 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.clf()
>>> 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])
请注意,一维布尔数组的长度必须与要切片的维度(或轴)的长度一致。在前面的示例中,长度为 3(中的行b1
数),并且
(长度为 4)适合索引 的第二个轴(列)
。a
b2
a
ix_() 函数#
该ix_
函数可用于组合不同的向量,以获得每个 n-uplet 的结果。例如,如果要计算从向量 a、b 和 c 中取出的所有三元组的所有 a+b*c:
>>> a = np.array([2, 3, 4, 5])
>>> b = np.array([8, 5, 4])
>>> c = np.array([5, 4, 6, 8, 3])
>>> ax, bx, cx = np.ix_(a, b, c)
>>> ax
array([[[2]],
[[3]],
[[4]],
[[5]]])
>>> bx
array([[[8],
[5],
[4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax + bx * cx
>>> result
array([[[42, 34, 50, 66, 26],
[27, 22, 32, 42, 17],
[22, 18, 26, 34, 14]],
[[43, 35, 51, 67, 27],
[28, 23, 33, 43, 18],
[23, 19, 27, 35, 15]],
[[44, 36, 52, 68, 28],
[29, 24, 34, 44, 19],
[24, 20, 28, 36, 16]],
[[45, 37, 53, 69, 29],
[30, 25, 35, 45, 20],
[25, 21, 29, 37, 17]]])
>>> result[3, 2, 4]
17
>>> a[3] + b[2] * c[4]
17
您还可以按如下方式实现减少:
>>> def ufunc_reduce(ufct, *vectors):
... vs = np.ix_(*vectors)
... r = ufct.identity
... for v in vs:
... r = ufct(r, v)
... return r
然后将其用作:
>>> ufunc_reduce(np.add, a, b, c)
array([[[15, 14, 16, 18, 13],
[12, 11, 13, 15, 10],
[11, 10, 12, 14, 9]],
[[16, 15, 17, 19, 14],
[13, 12, 14, 16, 11],
[12, 11, 13, 15, 10]],
[[17, 16, 18, 20, 15],
[14, 13, 15, 17, 12],
[13, 12, 14, 16, 11]],
[[18, 17, 19, 21, 16],
[15, 14, 16, 18, 13],
[14, 13, 15, 17, 12]]])
与普通的 ufunc.reduce 相比,此版本的 reduce 的优点是它利用 广播规则 ,以避免创建输出大小乘以向量数量的参数数组。
用字符串索引#
请参阅结构化数组。
技巧和技巧#
在这里,我们列出了一些简短而有用的提示。
“自动”重塑#
要更改数组的维度,您可以省略其中一个大小,然后将自动推导该大小:
>>> a = np.arange(30)
>>> b = a.reshape((2, -1, 3)) # -1 means "whatever is needed"
>>> b.shape
(2, 5, 3)
>>> b
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14]],
[[15, 16, 17],
[18, 19, 20],
[21, 22, 23],
[24, 25, 26],
[27, 28, 29]]])
矢量堆叠#
我们如何从大小相等的行向量列表构造一个二维数组?在 MATLAB 中,这非常简单:如果x
和y
是两个长度相同的向量,则只需执行m=[x;y]
。在 NumPy 中,这通过函数column_stack
、dstack
和hstack
来实现vstack
,具体取决于要完成堆叠的维度。例如:
>>> x = np.arange(0, 10, 2)
>>> y = np.arange(5)
>>> m = np.vstack([x, y])
>>> m
array([[0, 2, 4, 6, 8],
[0, 1, 2, 3, 4]])
>>> xy = np.hstack([x, y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4])
这些二维以上函数背后的逻辑可能很奇怪。
也可以看看
直方图#
应用于数组的NumPyhistogram
函数返回一对向量:数组的直方图和 bin 边缘的向量。注意:
matplotlib
还有一个构建直方图的函数(hist
在 Matlab 中称为 ),与 NumPy 中的函数不同。主要区别在于pylab.hist
自动绘制直方图,而
numpy.histogram
仅生成数据。
>>> import numpy as np
>>> rg = np.random.default_rng(1)
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
>>> mu, sigma = 2, 0.5
>>> v = rg.normal(mu, sigma, 10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=True) # matplotlib version (plot)
(array...)
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, density=True) # NumPy version (no plot)
>>> plt.plot(.5 * (bins[1:] + bins[:-1]), n)
对于 Matplotlib >=3.4,您还可以使用.plt.stairs(n, bins)