什么是Numpy的ndarray
⾸先,Numpy的核⼼是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 , which describes a collection of “items” of the same type. The items can be 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)
<class 'numpy.ndarray'>
>>> 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_pes.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_pes.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_pes.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}'.pes.data, b.ctypes.data + b.nbytes))
data buff address from 80831392 to 80831440
副本是⼀个数据的完整的拷贝,如果我们对副本进⾏修改,它不会影响到原始数据,物理内存不在同⼀位置。
视图是数据的⼀个别称或引⽤,通过该别称或引⽤亦便可访问、操作原有数据,但原有数据不会产⽣拷贝。如果我们对视图进⾏修改,它会影响到原始数据,物理内存在同⼀位置。
视图⼀般发⽣在:
1、numpy 的切⽚操作返回原数据的视图。
2、调⽤ ndarray 的 view() 函数产⽣⼀个视图。
副本⼀般发⽣在:
Python 序列的切⽚操作,调⽤deepCopy()函数。
调⽤ ndarray 的 copy() 函数产⽣⼀个副本。
—— from
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.
—— from
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实现,上⾯的属性可以在其源码中到对应,具体可见和等结构体。
为什么可以这样设计
为什么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阵列(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
numpy教程 pdfa=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埃斯.
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论