首先,Numpy的核心是ndarray。

然后,ndarray本质是数组,其不同于一般的数组,或者Python 的list的地方在于它可以有N 维(dimentions),也可简单理解为数组里面嵌套数组。

最后,Numpy为ndarray提供了便利的操作函数,而且性能优越,完爆Python 的list,因此在数值计算,机器学习,人工智能,神经网络等领域广泛应用。

Numpy几乎是Python 生态系统的数值计算的基石,例如Scipy,Pandas,Scikit-learn,Keras等出色的包都基于Numpy。

本文的主要目的在于理解numpy.ndarray的内存结构及其背后的设计哲学。

ndarray是什么

NumPy provides an N-dimensional array type, the ndarray, which describes a collection of “items” of the same type. The items can be indexed using for example N integers.

ndarray是numpy中的多维数组,数组中的元素具有相同的类型,且可以被索引。

如下所示:

>>> import numpy as np

>>> a = np.array([[0,1,2,3],[4,5,6,7],[8,9,10,11]])

>>> a

array([[ 0, 1, 2, 3],

[ 4, 5, 6, 7],

[ 8, 9, 10, 11]])

>>> type(a)

>>> a.dtype

dtype('int32')

>>> a[1,2]

6

>>> a[:,1:3]

array([[ 1, 2],

[ 5, 6],

[ 9, 10]])

>>> a.ndim

2

>>> a.shape

(3, 4)

>>> a.strides

(16, 4)

注:np.array并不是类,而是用于创建np.ndarray对象的其中一个函数,numpy中多维数组的类为np.ndarray。

ndarray的设计哲学

ndarray的设计哲学在于数据存储与其解释方式的分离,或者说copy和view的分离,让尽可能多的操作发生在解释方式上(view上),而尽量少地操作实际存储数据的内存区域。

如下所示,像reshape操作返回的新对象b,a和b的shape不同,但是两者共享同一个数据block,c=b.T,c是b的转置,但两者仍共享同一个数据block,数据并没有发生变化,发生变化的只是数据的解释方式。

>>> a

array([[ 0, 1, 2, 3],

[ 4, 5, 6, 7],

[ 8, 9, 10, 11]])

>>> b = a.reshape(4, 3)

>>> b

array([[ 0, 1, 2],

[ 3, 4, 5],

[ 6, 7, 8],

[ 9, 10, 11]])

# reshape操作产生的是view视图,只是对数据的解释方式发生变化,数据物理地址相同

>>> a.ctypes.data

80831392

>>> b.ctypes.data

80831392

>>> id(a) == id(b)

false

# 数据在内存中连续存储

>>> from ctypes import string_at

>>> string_at(b.ctypes.data, b.nbytes).hex()

'000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b000000'

# b的转置c,c仍共享相同的数据block,只改变了数据的解释方式,“以列优先的方式解释行优先的存储”

>>> c = b.T

>>> c

array([[ 0, 3, 6, 9],

[ 1, 4, 7, 10],

[ 2, 4, 8, 11]])

>>> c.ctypes.data

80831392

>>> string_at(c.ctypes.data, c.nbytes).hex()

'000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b000000'

>>> a

array([[ 0, 1, 2, 3],

[ 4, 5, 6, 7],

[ 8, 9, 10, 11]])

# copy会复制一份新的数据,其物理地址位于不同的区域

>>> c = b.copy()

>>> c

array([[ 0, 1, 2],

[ 3, 4, 5],

[ 6, 7, 8],

[ 9, 10, 11]])

>>> c.ctypes.data

80831456

>>> string_at(c.ctypes.data, c.nbytes).hex()

'000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b000000'

# slice操作产生的也是view视图,仍指向原来数据block中的物理地址

>>> d = b[1:3, :]

>>> d

array([[3, 4, 5],

[6, 7, 8]])

>>> d.ctypes.data

80831404

>>> print('data buff address from {0} to {1}'.format(b.ctypes.data, b.ctypes.data + b.nbytes))

data buff address from 80831392 to 80831440

副本是一个数据的完整的拷贝,如果我们对副本进行修改,它不会影响到原始数据,物理内存不在同一位置。

