超详细的图解 Numpy,不收藏后悔!_numpy、-程序员宅基地

技术标签: python  机器学习  人工智能  numpy  

Numpy是关于机器学习,数据处理的Python基础库,了解 NumPy 的工作原理可以提高你的编码效率,可以让你的代码看上去更加简洁和优雅。 NumPy 也可以GPU上运行代码而无需更改原始代码(或只做少量更改)。

NumPy 的核心概念是 多维数组。它的美妙之处在于,无论数组有多少维,大多数操作看起来都是一样的。但是 1D 和 2D 的情况有点特殊。文章由三部分组成:

  1. 向量,一维数组
  2. 矩阵,二维数组
  3. 3D及以上

Numpy 数组与 Python 列表(list)

粗看上去,NumPy 数组类似于 Python 列表。它们都用作容器,具有快速的原始获取和设置以及元素的插入和删除速度稍慢。

先看一个最简单的例子,Numpy能打败Python的list主要体现在算术计算上:

 除此之外,NumPy 数组的优缺点主要体现在:

  • Numpy更紧凑,尤其是当有多个维度时。
  • Numpy的向量化操作List表更快。
  • Numpy将元素附加到末尾时比List慢。
  • Numpy和List共同特点是:只能快速处理一种类型的元素。

 这里的大O表示运算的时间复杂度, O(N)表示运算的时间与数组的大小成正比。O*(1)表运算示时间一般不依赖于数组的大小。

1.向量,一维数组

向量初始化

创建 NumPy 数组的一种方法是通过将python的list 转换成numpy的数组:

NumPy 数组不能像 Python list 那样很方便的进行扩展(append):因为它在数组末尾没有保留空间以方便快速扩展。因此,通常的做法是先扩展 Python 列表然后将其转换为 NumPy 数组,或者使用 np.zerosnp.empty  预先分配必要的空间。

通常我们需要创建一个空数组来匹配现有数组的形状和元素类型 :

实际上,所有创建一个填充常量值的数组的函数都有_like对应的函数:

 NumPy 中创建序列数组初始化的函数有两个:

如果您需要一个外观相似的浮点数数组,例如[0., 1., 2.],您可以更改arange输出的类型:arange(3).astype(float),但还有更好的方法,由于arange函数是类型敏感的:如果您输入整数作为参数,它将生成整数,如果您输入浮点数(例如arange(3.)),它将生成浮点数。 

但arange不是特别擅长处理浮点数:

 np.arange(start,stop,step)通常情况下比较擅长处理整数,也可以处理浮动数,但是处理浮动数时可读性较差,因为当参数start,stop,step的小数位数不一致时,生产的结果有时难以理解(比如上图中的solution1的结果),因为浮点数没有连续的概念。 当需要生成浮点数序列数组时更为有效的方法是使用np.linspace(start,stop,num)方法,其中num必须是整数,np.linspace生成的浮点数序列并不是连续的,它只是在start,stop之间均匀的生成num个浮点数(包含start,stop在内)。即x∈[start,stop]。但是有一些numpy生产的浮动数序列并不是闭区间的,比如用numpy生成随机数序列:

在Numpy中还有一个用于生成随机数组的新接口。它的特点是:

  • 更适合多线程
  • 速度更快
  • 更可配置(您可以通过选择非默认的所谓“位生成器”来获得更快的速度或更高的质量) 

向量索引

当将数据放入数组后,NumPy 非常擅长提供简单的方法来返回部分或全部的结果:

 上面介绍的多种通过多种花哨的索引来裁剪数组的方法,但是它们的结果实际上都是所谓的“视图(view)”:如果数据在被索引后发生变化,它们不会存储数据并反映原始数组中的变(赋值操作除外)。下面是对list和numpy数组进行切片和赋值后的结果差异的对比:

 从 NumPy 数组获取数据的另一种非常有用的方法是布尔索引,它允许使用各种逻辑运算符:

 numpy数组通过使用布尔索引的方式也可以对原数组进行赋值。为此衍生出来的两个常用的方法:np.where和np.clip

 请注意,np.where使用一个参数返回一个数组元组(1D 情况下为 1 元组,2D 情况下为 2 元组等),因此您需要在上面的示例中写入np.where(a>5)[0]这种方式来返回你想要的数组。

向量运算

