python科学计算之narray对象用法
写在前面
最近在系统的看一些python科学计算开源包的内容,虽然以前是知道一些的,但都属于零零碎碎的,希望这次能把常用的一些函数、注意项整理下。小白的一些废话,高手请略过^ _ ^。文章中的函数仅仅是为了自己好理解,并没有按照官方文档上的函数声明形式记录。
numpy.narray
numpy.narray创建
numpy.narray的构造方式挺多的,这里就不一一说明,因为一般情况下,在进行科学计算时是通过给定的数据文件来读取的,而读取时使用的是pandas,具体可参考官方文档,或者参见这位大牛的文章。
numpy.narray.shape & numpy.narray.reshape
numpy.narray的形状可以用numpy.narray.shape属性来获取,它返回的是narray形状的tuple,反映了各个轴的长度。
na_a = np.array([1,2,3,4]) #长度为4的一维数组 na_b = np.array([[1,2,3,4],[5,6,7,8]]) #2行4列的二维数组 out: #print na_a [1 2 3 4] #print na_b [[1 2 3 4] [5 6 7 8]] #print na_a.shape (4,) #print na_b.shape (2, 4)
numpy.narray.shape相当于一个c++的public成员,不仅可以获取,还可以通过它改变narray的形状。当某一轴指定为-1时,该轴的长度将通过其他轴来计算。
na_b.shape = (4,2) na_a.shape = (2,-1) out: #print na_b [[1 2] [3 4] [5 6] [7 8]] #print na_a [[1 2] [3 4]]
需要注意的是:通过shape属性改变narray的形状时,并没对narray元素内存中的位置改变,只是改变了外观,仅仅是换了件马甲。
当然,如果你熟悉了使用函数,不想使用属性来改变形状时,可以通过narray.reshape(),注意的是该函数返回的结果和原narray共享内存空间,换句话说,一个narray对象的改变将导致另一个narray也发生相应的改变。
na_c = na_b.reshape((4,2)) na_c[0][0] = 100 out: #print na_c [[1 2] [3 4] [5 6] [7 8]] #print na_c [[100 2] [ 3 4] [ 5 6] [ 7 8]] #print na_b [[100 2] [ 3 4] [ 5 6] [ 7 8]]
numpy.narray.dtype
narray元素的类型可以使用dtype属性来获取,当然也可以在构造narray对象时指定元素的类型,需要注意的是,在numpy中float是64位,而complex是128位的。这里的float和complex是python内置的数据类型,numpy也定义了自己的float等数据类型,而且更加的细化,比如float类型,numpy就有float16、float32、float64和float128.
af = np.array([1,2,3,4], dtype=float) ac = np.array([5,6,7,8], dtype=complex) af.dtype ac.dtype dtype('float64') dtype('complex128')
python的完整数据类型中有大部分是所占字节来细化的,完整的数据在typeDict中定义,可通过numpy.typeDict.values()来获取完整类型。numpy在多维数组上的计算很高效,但是在数值对象计算上却比python慢很多,所以在进行数值计算时应避免使用numpy。
numpy.narray.astype() 类型转换
numpy.narray.astype() 可以对narray对象中元素逐一进行类型转换。
a = np.array([1,2,3,4], dtype=np.float) a.dtype dtype('float64') b = a.astype(np.int32) b.dtype dtype('int32') c.dtype c = a.astype(np.complex) c.dtype dtype('complex128')
numpy.narray的自动生成
1.np.arange(start,end,step,dtype) 指定开始值、终值和步长生成一个等差数列,生成的narray中不包括end值。
2.np.linspace(start, end, element_nums, endpoint,dtype) 指定开始值、终值和元素个数生成一个等差数列,通过endpoint指定包不包括end,默认为True。
3.np.logspace(start, end, element_nums, base, endpoint,dtype) endpoint默认为True,base默认为10,该函数产生一个等比数列,元素为
4.np.zeros(dim,dtype)对维度为dim的narray对象初始化为0,数据类型为dtype
5.np.ones(dim, dtype) 对维度为dim的narray对象初始化为1,数据类型为dtype
6.np.empty(dim,dtype) 该函数相当于对narray对象分配内存空间,但不进行初始化,所以切不可认为产生的元素为0。
7.np.full(dim, val, dtype) 产生维度为dim的narray对象并且全部初始为val,前面的zeros()和ones()可看作该函数的特殊形式。
8.上面函数的like版本,即np.zeros_like(object) 、np.ones_like(object) 、np.empty_like(object) 、np.full_like(object) ,这里的object是narray对象,这些函数产生的narray对象和上面一样,但narray的维度(shape)以及数据类型(dtype)是和传入的参数一样的,例如np.zeros_like(object) ==np.zeros(object.shape, object.dtype)。
9.fromstring(s, dtype)这个函数是比较又意思的,将字符串转换为一个narray对象, 我们先看看这个函数输出情况。
s="abcdefgh" np.fromstring(s) array([ 8.54088322e+194]) ## 这是什么鬼? ### 输出一下dtype呢? dtype('float64')
显然,这里是按照float64类型存放的,那如果换一个数据类型呢?
np.fromstring(s,dtype=np.int8) array([ 97, 98, 99, 100, 101, 102, 103, 104], dtype=int8)
这个学过c语言的都应该知道是ASCII编码,也就可以看出,在python中一个字符是占8位,即一个字节的,继续看:
np.fromstring(s,dtype=np.int16) array([25185, 25699, 26213, 26727], dtype=int16)
这里改变数据类型位2个字节,那么应该是两个字符联立一起表示一个int16,25185=98*256+97,高位存放在低字节中,这是小端模式。OK,那如果我现在要存放的是[‘a','b'…'h']这样的narray对象呢?数据类型是什么?应该是‘|s1'
np.fromstring(s,'|S1') array(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], dtype='|S1')
frombuffer()也可以创建的narray对象,但此时创建的narray对象和原字符串是内存共享的,学过c++的都知道,string类型是只读类型,无法修改,所以在frombuffer()创建的narray无法做修改,如果要修改,请用fromstring()。
. fromfunction(fun, dim)通过自定义函数接口来创建narray对象,fun是一个函数名(看作指针),dim是一个创建对象的维度。
def fun(i): return i%4+1 np.fromfunction(fun,(10,)) array([ 1., 2., 3., 4., 1., 2., 3., 4., 1., 2.])
可见利用fromfunction()创建narray时,参数i是从0开始自增的,自增的次数是维度的大小给定的,上面给定的维度位(10,),是一个一维数组,i从0自增到9(共10次)。看另外一个例子:
def fun2(i,j): return (i+1)*(j+1) np.fromfunction(fun2,(9,9)) array([[ 1., 2., 3., 4., 5., 6., 7., 8., 9.], [ 2., 4., 6., 8., 10., 12., 14., 16., 18.], [ 3., 6., 9., 12., 15., 18., 21., 24., 27.], [ 4., 8., 12., 16., 20., 24., 28., 32., 36.], [ 5., 10., 15., 20., 25., 30., 35., 40., 45.], [ 6., 12., 18., 24., 30., 36., 42., 48., 54.], [ 7., 14., 21., 28., 35., 42., 49., 56., 63.], [ 8., 16., 24., 32., 40., 48., 56., 64., 72.], [ 9., 18., 27., 36., 45., 54., 63., 72., 81.]])
行可以看作j的自增,列可以看作i的自增,所以可以看作是两个for循环。
numpy.narray的元素存取
可以按照python中list存取元素的方式。
a = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) a[5] #5 a[3:5] #[3,4] a[:5] #[0,1,2,3,4] a[:-1] #[0, 1, 2, 3, 4, 5, 6, 7, 8] ### 也可以指定步长,通过第三个数指定 a[1:-1:2] #从开始到结束,没隔两个数取一次 array([1, 3, 5, 7]) a[5:1:-2] #反向取 array([5, 3])
注意,通过这种方式取的新narray和原始narray内存共享,所以任何一个改变,另一个也将改变。 除此之外,numpy还提供了一下几个存取方式: 使用list存取,list的每个元素作为下标,这种方式得到的新narray和原始narray不共享内存。
a = np.arange(10) a[[1,1,7,7]] #array([1, 1, 7, 7])
使用narray提取新数组,narray的元素作为下标,得到的新narray维度和参数narray维度相同。这个有点绕,看个例子吧:
a[np.array([2,1,3,6])] #array([2, 1, 3, 6]) a[np.array([[2,1,3,6],[7,4,6,1]])] ## ## array([[2, 1, 3, 6], [7, 4, 6, 1]])
使用bool数组提取,提取bool数组中为True的下标元素:
a[np.array([True, False,True,False,False,False,True,False,False,False])] ## array([0, 2, 6]) a[np.array([True,False,True,False,False,False,True,False,False,False])]=-1,-2,-3 print a #array([-1, 1, -2, 3, 4, 5, -3, 7, 8, 9])
多维数组
请先看一张图:
左边是用切片方式提取右边6*6的narray对象元素。这种提取元素方式和原narray对象内存共享。
这里个人的理解,利用切片方式提取的新narray和原narray是内存共享的,而使用列表、bool数组等方式则不共享,原因很简单,利用切片方式,例如a[3:5],是从第三个元素到第5个元素(不包括),那么是用元素所占字节来移动的,就像c中的指针,这样比较便利。而其他方式是用下标,这些下标往往不连续,如果用指针方式的话则要来回移动,比较麻烦。所以不提供内存共享这种方式。
在这之前我们使用切片都是通过在[]中指定起点、终点以及步长,如果要单独创建切片对象呢?使用slice对象,创建的方式很简单,把[]中的:替换为None。
a = np.arange(0,60,10).reshape(-1,1)+np.arange(6) ### 广播,后面会说到 array([[ 0, 1, 2, 3, 4, 5], [10, 11, 12, 13, 14, 15], [20, 21, 22, 23, 24, 25], [30, 31, 32, 33, 34, 35], [40, 41, 42, 43, 44, 45], [50, 51, 52, 53, 54, 55]]) a[::2,2:] ## array([[ 2, 3, 4, 5], [22, 23, 24, 25], [42, 43, 44, 45]]) idx = slice(None,None,2),slice(2,None) a[idx] ## array([[ 2, 3, 4, 5], [22, 23, 24, 25], [42, 43, 44, 45]])
同时,numpy提供了IndexExpression类的对象s_来方便slice的创建。
np.s_[::2,2:] ## (slice(None, None, 2), slice(2, None, None))
结构体数组
numpy提供了和c类似的结构体数组,并且相互之间能够读取,以一个例子来说明:
## 创建persontype类型 persontype=np.dtype({ 'names':['name','age','weight'], 'formats':['S30','i','f']}, align=True) ## 创建结构体数组 a=np.array([("zhang",20,55.5), ("wang",24,65.2)], dtype=persontype)
persontype有两个key,names和formats,names是以字典的方式标明结构体所含的字段,formats是对相应字段的数据类型。S30表示长度不超过30个字节的字符串,i相当于np.int32,f相当于np.float32,下面创建结构体数组的时候指定数据类型是我们创建的persontype。
## print a array([('zhang', 20, 55.5 ), ('wang', 24, 65.19999695)], dtype={'names':['name','age','weight'], 'formats':['S30','<i4','<f4'], 'offsets':[0,32,36], 'itemsize':40, 'aligned':True}) ## print a[0] ('zhang', 20, 55.5) ## a[0].dtype dtype({'names':['name','age','weight'], 'formats':['S30','<i4','<f4'], 'offsets':[0,32,36], 'itemsize':40}, align=True) ## a[0]["name"] 'zhang' b=a['age'] ## 20 24 b[0]=50 ## print a[0] ('zhang', 50, 55.5)
通过tostring()和tofile()可以将结构体数组写入文件,并可以通过c读取,由于c中的结构体存在对齐,所以在python创建结构体数组时也需要使用相应的对齐方式,这一点通过align来指定,True为对齐,False不对齐。
narray内存结构
以二维narray对象为例,我们来看看narray对象在内存中的结构。
a = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32)
该二维数组的结构如下图:
左边时一个结构体,描述了该narray对象的基本信息,包括数据类型(np.float32)、维度(轴的数量)、每个轴的数据个数、每个轴相邻元素的字节差、最后是存放数据的内存地址。通过切片方式得到的新narray就是在data字段表示的内存上获得原始narray的视图,我们来看看:
b=a[::2,::2] ## b array([[ 0., 2.], [ 6., 8.]], dtype=float32) ## b.strides (24, 8)
获得的新narray.strides发生了变化,而这个变化也刚好是每个轴相邻元素跨越的字节数。narray的数据在内存上的排列有两种形式,分别是C和FORTRAN格式,上面我们展示的就是C的格式,第0轴是最上位的,也就是说,第0轴作为行,所以第0轴的相邻元素跨越的字节数是最多的(比如这里为3*4=12),而FORTRAN结构与C格式相反,它是第1轴跨越的字节数最多。numpy默认是C格式,如果想改成FORTRAN的格式,只需要在创建narray的时候设置order=‘F'。narray的flags属性描述了存储区的一些属性:
## a.flags C_CONTIGUOUS : True #C语言格式 F_CONTIGUOUS : False #Fortran语言格式 OWNDATA : True #数组是否拥有该存储区,如果是一个narray的视图则不拥有 WRITEABLE : True #是否可写 (改变其中数据) ALIGNED : True # 对齐 UPDATEIFCOPY : False
OK,这里引出一个东东,如果要将a进行转置,我们是需要将需要将a顺时针旋转90°,那么还有上面简单的方法?我们先看看a.T.flags:
## a.T.flags C_CONTIGUOUS : False F_CONTIGUOUS : True OWNDATA : False WRITEABLE : True ALIGNED : True UPDATEIFCOPY : False
从这上面可以看到,对于a的转置,numpy是将其内存格式改变了,从C语言格式变成了Fortran的格式,并且转置是a的一个视图。这样的方式比旋转方式更加快速。
以上这篇python科学计算之narray对象用法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持【听图阁-专注于Python设计】。