视图是数据的一个别称或引用,通过该别称或引用亦便可访问、操作原有数据,但原有数据不会产生拷贝。如果我们对视图进行修改,它会影响到原始数据,物理内存在同一位置。

视图一般发生在:

1、numpy 的切片操作返回原数据的视图。

2、调用 ndarray 的 view() 函数产生一个视图。

副本一般发生在:

Python 序列的切片操作,调用deepCopy()函数。

调用 ndarray 的 copy() 函数产生一个副本。

view机制的好处显而易见,省内存,同时速度快。

ndarray的内存布局

NumPy arrays consist of two major components, the raw array data (from now on, referred to as the data buffer), and the information about the raw array data. The data buffer is typically what people think of as arrays in C or Fortran, a contiguous (and fixed) block of memory containing fixed sized data items. NumPy also contains a significant set of data that describes how to interpret the data in the data buffer.

ndarray的内存布局示意图如下:

可大致划分成2部分——对应设计哲学中的数据部分和解释方式:

raw array data:为一个连续的memory block,存储着原始数据,类似C或Fortran中的数组,连续存储

metadata:是对上面内存块的解释方式

metadata都包含哪些信息呢?

dtype:数据类型,指示了每个数据占用多少个字节,这几个字节怎么解释,比如int32、float32等;

ndim:有多少维;

shape:每维上的数量;

strides:维间距,即到达当前维下一个相邻数据需要前进的字节数,因考虑内存对齐,不一定为每个数据占用字节数的整数倍;

上面4个信息构成了ndarray的indexing schema,即如何索引到指定位置的数据,以及这个数据该怎么解释。

除此之外的信息还有:字节序(大端小端)、读写权限、C-order(行优先存储) or Fortran-order(列优先存储)等,如下所示,

>>> a.flags

C_CONTIGUOUS : True

F_CONTIGUOUS : False

OWNDATA : True

WRITEABLE : True

ALIGNED : True

WRITEBACKIFCOPY : False

UPDATEIFCOPY : False

ndarray的底层是C和Fortran实现,上面的属性可以在其源码中找到对应,具体可见PyArrayObject和PyArray_Descr等结构体。

为什么可以这样设计

为什么ndarray可以这样设计?

因为ndarray是为矩阵运算服务的,ndarray中的所有数据都是同一种类型,比如int32、float64等,每个数据占用的字节数相同、解释方式也相同,所以可以稠密地排列在一起,在取出时根据dtype现copy一份数据组装成scalar对象输出。这样极大地节省了空间,scalar对象中除了数据之外的域没必要重复存储,同时因为连续内存的原因,可以按秩访问,速度也要快得多。

>>> a

array([[ 0, 1, 2, 3],

[ 4, 5, 6, 7],

[ 8, 9, 10, 11]])

>>> a[1,1]

5

>>> i,j = a[1,1], a[1,1]

# i和j为不同的对象,访问一次就“组装一个”对象

>>> id(i)

102575536

>>> id(j)

102575584

>>> a[1,1] = 4

>>> i

5

>>> j

5

>>> a

array([[ 0, 1, 2, 3],

[ 4, 4, 6, 7],

[ 8, 9, 10, 11]])

# isinstance(val, np.generic) will return True if val is an array scalar object. Alternatively, what kind of array scalar is present can be determined using other members of the data type hierarchy.

>> isinstance(i, np.generic)

True

这里,可以将ndarray与python中的list对比一下,list可以容纳不同类型的对象,像string、int、tuple等都可以放在一个list里,所以list中存放的是对象的引用,再通过引用找到具体的对象,这些对象所在的物理地址并不是连续的,如下所示

所以相对ndarray,list访问到数据需要多跳转1次,list只能做到对对象引用的按秩访问,对具体的数据并不是按秩访问,所以效率上ndarray比list要快得多,空间上,因为ndarray只把数据紧密存储,而list需要把每个对象的所有域值都存下来,所以ndarray比list要更省空间。

小结

下面小结一下:

ndarray的设计哲学在于数据与其解释方式的分离,让绝大部分多维数组操作只发生在解释方式上;