向量化运算的速度是 Numpy最值得夸耀的地方。向量化运算达到了 c++ 运算的级别,使我们能够避免缓慢的 Python 循环的成本。NumPy 允许像普通数字一样操作整个数组:

 和 Python 中一样,a//b 表示 整除(得到商),x**n 表示 xⁿ

当一个数组与一个数做加减乘除时,表示数组的每个元素都与这个数做相关的运算。(也称为广播):

大多数数学函数都有NumPy函数与之对应:

 在numpy中向量的点积与叉积有自己运算符:

 在numpy中也有三角函数运算符:

 在numpy中还有多种不同的取整函数:

 这里np.floor是向下取整,np.ceil是向上取整,而np.round 并非是“四舍五入”,而是“去奇存偶”,或者说 “4舍6入5凑偶”,与一般理解的四舍五入不同:当整数部分是偶数,小数部分是0.5时,向下取整,最后结果为偶数。当整数部分是奇数,小数部分是0.5时,则向上取整,最后结果为偶数。这样得到的结果在统计学上更精确。numpy的round函数采用的是这种方法。

numpy还有一些基本的统计函数:

这些函数中的每一个都有一个 nan-resistant 变体:例如nansumnanmax

从上面的公式可以看出,在总体均值未知时从样本估计 std 的最典型用例中,std和都var忽略贝塞尔校正并给出有偏差的结果。获得较少偏差估计的标准方法是n-1在分母中使用ddof=1('delta degrees of freedom'):

Pandas std默认使用贝塞尔校正 

随着样本量的增加,贝塞尔校正的效果会迅速减弱。此外,它不是一个标准的通用解决方案,例如正态分布ddof=1.5 更好: 

 排序函数的功能少于 Python  对应函数:

 在一维情况下,reversed可以通过反转结果轻松补偿关键字的缺失。在 2D 中,它有点困难

在向量中搜索元素

与Python的List不同,NumPy 数组没有index方法。 所以查找元素是Numpy的一个痛点:

  • 一种查找元素的方法是np.where(a==x)[0][0],但它效率不高,因为它需要遍历数组的所有元素,即使要查找的元素在最开始位置。
  • 另一种更快的方法是使用 Numba 加速 next((i[0] for i, v in np.ndenumerate(a) if v==x), -1) (否则在最坏的情况下比 where 还要慢).
  • 一旦对数组进行排序,情况就会好转:v = np.searchsorted(a, x); return v if a[v]==x else -1 确实很快,复杂度为 O(log N),但首先需要 O(N log N) 时间进行排序。

浮点运算比较 

函数np.allclose(a, b)用于比较具有给定公差的浮点数组:

  • np.allclose假设所有的比较数字的等级是1个单位。例如在上图中,它就认为1e-9和2e-9相同,如果要进行更细致的比较,需要通过atol指定比较等级1:np.allclose(1e-9, 2e-9, atol=1e-17) == False。
  • math.isclose进行比较没有假设前提,而是基于用户给出的一个合理abs_tol值:math.isclose(0.1+0.2–0.3, abs_tol=1e-8) == True。

除此之外np.allclose在绝对和相对公差公式中还存在一些小问题,例如,对某些数存在allclose(a, b) != allclose(b, a)。这些问题已在math.isclose函数中得到解决。

2.矩阵,二维数组

NumPy 中曾经有一个专门的matrix类,但现在已弃用,因此我将交替使用矩阵和二维数组这两个词。

矩阵初始化语法类似于向量:

这里需要双括号,因为第二个位置参数是为(可选)dtype(也接受整数)保留的。

随机矩阵生成也类似于向量:

 还有一种更简洁的随机数生成方式:

 二维索引语法比嵌套列表更方便:

 和一维数组一样,上图的view表示,切片数组实际上并未进行任何复制。修改数组后,更改也将反映在切片中。

axis参数 

在许多操作(例如求和)中,我们需要告诉NumPy是否要跨行或跨列进行操作。为了使用任意维数的通用表示法,NumPy引入了axis的概念:axis参数实际上是所讨论索引的数量:第一个索引是axis=0,第二个索引是axis=1,等等。

因此在二维数组中,如果axis=0是按列,那么axis=1就是按行。

矩阵运算

