什么是NumPy?
NumPy是Python中科学计算的基础软件包。 它是一个提供多了维数组对象,多种派生对象(如:掩码数组、矩阵)以及用于快速操作数组的函数及API, 它包括数学、逻辑、数组形状变换、排序、选择、I/O 、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。
NumPy包的核心是ndarray对象。
它封装了python原生的同数据类型的n维数组,为了保证其性能优良,其中有许多操作都是代码在本地进行编译后执行的。 NumPy数组 和 标准Python Array(数组) 之间有几个重要的区别:
- NumPy数组在创建时具有固定的大小,与Python的原生数组对象(可以动态增长)不同。 更改ndarray的大小将创建一个新数组并删除原来的数组。
- NumPy数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。 例外情况:Python的原生数组里包含了NumPy的对象的时候,这种情况下就允许不同大小元素的数组。
- NumPy数组有助于对大量数据进行高级数学和其他类型的操作。 通常,这些操作的执行效率更高,比使用Python原生数组的代码更少。
- 越来越多的基于Python的科学和数学软件包使用NumPy数组; 虽然这些工具通常都支持Python的原生数组作为参数,但它们在处理之前会还是会将输入的数组转换为NumPy的数组,而且也通常输出为NumPy数组。 换句话说,为了高效地使用当今科学/数学基于Python的工具(大部分的科学计算工具),你只知道如何使用Python的原生数组类型是不够的 - 还需要知道如何使用NumPy数组。
关于数组大小和速度的要点在科学计算中尤为重要。举一个简单的例子,考虑将1维数组中的每个元素与相同长度的另一个序列中的相应元素相乘的情况。如果数据存储在两个Python列表a和b中,我们可以迭代每个元素,如下所示:
确实符合我们的要求,但如果a和b每个包含数百万个数字,我们将为Python中循环的低效率付出代价。我们可以通过使用C语言来编写代码帮助我们更快地完成相同的任务(为了清楚起见,我们忽略了变量声明和初始化,内存分配等)
这节省了解释Python代码和操作Python对象所涉及的所有开销,但牺牲了用Python编写代码所带来的好处。此外,编码工作需要增加的维度,我们的数据。例如,对于二维数组,C代码(如前所述)会扩展为这样:
NumPy为我们提供了两全其美的解决方案:当涉及到ndarray时,逐个元素的操作是“默认模式”,但逐个元素的操作由预编译的C代码快速执行。在NumPy中:
以近C速度执行前面的示例所做的事情,但是我们期望基于Python的代码具有简单性。的确,NumPy的语法更为简单!最后一个例子说明了NumPy的两个特征,它们是NumPy的大部分功能的基础:矢量化和广播。
矢量化描述了代码中没有任何显式的循环、索引等这些事情,当然,只是在优化的、预编译的C代码中“幕后”发生了这些事情。矢量化代码有许多优点,其中包括:
- 矢量化代码更简洁,更易于阅读
- 更少的代码行通常意味着更少的错误
- 代码更接近于标准的数学符号(更通俗易懂、更容易、正确的编码常规数学结构)
- 矢量化导致更多“Pythonic”代码。如果没有矢量化,我们的代码就会被低效且难以阅读的
循环
所困扰。
广播是用来描述操作的隐式逐个元素行为的术语;一般来说,在NumPy中,所有的操作,不仅是算术操作,而且是逻辑的、按位的、功能的等,以这种隐式逐个元素的方式表现,即它们广播。此外,在上面的示例中,a和b可以是相同形状的多维数组,也可以是一个标量和一个数组,甚至是两个不同形状的数组,只要较小的数组“可以”扩展到较大的数组的形状,从而得到的广播是明确的。
NumPy完全支持面向对象的方法,同样从ndarray开始。例如,ndarray是一个类,具有许多方法和属性。它的许多方法在最外层的NumPy命名空间中映射函数,让码农们可以完全自由地按照自己的习惯编写合适的代码。
Numpy 数组
NumPy提供了一个N维数组的类型,即ndarray,它描述了相同类型的“items”的集合。 可以使用例如整数的N来索引项目(items)。
所有的ndarray都是同质的:每个条目占用相同大小的内存块,并且所有块都以完全相同的方式进行解释。如何解释数组中的每个项是由一个单独的数据类型对象指定的,其中一个对象与每个数组相关联。除了基本类型(整数、浮点数等)之外,数据类型对象还可以表示数据结构。
从数组中提取的项(例如,通过索引)由Python对象表示,其类型是在NumPy中构建的阵列标量类型之一。 阵列标量允许容易地操纵更复杂的数据排列。
NumPy的主要对象是同类型的多维数组。它是一张表,所有元素(通常是数字)的类型都相同,并通过正整数元组索引。在NumPy中,维度称为轴。轴的数目为rank。
例如,3D空间中的点的坐标 [1, 2, 1]
是rank为1的数组,因为它具有一个轴。该轴的长度为3。在下面的示例中,该数组有2个轴。
第一个轴(维度)的长度为2,第二个轴(维度)的长度为3。
NumPy的数组类被称为ndarray。别名为 array
。 请注意,numpy.array
与标准Python库类 array.array
不同,后者仅处理一维数组并提供较少的功能。 ndarray
对象则提供更关键的属性:
- ndarray.ndim:数组的轴(维度)的个数。在Python世界中,维度的数量被称为rank。
- ndarray.shape:数组的维度。这是一个整数的元组,表示每个维度中数组的大小。对于有n行和m列的矩阵,shape将是(n,m)。因此,
shape
元组的长度就是rank或维度的个数ndim
。 - ndarray.size:数组元素的总数。这等于shape的元素的乘积。
- ndarray.dtype:一个描述数组中元素类型的对象。可以使用标准的Python类型创建或指定dtype。另外NumPy提供它自己的类型。例如numpy.int32、numpy.int16和numpy.float64。
- ndarray.itemsize:数组中每个元素的字节大小。例如,元素为
float64
类型的数组的itemsize
为8(=64/8),而complex32
类型的数组的itemsize
为4(=32/8)。它等于ndarray.dtype.itemsize
。 - ndarray.data:该缓冲区包含数组的实际元素。通常,我们不需要使用此属性,因为我们将使用索引访问数组中的元素。
一般有6个机制创建数组:
- 从其他Python结构(例如,列表,元组)转换
- numpy原生数组的创建(例如,arange、ones、zeros等)
- 从磁盘读取数组,无论是标准格式还是自定义格式
- 通过使用字符串或缓冲区从原始字节创建数组
- 使用特殊库函数(例如,random)
- 复制、join或以其他方式扩展或改变现有数组的方法。
Numpy 运算
数组和标量的运算:数组里的元素和标量逐一进行运算。
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 + 5
Out[4]: array([5, 6, 7, 8, 9])
In [5]: a * 5
Out[5]: array([ 0, 5, 10, 15, 20])
数组和数组的运算:如果维度相同,数组对应位置进行逐个元素的数学运算。
In [6]: a + a
Out[6]: array([0, 2, 4, 6, 8])
In [7]: a * a
Out[7]: array([ 0, 1, 4, 9, 16])
基本操作
- copyto(dst, src[, casting, where]) 将值从一个数组复制到另一个数组,并根据需要进行广播。
改变数组形状
- reshape(a, newshape[, order]) 为数组提供新形状而不更改其数据。
- ravel(a[, order]) 返回一个连续的扁平数组。
- ndarray.flat 数组上的一维迭代器.
- ndarray.flatten([order]) 返回折叠成一维的数组的副本。
转置式运算
-
moveaxis(a, source, destination) 将数组的轴移动到新位置。
- rollaxis(a, axis[, start]) 向后滚动指定的轴,直到它位于给定位置。
- swapaxes(a, axis1, axis2) 交换数组的两个轴。
- ndarray.T 与self.transpose() 相同,只是如果self.ndim < 2 则返回self。
- transpose(a[, axes]) 置换数组的维度。
更改尺寸数量
- atleast_1d(*arys) 将输入转换为至少具有一个维度的数组。
- atleast_2d(*arys) 将输入视为具有至少两个维度的数组。
- atleast_3d(*arys) 将输入视为具有至少三维的数组。
- broadcast 制作一个模仿广播的对象。
- broadcast_to(array, shape[, subok]) 将数组广播到新形状。
- broadcast_arrays(*args, **kwargs) 相互广播任意数量的数组。
- expand_dims(a, axis) 展开数组的形状。
- squeeze(a[, axis]) 展开数组的形状。
改变阵列的种类
- asarray(a[, dtype, order]) 将输入转换为数组。
- asanyarray(a[, dtype, order]) 将输入转换为ndarray,但通过ndarray子类。
- asmatrix(data[, dtype]) 将输入解释为矩阵。
- asfarray(a[, dtype]) 返回转换为float类型的数组。
- asfortranarray(a[, dtype]) 返回在内存中以Fortran顺序布局的数组。
- ascontiguousarray(a[, dtype]) 在内存中返回一个连续的数组(C顺序)。
- asarray_chkfinite(a[, dtype, order]) 将输入转换为数组,检查NaN或Infs。
- asscalar(a) 将大小为1的数组转换为标量等效数组。
- require(a[, dtype, requirements]) 返回满足要求的提供类型的ndarray。
加入数组
- concatenate((a1, a2, …)[, axis, out]) 沿现有轴加入一系列数组。
- stack(arrays[, axis, out]) 沿新轴加入一系列数组。
- column_stack(tup) 将1-D阵列作为列堆叠成2-D阵列。
- dstack(tup) 按顺序深度堆叠阵列(沿第三轴)。
- hstack(tup) 按顺序堆叠数组(列式)。
- vstack(tup) 垂直堆叠数组(行方式)。
- block(arrays) 从嵌套的块列表中组装nd数组。
拆分数组
- split(ary, indices_or_sections[, axis]) 将数组拆分为多个子数组。
- array_split(ary, indices_or_sections[, axis]) 将数组拆分为多个子数组。
- dsplit(ary, indices_or_sections) 沿第3轴(深度)将数组拆分为多个子数组。
- hsplit(ary, indices_or_sections) 将数组水平拆分为多个子数组(按列)。
- vsplit(ary, indices_or_sections) 将数组垂直拆分为多个子数组(逐行)。
平铺阵列
- tile(A, reps) 通过重复A重复给出的次数来构造数组。
- repeat(a, repeats[, axis]) 重复数组的元素。
增删元素
- delete(arr, obj[, axis]) 返回一个新数组,其子轴数组沿轴被删除。
- insert(arr, obj, values[, axis]) 在给定索引之前沿给定轴插入值。
- append(arr, values[, axis]) 将值附加到数组的末尾。
- resize(a, new_shape) 返回具有指定形状的新数组。
- trim_zeros(filt[, trim]) 从1-D数组或序列中修剪前导和/或尾随零。
- unique(ar[, return_index, return_inverse, …]) 找到数组的唯一元素。
重新排列元素
- flip(m, axis) 沿给定轴反转数组中元素的顺序。
- fliplr(m) 向左/向右翻转阵列。
- flipud(m) 向上/向下翻转阵列。
- reshape(a, newshape[, order]) 为数组提供新形状而不更改其数据。
- roll(a, shift[, axis]) 沿给定轴滚动数组元素。
- rot90(m[, k, axes]) 在轴指定的平面中将数组旋转90度。
Numpy Cheat Sheet
参考: NumPy 中文文档