ndarray中的数据在物理内存上连续存储,在读取时根据dtype现组装成对象输出,可以按秩访问,效率高省空间;

之所以能这样实现,在于ndarray是为矩阵运算服务的,所有数据单元都是同种类型。

参考

numpy数组和矩阵之间有什么区别?我该用哪一个?

Numpy矩阵严格是二维的,而numpy阵列(Ndarray)是N维的.矩阵对象是ndarray的子类,因此它们继承了ndarray的所有属性和方法。

numpy矩阵的主要优点是它们为矩阵乘法提供了一种方便的表示法:如果a和b是矩阵,那么a*b就是它们的矩阵乘积。

import numpy as np

a=np.mat('4 3; 2 1')b=np.mat('1 2; 3 4')print(a)# [[4 3]#  [2 1]]print(b)# [[1 2]#  [3 4]]print(a*b)# [[13 20]#  [ 5  8]]

另一方面,从Python3.5开始,NumPy支持使用@运算符,因此您可以使用Python>=3.5中的ndarray实现矩阵乘法的同样方便。

import numpy as np

a=np.array([[4, 3], [2, 1]])b=np.array([[1, 2], [3, 4]])print(a@b)# [[13 20]#  [ 5  8]]

矩阵对象和ndarray都有.T若要返回转置,但矩阵对象也具有.H对于共轭转置,和.I反之亦然。

相反,numpy数组始终遵循按元素应用操作的规则(新的除外)。@(操作员)因此,如果a和b是numpy数组,那么a*b是将组件元素按顺序相乘形成的数组:

c=np.array([[4, 3], [2, 1]])d=np.array([[1, 2], [3, 4]])print(c*d)# [[4 6]#  [6 4]]

若要获得矩阵乘法的结果,请使用np.dot(或@在Python>=3.5中,如上文所示):

print(np.dot(c,d))# [[13 20]#  [ 5  8]]

这个**运算符的行为也不同:

print(a**2)# [[22 15]#  [10  7]]print(c**2)# [[16  9]#  [ 4  1]]

自a是矩阵,a**2返回矩阵积a*a..自c是一条警钟,c**2返回一个包含每个组件平方元素的ndarray。

矩阵对象和ndarray之间还有其他技术差异(与np.ravel、项选择和序列行为有关)。

Numpy阵列的主要优点是它们比二维矩阵更通用.当你想要一个三维数组时会发生什么?然后你必须使用ndarray,而不是矩阵对象。因此,学习使用矩阵对象是更多的工作-你必须学习矩阵对象操作和ndarray操作。

编写一个同时使用矩阵和数组的程序会使你的生活变得困难,因为你必须跟踪你的变量是哪种类型的对象,以免乘法返回你不想要的东西。

相反,如果您只使用ndarray,那么您可以完成矩阵对象所能做的所有事情,甚至更多,除非函数/表示法稍有不同。

如果您愿意放弃NumPy矩阵乘积表示法的视觉吸引力(在Python>=3.5中使用ndarray几乎可以很好地实现它),那么我认为NumPy数组绝对是可行的。

PS。当然,你真的不必牺牲另一个而选择一个,因为np.asmatrix和np.asarray允许您将其中一个转换为另一个(只要数组是二维的)。

NumPy之间的区别有一个概要arraysVS NumPymatrix埃斯这里.