除了按元素工作的普通运算符(如 +、-、*、/、// 和 **)之外,还有一个计算矩阵乘积的 @ 运算符: 

 

 在第一部分中,我们已经看到向量乘积的运算,NumPy允许向量和矩阵之间,甚至两个向量之间进行元素的混合运算:

请注意,在最后一个示例中,它是一个对称的每元素乘法(笛卡尔积)。要使用非对称线性代数矩阵乘法计算外积,应颠倒操作数的顺序:

从上面的示例可以看出,在二维数组中,行向量和列向量被不同地对待。

默认情况下,一维数组在二维操作中被视为行向量。因此,将矩阵乘以行向量时,可以使用(n,)或(1,n),结果将相同。

如果需要列向量,则有转置方法对其进行操作:

 能够从一维数组中生成二位数组列向量的两个操作是使用命令reshape重排和newaxis建立新索引:

这里的-1参数表示reshape自动计算第二个维度上的数组长度,None在方括号中充当np.newaxis的快捷方式,该快捷方式在指定位置添加了一个空axis。

因此,NumPy中总共有三种类型的向量:一维数组,二维行向量和二维列向量。这是两者之间显式转换的示意图:

flatten始终是一个副本,reshape (-1) 始终是一个视图,ravel尽可能是一个视图 

根据规则,一维数组被隐式解释为二维行向量,因此通常不必在这两个数组之间进行转换,相应区域用灰色标出。 

矩阵操作

连接矩阵有两个主要函数:

这两个函数只堆叠矩阵或只堆叠向量时,都可以正常工作。但是当涉及一维数组与矩阵之间的混合堆叠时,vstack可以正常工作:hstack会出现尺寸不匹配错误。

因为如上所述,一维数组被解释为行向量,而不是列向量。解决方法是将其转换为列向量,或者使用column_stack自动执行:

 堆叠的逆向操作是拆分:

 所有split风格都接受要拆分的索引列表作为参数,或单个整数作为参数,即大小相等的部分的数量:

 

 矩阵可以通过两种方式完成复制:tile类似于复制粘贴,repeat类似于分页打印。

特定的列和行可以用delete进行删除:

逆运算是插入(insert)

 append就像hstack一样,该函数无法自动转置一维数组,因此再次需要对向量进行转置或添加长度,或者使用column_stack代替:

 实际上,如果您需要做的只是将常量值添加到数组的边界,那么(稍微有点复杂)pad 函数就足够了:

Meshgrids

广播规会让使用 meshgrids 变得更简单。假设,您需要以下矩阵(但尺寸非常大):

两种明显的方法都很慢,因为它们使用 Python 循环。处理此类问题的MATLAB的方法是创建一个meshgrid:

meshgrid函数接受任意一组索引,该mgrid只是切片并且,indices只能生成完整的索引范围。fromfunction如上所述,使用 I 和 J 参数调用提供的函数一次。

但实际上,在 NumPy 中有更好的方法。没有必要在整个 I 和 J 矩阵上花费内存(即使meshgrid足够聪明,如果可能的话只存储对原始向量的引用)。只存储正确形状的向量就足够了,其余的由广播规则处理:

如果没有indexing=’ij’参数,meshgrid将更改参数的顺序:J, I= np.meshgrid(j, i)—它是一种“xy”模式,可用于可视化 3D 绘图(请参阅文档中的示例)。

除了在二维或三维网格上初始化函数外,网格对于索引数组也很有用:

 

也适用于稀疏网格 

矩阵统计

就像 一样sum函数,所有其他统计函数 (min/maxargmin/argmaxmean/median/percentilestd/var)接受 axis 参数并相应地执行操作:

在二维及更高版本中的argminandargmax函数有返回扁平化索引(最小值和最大值的第一个实例)的麻烦。要将其转换为两个坐标,unravel_index需要一个函数 :

all和any两个函数也能使用axis参数:

矩阵排序

尽管axis参数对上面列出的函数有帮助,但对 二维 排序没有帮助

这通常不是您希望对矩阵或电子表格进行排序的结果:axis它根本不是key参数的替代品。但幸运的是,NumPy 有几个辅助函数,允许按列排序——如果需要,也可以按多列排序: 

1. a[a[:,0].argsort()] 按第一列对数组排序:

这里argsort返回排序后原始数组的索引数组。

可以重复这个技巧,但必须注意下一次排序不会弄乱上一次的结果:
a = a[a[:,2].argsort()]
a = a[a[:,1].argsort(kind='stable')]
a = a[a[:,0].argsort(kind='stable')]

 

 2. 有一个辅助函数lexsort可以按照上面描述的方式对所有可用列进行排序,但它总是按行执行,并且要排序的行的顺序是倒置的(即从下到上)所以它的用法有点麻烦,例如:
a[np.lexsort(np.rot90(a))]按从左到右的顺序按所有列排序。

 –a[np.lexsort(np.rot90(a[:,[2,5]]))]首先按第 2 列排序,然后(第 2 列中的值相等)按第 5 列排序。另一种形式是a[np.lexsort(np.flipud(a.T[[2,5]]))]

 这里 flipud 在上下方向上翻转矩阵(准确地说,在axis=0方向上,与 相同a[::-1,...],其中三个点表示“所有其他维度”——所以突然flipud,而不是fliplr,翻转一维数组)并rot90旋转矩阵在正方向(即逆时针)旋转 90 度。flips 和 rot90 都返回视图。

3. 要使用 的order参数sort,首先将数组转换为结构化形式,然后对其进行排序,最后将其转换回普通(“非结构化”)形式:

 

 这两种转换实际上都是视图,因此它们速度很快并且不需要任何额外的内存。但是功能u2ss2u需要首先导入:

from numpy.lib.recfunctions
import unstructured_to_structured as u2s, structured_to_unstructured as s2u

4. 在 Pandas 中执行此操作可能是更好的选择,因为此特定操作在那里更具可读性且更不容易出错:

  • pd.DataFrame(a).sort_values(by=[2,5]).to_numpy():先按第 2 列排序,然后按第 5 列排序。 
  • pd.DataFrame(a).sort_values().to_numpy():按从左到右的顺序对所有列排序。

3. 3维及更高维

通过重排一维向量或转换嵌套的Python列表来创建3维数组时,索引的含义为(z,y,x)。第一个索引是平面的编号,然后是该平面中的坐标:

但是此索引顺序不是通用的。处理RGB图像时,通常使用(y,x,z)顺序:前两个是像素坐标,最后一个是颜色坐标(Matplotlib中是RGB ,OpenCV中是BGR ):

RGB图像 

 这样,可以方便地引用特定像素:a[i,j]给出像素的RGB元组(i,j)。

因此,创建特定几何形状的实际命令取决于您正在处理的领域的约定:

显然,像 hstackvstack、dstack 这样的 NumPy 函数并不知道这些约定。其中硬编码的索引顺序是 (y,x,z),RGB 图像顺序: 

 堆叠RGB图像(这里只有两种颜色)

如果您的数据布局不同,使用concatenate命令堆叠图像,在参数中为其提供显式索引号axis会更方便:

如果您不方便用数轴(axis)来思考问题,可以将数组转换硬编码为hstack的形式:

 这种转换没有发生实际的复制操作。它只是混合索引的顺序。

另一个混合索引顺序的操作是数组转置。检查它可能会让您对 3维数组更加熟悉。根据您决定的轴(axis)顺序,转置数组所有平面的实际命令将有所不同:对于通用数组,它交换索引 1 和 2,对于RGB图像,它交换0和1:

 有趣的是,默认transpose的轴参数axes(以及唯一的a.T操作模式)颠倒了索引顺序,这与上面描述的索引顺序约定都不一致。

广播也适用于更高维度,有关详细信息,请参阅我的简短文章“ NumPy 中的广播”。

最后,这里有一个函数可以在处理多维数组时为您节省大量的Python 循环,并可以使您的代码更简洁 — einsum(爱因斯坦求和):

 它沿着重复索引对数组求和。在这个特定的例子中 np.tensordot(a, b, axis=1),这两种情况都足够了,但在更复杂的情况下 einsum 可能会工作得更快,而且通常更容易编写和阅读——一旦你理解了它背后的逻辑。

如果您想测试您的 NumPy 技能,GitHub 上有一套棘手的众包100 个 NumPy 练习¹⁰。

参考

  1. Scott Sievert,NumPy GPU 加速
  2. Jay Alammar,NumPy 和数据表示的可视化介绍
  3. Big-O备忘单网站
  4. Python 时间复杂度维基页面
  5. NumPy Issue #14989,排序函数中的反向参数
  6. NumPy Issue #2269,第一个非零元素
  7. Numba 图书馆主页
  8. 浮点指南,比较
  9. NumPy Issue #10161,numpy.isclose 与 math.isclose
  10. GitHub 上的 100 个 NumPy 练习

本文来自于Medium的一篇国外博主的博客文章 作者:Lev Maximov

原文地址:https://betterprogramming.pub/numpy-illustrated-the-visual-guide-to-numpy-3b1d4976de1d

\

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42608414/article/details/128774964

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法