numpy是一个很大的库,完全了解它是不现实的,只能是了解常用的功能。平时遇见不懂的地方弄清楚,注意积累。
组元不需要圆括号
虽然我们经常在Python中用圆括号将组元括起来,但是其实组元的语法定义只需要用逗号隔开即可,例如 x,y=y,x 就是用组元交换变量值的一个例子。
一.为啥需要numpy
python虽然说注重优雅简洁,但它终究是需要考虑效率的.别说运行速度不是瓶颈,在科学计算中运行速度就是瓶颈.
python的列表,跟java一样,其实只是一维列表.一维列表相当于一种类型,这样对于元素的访问效率是很低的. python中一切皆引用,每一个int对象都要用指针指一下再用int存储一下,浪费空间也浪费时间.当读取某个元素的时候需要先读取引用,再根据引用指向的内存地址来读取int值. numpy相当于完全采用了C语言那套数组机制.二.numpy原则
- 一切皆一维,多维只是马甲 多维数组的内部实现就是一维.
- 定长,一切皆矩形,一切皆长方体. 比如定义了一个数组a[3],则 len(a[0])=len(a[1])=len(a[2]),各个元素不能变长 正是因为定长这个原则,才有可能能实现"一切皆一维"这个原则.
- 数组中元素类型相同,长度相同 numpy中的数组都是一维数组,并且这个一维数组中每个元素的长度相同,各个元素属于同一种类型. numpy中的元素相当于结构体,一个结构体所占字节数是固定的,numpy是允许用户自定义结构体类型的.
- 数组就是一块空间 想对它作何解释就作何解释,想给它穿上什么马甲就给它穿上什么马甲. 对于一个包含24个元素的一维数组,可以把它理解为4×6或者2×12或者3×8的二维数组,也可以把它理解为2×2×6或者3×2×4的三维数组.
三.numpy概念
- ndarray.ndim n前缀表示个数,dim表示dimension,ndim表示维数
- ndarray.shape 数组的维度。这是一个指示数组在每个维度上大小的整数元组,这个元组的长度显然是秩,即维度或者ndim属性
- ndarray.size 数组元素的总个数,等于shape属性中元组元素的乘积。
- ndarray.dtype 一个用来描述数组中元素类型的对象,可以通过创造或指定dtype使用标准Python类型。另外NumPy提供它自己的数据类型。
- ndarray.itemsize 数组中每个元素的字节大小。例如,一个元素类型为float64的数组itemsiz属性值为8(=64/8),又如,一个元素类型为complex32的数组item属性为4(=32/8).
- ndarray.data 包含实际数组元素的缓冲区,通常我们不需要使用这个属性,因为我们总是通过索引来使用数组中的元素。 这个属性太重要了,因为numpy中的ndarray只是一个马甲,data部分才是真真正正的数据。ndarray中的shape、dtype等属性相当于“数据解释器”,用来描述data中的数据是如何组织的。
一个例子
>>> from numpy import *>>> a = arange(15).reshape(3, 5)>>> aarray([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]])>>> a.shape(3, 5)>>> a.ndim2>>> a.dtype.name'int32'>>> a.itemsize4>>> a.size15>>> type(a)numpy.ndarray>>> b = array([6, 7, 8])>>> barray([6, 7, 8])>>> type(b)numpy.ndarray
改变数组维度的两种方式:
- a.reshape(d1,d2...),不改变a本身
- a.shape=元组,改变a本身,相当于a=a.reshape(元组)
a=np.arange(24)b=a.reshape(4,-1)a.shape,b.shapeOut[21]: ((24,), (4, 6))a.shape=-1,5Traceback (most recent call last): File "", line 1, in a.shape=-1,5ValueError: total size of new array must be unchangeda.shape=-1,4aOut[24]: array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]])
一个比较常用的功能是把多维数组变成一维数组,有如下三种实现方式
- a.shape=-1
- b=a.reshape(-1)
- b=a.ravel() b和a共用同一份data
- b=a.flatten() b的data是a的data的复制品
- a.resize((d1,d2...)) 相当于a=a.reshape((d1,d2..)),也相当a.shape=d1,d2...,它直接更改a的形状
四.创建ndarray对象
- np.array([[1,2],[3,4]],dtype=userType) 使用array对象来封装python的列表或者元祖或者range对象.
- 创建一维对象 np.linspace(start,stop,num,endpoint=True,retStep=false,dtype=None)产生等差数列,指明数组的首元素、末元素和数组长度,生成一个等差数列。 np.logspace产生等比数列,参数跟linspace完全一致 np.arange(start=0,end,step=1,dtype=None)产生等差数列
给定维数数组创建ndarray
np.ones全1数组 np.zeros全0数组 np.empty不做处理的数组,只负责开辟空间,比前面两个速度快 创建随机ndarray,可以指定不同的随机分布,详见下文从字节序列出发创建ndarray
np.fromstring,np.frombuffer,np.fromfile 一切都是小头序从迭代器出发创建ndarray
np.fromiter:np.fromiter(range(100),dtype=np.int32,count=10)从函数出发创建ndarray
np.fromfunction
>>> def func2(i, j):... return (i+1) * (j+1)...>>> a = np.fromfunction(func2, (9,9))>>> aarray([[ 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.]])
五.创建随机ndarray对象
随机值都是在区间[0,1)上
- 均匀分布 random.random(size=None)默认返回一个0~1之间的数字,可以指明维度序列来生成特定维度的随机值. random.randint(low,high,size)返回一个int型数组,每个元素都在low,high之间. np.random.rand,相当于random.random((d1,d2,d3)),只不过这个函数的参数可以是多个而不仅仅是一个元组.
- 正态分布 np.random.randn
六、存取元素
1、共享存储空间的切片操作
numpy的设计原则就是“高效”。Python中的切片是复制原数组,效率很低。而numpy中的数组切片与原数组共享同一内存,效率极高。>>> b = a[3:7] # 通过下标范围产生一个新的数组b,b和a共享同一块数据空间>>> barray([101, 4, 5, 6])>>> b[2] = -10 # 将b的第2个元素修改为-10>>> barray([101, 4, -10, 6])>>> a # a的第5个元素也被修改为10array([ 0, 1, 100, 101, 4, -10, 6, 7, 8, 9])
2、numpy下标操作完全包括python中的下标操作
>>> a = np.arange(10)>>> a[5] # 用整数作为下标可以获取数组中的某个元素5>>> a[3:5] # 用范围作为下标获取数组的一个切片,包括a[3]不包括a[5]array([3, 4])>>> a[:5] # 省略开始下标,表示从a[0]开始array([0, 1, 2, 3, 4])>>> a[:-1] # 下标可以使用负数,表示从数组后往前数array([0, 1, 2, 3, 4, 5, 6, 7, 8])>>> a[2:4] = 100,101 # 下标还可以用来修改元素的值>>> aarray([ 0, 1, 100, 101, 4, 5, 6, 7, 8, 9])>>> a[1:-1:2] # 范围中的第三个参数表示步长,2表示隔一个元素取一个元素array([ 1, 101, 5, 7])>>> a[::-1] # 省略范围的开始下标和结束下标,步长为-1,整个数组头尾颠倒array([ 9, 8, 7, 6, 5, 4, 101, 100, 1, 0])>>> a[5:1:-2] # 步长为负数时,开始下标必须大于结束下标array([ 5, 101])
3、numpy下标操作更加灵活
在python下标操作基础上,numpy的下标操作更加灵活:a=np.arange(25).reshape(5,5)aOut[63]: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]])a[3,3]Out[65]: 18a[:,2]Out[66]: array([ 2, 7, 12, 17, 22])
4、通过下标数组获取元素数组
numpy提供了两种方式:- 使用整数序列
- 使用bool数组
使用整数序列获取多个元素,返回的是原数组的副本
>>> x = np.arange(10,1,-1)>>> xarray([10, 9, 8, 7, 6, 5, 4, 3, 2])>>> x[[3, 3, 1, 8]] # 获取x中的下标为3, 3, 1, 8的4个元素,组成一个新的数组array([7, 7, 9, 2])>>> b = x[np.array([3,3,-3,8])] #下标可以是负数>>> b[2] = 100>>> barray([7, 7, 100, 2])>>> x # 由于b和x不共享数据空间,因此x中的值并没有改变array([10, 9, 8, 7, 6, 5, 4, 3, 2])>>> x[[3,5,1]] = -1, -2, -3 # 整数序列下标也可以用来修改元素的值>>> xarray([10, -3, 8, -1, 6, -2, 4, 3, 2])
使用布尔数组获取元素,如果布尔数组不够长,那么不够长的部分默认为false
>>> x = np.arange(5,0,-1)>>> xarray([5, 4, 3, 2, 1])>>> x[np.array([True, False, True, False, False])]>>> # 布尔数组中下标为0,2的元素为True,因此获取x中下标为0,2的元素array([5, 3])>>> x[[True, False, True, False, False]]>>> # 如果是布尔列表,则把True当作1, False当作0,按照整数序列方式获取x中的元素array([4, 5, 4, 5, 5])>>> x[np.array([True, False, True, True])]>>> # 布尔数组的长度不够时,不够的部分都当作Falsearray([5, 3, 2])>>> x[np.array([True, False, True, True])] = -1, -2, -3>>> # 布尔数组下标也可以用来修改元素>>> xarray([-1, 4, -2, -3, 1])
5、多维数组
多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标。多维数组的下标也可以使用下标数组和掩码数组,但要注意这时得到的是原数组的副本。
七、numpy数据类型
bool_ Boolean (True or False) stored as a byte
int_ Default integer type (same as C long; normally either int64 or int32) intc Identical to C int (normally int32 or int64) intp Integer used for indexing (same as C ssize_t; normally either int32 or int64) int8 Byte (-128 to 127) int16 Integer (-32768 to 32767) int32 Integer (-2147483648 to 2147483647) int64 Integer (-9223372036854775808 to 9223372036854775807) uint8 Unsigned integer (0 to 255) uint16 Unsigned integer (0 to 65535) uint32 Unsigned integer (0 to 4294967295) uint64 Unsigned integer (0 to 18446744073709551615) float_ Shorthand for float64. float16 Half precision float: sign bit, 5 bits exponent, 10 bits mantissa float32 Single precision float: sign bit, 8 bits exponent, 23 bits mantissa float64 Double precision float: sign bit, 11 bits exponent, 52 bits mantissa complex_ Shorthand for complex128. complex64 Complex number, represented by two 32-bit floats (real and imaginary components) complex128 Complex number, represented by two 64-bit floats (real and imaginary components)要注意,直接更改ndarray的dtype属性,并不改变data部分。还是那句话,ndarray是一个马甲,用来描述一块内存。
- a.dtype=np.float32:并不改变data部分
- b=a.astype(np.float32):并不改变a,只是将a的data部分进行数据类型转换后复制到了b的data部分
a=np.random.random(4)a.dtypeOut[21]: dtype('float64')a.dtype=np.float32len(a)Out[23]: 8a.dtype=np.float16len(a)Out[25]: 16
八.通用函数
通用函数的作用对象是数组中的每一个元素.
通用函数通常有一个out参数,如果带上这个参数就可以避免开辟新的内存空间.九.广播broadcast
两个不同维度的数组相加
import numpy as npa = np.arange(5)b = np.arange(6).reshape(-1, 1)def rep(a, c): for i in range(a.ndim-1, -1, -1): if a.shape[i] == c.shape[i]: continue if a.shape[i] == 1: a = a.repeat(c.shape[i], axis=i) else: raise Exception("dimention not match exception") return adef add(a, b): if a.ndim>b.ndim: a, b = b, a ashape = [1] * (b.ndim-a.ndim) + list(a.shape) a = a.reshape(ashape) cshape = [max(a.shape[i], b.shape[i]) for i in range(a.ndim)] c = np.empty(cshape) a = rep(a, c) b = rep(b, c) a = a.reshape(-1) b = b.reshape(-1) cc = c.reshape(-1) for i in range(len(cc)): cc[i] = a[i] + b[i] return cprint(add(a, b))print(a + b)
九、ndarray的组合与拆分
np.hstack
np.vstack np.dstack np.column_stack np.row_stacknp.hsplit
np.vsplit np.dsplit np.split