Python中的numpy模块解析
一、定义
NumPy 是 Python 科学计算的基础包。它是一个 Python 库,提供了一个多维数组对象、各种派生对象(例如掩码数组和矩阵),以及用于对数组进行快速操作的各种例程,包括数学、逻辑、形状操作、排序、选择、I/O 、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。
NumPy 包的核心是ndarray对象。这封装了同构数据类型的n维数组,许多操作在编译代码中执行以提高性能。NumPy 数组和标准 Python 序列之间有几个重要的区别:
• 与 Python 列表(可以动态增长)不同,NumPy 数组在创建时具有固定大小。更改ndarray的大小将创建一个新数组并删除原始数组。
• NumPy 数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。例外:可以有(Python,包括 NumPy)对象的数组,从而允许不同大小元素的数组。
• NumPy 数组有助于对大量数据进行高级数学运算和其他类型的运算。通常,与使用 Python 的内置序列相比,此类操作的执行效率更高,代码更少。
• 越来越多的基于 Python 的科学和数学包正在使用 NumPy 数组;尽管这些通常支持 Python 序列输入,但它们在处理之前将此类输入转换为 NumPy 数组,并且通常输出 NumPy 数组。换句话说,为了有效地使用当今大部分(甚至大部分)基于 Python 的科学/数学软件,仅仅知道如何使用 Python 的内置序列类型是不够的——还需要知道如何使用 NumPy 数组。
二、功能
关于序列大小和速度的要点在科学计算中尤为重要。作为一个简单的例子,考虑将一维序列中的每个元素与另一个相同长度序列中的相应元素相乘的情况。如果数据存储在两个 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 代码的“幕后”发生。矢量化代码有很多优点,其中包括:
-
矢量化代码更简洁易读
-
更少的代码行通常意味着更少的错误
-
代码更接近标准数学符号(通常更容易正确编码数学结构)
-
矢量化会产生更多的“Pythonic”代码。如果没有矢量化,我们的代码将充斥着低效且难以阅读的
for循环。
广播是用于描述操作的隐式逐元素行为的术语;一般而言,在 NumPy 中,所有操作,不仅是算术运算,还有逻辑、按位、函数等,都以这种隐式的逐元素方式表现,即它们进行广播。此外,在上面的例子中,a并且b可以是相同形状的多维数组,或者一个标量和一个数组,甚至两个不同形状的数组,前提是较小的数组可以“扩展”为较大的数组的形状结果广播是明确的。
三、运用
NumPy 的主要对象是同构多维数组。它是一个元素表(通常是数字),所有类型都相同,由非负整数元组索引。在 NumPy 中,维度(dimensions)称为轴。轴的个数称为秩(rank)
例如,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。 ndarray.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 #包含数组实际元素的缓冲区。通常,我们不需要使用此属性,因为我们将使用索引工具访问数组中的元素。
1、简单例子运用
>>> 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'>
2、创建数组对象
#array 将输入数据(列表、元组、数组或者其他序列类型)转换为ndarray。要么推断出dtype,要么显示制定dtype。默认直接复制输入数据。 #asarray 将输入转换为ndarray,如果输入本身就是一个ndarray就不进行复制。 #arrange 类似于内置的range,但返回的是一个ndarray而不是列表。 >>> 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]]] #ones,ones_like 根据制定的形状和dtype创建一个全1数组。ones_like一另一个数组为参数,并根据其形状和dtype创建一个全1数组。 >>> 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) #zeros,zeros_like 类似于ones和ones_like,只不过产生的是全0数组而已。 >>> np.zeros((3, 4)) array([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]) #empty,empty_like 创建新数组,只分配内存空间但不填充任何值。 >>> 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]]) #eye,identity 创建一个正方的N x N 单位矩阵(对角线为1,其余为0)
还有linspace, numpy.random.Generator.rand , numpy.random.Generator.randn , fromfunction, fromfile等等创建数组对象
3、数组的运算
数组上的算术运算符按元素应用。创建一个新数组并填充结果。
>>> 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) # 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]])
4、通用函数
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
5、索引、切片和迭代
一维数组可以被索引、切片和迭代,就像 列表 和其他 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
高级索引和索引技巧
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]]])
In Python, arr[i, j] is exactly the same as arr[(i, j)]—so we can put i and j in a tuple and then do the indexing with that.
>>> 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.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])
请注意,一维布尔数组的长度必须与要切片的维度(或轴)的长度一致。
6、改变数组的形状
数组的形状由沿每个轴的元素数量决定:
>>> 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 样式数组,其中最左边的索引变化最快。
该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
7、将不同的数组堆叠在一起
多个数组可以沿不同的轴堆叠在一起:
>>> 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
在复杂的情况下,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_
8、将一个数组拆分成几个较小的数组
使用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允许指定沿哪个轴拆分。
9、副本和视图
在操作和操作数组时,它们的数据有时会复制到新数组中,有时不会。这通常是初学者困惑的根源。有以下三种情况:
没有复制
简单赋值不会复制对象或其数据。
>>> 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.
If b = a[:100] is used instead, a is referenced by b and will persist in memory even if del a is executed.
10、函数和方法概述
以下是按类别排序的一些有用的 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
四、属性
dtype()是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息。
numpy中的数据类型转换,不能直接改原数据的dtype!只能用函数astype()。
大小相等的数组之间的任何算术运算都会将运算进行到元素级。
同样,数组和标量的算术运算也会将那个标量传播到各个元素。
不同大小的数组之间的运算叫做广播。

#函数 说明 np.arange(n) # 类似range()函数,返回ndarray类型,元素从0到n‐1 np.ones(shape) # 根据shape生成一个全1数组,shape是元组类型 np.zeros(shape) #根据shape生成一个全0数组,shape是元组类型 np.full(shape,val) #根据shape生成一个数组,每个元素值都是val np.eye(n) #创建一个正方的n*n单位矩阵,对角线为1,其余为0 np.ones_like(a) #根据数组a的形状生成一个全1数组 np.zeros_like(a) #根据数组a的形状生成一个全0数组 np.full_like(a,val) #根据数组a的形状生成一个数组,每个元素值都是val np.linspace() #根据起止数据等间距地填充数据,形成数组 np.concatenate() #将两个或多个数组合并成一个新的数组 np.abs(x) np.fabs(x) #计算数组各元素的绝对值 np.sqrt(x) #计算数组各元素的平方根 np.square(x) #计算数组各元素的平方 np.log(x) np.log10(x) np.log2(x) #计算数组各元素的自然对数、10底对数和2底对数 np.ceil(x) np.floor(x) # $计算数组各元素的ceiling值或floor值 np.rint(x) #计算数组各元素的四舍五入值 np.modf(x) #将数组各元素的小数和整数部分以两个独立数组形式返回 np.cos(x) np.cosh(x) np.sin(x) np.sinh(x) np.tan(x) np.tanh(x) #计算数组各元素的普通型和双曲型三角函数 np.exp(x) #计算数组各元素的指数值 np.sign(x) #计算数组各元素的符号值,1(+), 0, ‐1(‐) #方法 说明 .reshape(shape) #不改变数组元素,返回一个shape形状的数组,原数组不变 .resize(shape) #与.reshape()功能一致,但修改原数组 .swapaxes(ax1,ax2) #将数组n个维度中两个维度进行调换 .flatten() #对组进行数降维,返回折叠后的一维数组,原数组不变
浙公网安备 33010602011771号