python中ndarray是什么意思_什么是Numpy的ndarray相关推荐

  1. Python中既有列表,何必再有数组(NumPy‘s ndarray)?

    1:数组对象能够省略元素间运算所需循环,让一维向量使用起来更像单个数据,从而简化数据处理程序的编写. 2:经过大量观察后发现科学计算当中同一维度的数据的数据类型往往相同,数组和列表的区别是列表的元素类 ...

  2. python中的np.array函数_对列表numpy数组中的每个列表应用函数

    一些比较和时间测试:但请记住,这只是一个小例子.在In [106]: test_arr = np.array([['the', 'quick', 'brown', 'fox'], ['lorem', ...

  3. python中五种下划线 _

    python中五种下划线 "_" 单前导下划线:_var 单末尾下划线:var_ 双前导下划线:__var 双前导和末尾下划线:var 单下划线:_ 在文章结尾处,你可以找到一个简 ...

  4. python中的array函数作用_数据分析的python基底(3)——array、Series、DataFrame笔记...

    <利用python进行数据分析>的第4.5章介绍了两个非常重要的包,NumPy和Pandas,这篇文章是我看这两章做的笔记,只列了要点和我觉得要留意的地方. 电子书和配套代码,还有一些学习 ...

  5. python中脚本是指什么_【学习猿地】初识python脚本 #千万别看,我怕你控制不住...

    >编写python程序的文件,称为python的脚本或程序 >要求当前的python脚本的文件后缀名必须是.py #### pycharm使用注意 > 需要明确的知道你当前pycha ...

  6. python中mod是什么意思_【python中,mod_python到底做了些什么呢?】mod python 教程

    python 编程小白 ,不会用doctest 请大神指教怎么用!! >>> >>> def is_between(v, lower, higher): ...   ...

  7. python中二进制和文本不同_关于Python字符编码与二进制不得不说的一些事

    二进制 核心思想: 冯诺依曼 + 图灵机 电如何表示状态,才能稳定? 计算机开始设计的时候并不是考虑简单,而是考虑能自动完成任务与结果的可靠性, 简单始终是建立再稳定.可靠基础上 经过尝试10进制,但 ...

  8. python中sub是什么意思_相当于Python中R的sub和paste(字符串和数字的连接)

    以前,在R中,我使用sub和paste将字符串和数字连接在一起. 我发现Python有点难.下面是Python中的示例代码import pandas as pd from numpy.random i ...

  9. python中的符号下划线_详解Python中下划线的使用方法

    编程派微信号:codingpy 这篇文章讨论Python中下划线_的使用.跟Python中很多用法类似,下划线 _ 的不同用法绝大部分(不全是)都是一种惯例约定. 单个下划线(_) 主要有三种情况: ...

最新文章

  1. Web前端面试自我介绍对话技巧注意事项
  2. rsyslog概要以及源码安装mysql,rsyslog输出到Mysql基于loganalyzer查看分析
  3. 2019年1月计算机书籍JavaScript新书
  4. C#几种常用的排序算法
  5. linux安装的mysql没有密码_linux系统安装的mysql数据库root帐户密码忘记的两种处理方法...
  6. mysql have_mysql having的用法
  7. 【和60】软件即服务的三重境界
  8. js 创建file对象_JS 之创建对象
  9. 通过cacti+nagios监控服务器的运行—nagios nrpe
  10. 如何用计算机表白男神,女生表白男生的方法 教你如何正确向男神表白
  11. python os.walk如何不遍历隐藏文件,Python os.walk() 遍历出当前目录下的文件夹和文件...
  12. Barsetto百胜图全自动美式磨豆咖啡机测评
  13. JS变量、数据类型及运算符
  14. iOS 初学者功能代码大集合,个人笔记
  15. [[机缘参悟-87]:每个人需要了解自己的性格特征(老虎、孔雀、考拉、猫头鹰、变色龙)
  16. 基于ASP.NET+SQL Server实现(Web)企业进销存管理系统【100010296】
  17. 怎样申请建立一个全新的顶级域名后缀?
  18. 零基础通过Ajax实现网易云音乐数据交互(4)
  19. 布法罗大学计算机硕士学费,美国水牛城大学学费贵不贵(美国水牛城大学往年排名情况怎么样)...
  20. Mac开发-公证流程记录Notarization-附带脚本

热门文章

  1. 数字字符与中文字符的替换
  2. pandas 数据分析 相关性_pandas数据分析
  3. 豆神教育轻装上阵,搏命“大语文”下能否扭转24亿亏损的乾坤?
  4. 周灵王,姬泄心(?―前545年)
  5. 让HTTPS简要易懂
  6. Vscode配置python环境
  7. Spark GraphX学习笔记
  8. 企业系统服务器配置,企业买服务器如何选?
  9. ELK 日志分析系统的部署
  10. MVC validate数据验证