numpy快速入门(常用操作)

numpy快速入门

在numpy中阵列维度称为轴axes,axes中的每一个轴称为axis,每个axis可以有不同的长度

e.g.
	[[1, 0, 0],
	 [0, 1, 2]]
# 其中,axes=2,first axis 长度为2(行),second axis 长度为3(列)

numpy’s array 类被称为 ndarray,通常也用别名 array称呼。有如下重要性质:

  .ndim: array的axes数 		
  .shape: array的阵列维数,整型值的元祖,n行m列的矩阵,其shape就是(n, m),shape的len就是axes的值,即等同于.ndim值 		
  .size: array中元素的总数,等于shape元祖中元素的乘积。
  .dtype: array中元素的数据类型,可以用标准pyhton数据类型进行创建或指定,也可以用numpy自身的数据类型进行创建或指定。
  .itemsize: array中每个元素的字节大小,等价于ndarray.dtype.itemsize 		
  .data: 包含数array的实际元素的缓存。通常很少使用这个性质,更有意义的是我们使用array的索引来对array中的元素进行计算。

创建array

有多种创建ndarray的方式:

  1. 方式一:从常规的Python’s list or tuple利用array函数创建,生成结果array的元素类型由基础序列中元素的类型推导而来(也可以利用dtype强制指定)。
    在调用array函数时,传入参数应该是一个列表(元组)对象,否则会引发错误:

    np.array(1, 2, ,3) 	# raise a error
    np.array([1, 2, 3]) 	# right,由源数据推测数据类型
    np.array([1, 2, 3], dtype=np.int16) 	# right,指定数据类型
    

    对于序列为元素的序列,将其作为array的元素对象,有几层嵌套则生成几维数组:

    np.array([(1, 2, 3, 4), (6, 7, 4, 3)]) or np.array([[1, 2, 3, 4], [6, 7, 4, 3])
    result:二维数组 shape=(2, 2)
    
    np.array([([1, 2], [3, 4]), ([6, 7], [4, 3])]) or np.array([[[1, 2], [3, 4]], [[6, 7], [4, 3]]])
    result:三维数组 shape=(2, 2, 2)
    
  2. 几种特殊array 函数:

    .zeros, 	# 全0矩阵
    .ones,		# 全1矩阵
    .empty,		# 空矩阵,“空”仅是一种描述,实际生成矩阵会有值作为占位。
    

    注意默认生成数据类型是‘float64’,其他数据类型需要手动指定:

    np.zeros(shape=(3, 4), dtype=int)
    
  3. 生成数字序列:范围内指定步长生成满足条件的全部值(数量未知) and 指定范围内生成指定数量的值(步长未知);

    ------------------------------------------------------------------------------------
    # 范围内指定步长生成满足条件的全部值(数量未知)
    np.arange(start, end, [step], [dtype]): 类似于range函数,返回arrays
    np.arange(2, 5, 1)
    ------------------------------------------------------------------------------------
    # 指定范围内生成指定数量的值(步长未知)
    np.linspace(start, end, num)
    np.linspace(0, 2, 9) 	# 返回0-2范围内的9个数
    

打印array

如果array过大,numpy仅打印array的角落数值,跳过其它值。
如果想全部打印,可以利用set_printoptions来设置:

	np.set_printoptions(threshold=np.nan)

基本运算

数组上的算术计算应用在元素级(elementwise),创建一个新的数组并用算术计算结果填充它。

numpy同其它矩阵语言不同之处:

  1. array间的*运算是对应元素的乘积

  2. array或矩阵乘积需要使用运算符@或者dot函数

    a = np.array([[a, b], [c, d]])
    b = np.array([[e, f], [g, h]])
    a * b = array([[a*e, b*f], [c*g, d*h]]) 	# elementwise product
    a @ b = array([[a*e+b*g, a*f+b*h], [c*e+d*g, c*f+d*h]]) 	# matrix product
    a.dot(b)	= array([[a*e+b*g, a*f+b*h], [c*e+d*g, c*f+d*h]]) 	# anther matrix product
    

对于类似+=或者*=这样的累计运算,结果是在当前数组上进行修改而不是创建新的数组。

array_a = np.ones((3, 3), dtype=np.int64)
array_b = np.random.random((3, 3))
print(array_a, array_b)
array_a *= 2
print(array_a)
array_b += a
print(b)
array_a += array_b 	 	# 发生类型错误,array_b is not automatically converted to integer type
but:array_a + array_b 可以运行,结果生成新的数组,结果数组数据类型对应于更一般的数组 如 int + float = float

当操作不同类型的数组时,结果数组的类型对应于更一般或更精确的数组(称为向上转换行为)。

array_c = array_a + array_b
array_c.dtype.name 		# out:'float64'

诸如sum这样的对array中所有元素进行计算的一元运算函数,这类函数都是作为ndarray类的方法来实现的。

array_a.sum() 	# array_a 是一个ndarray
array_b.max() 		# array_b 是一个ndarray

默认情况下,sum()这样的函数对array进行计算时,将array假想成一个数值list,而忽略array的形状。
但是,通过指定axis参数,可以强制计算沿着制定的axis而进行。

array_b.sum(axis=1) 	# sum of each row
array_b.sum(axis=0) 	# sum of each column

通用函数(universal functions)

numpy提供了一些诸如三角函数这样的常用函数,在numpy中,将其称为“universal functions(ufunc)”.
在numpy内部,这些函数在array中执行elementwise(元素级)的运算,生成一个新的array作为输出。

索引 切片和迭代

一维array可以像python中的list等序列对象一样,进行索引切片和迭代操作。

a = np.arange(10)**3
a[2] 				# 取特定索引位的值,索引位=2
a[2:5] 				# 取特定索引范围的值,索引范围[2:5],不含index=5的值
a[:6:2] = -1000 	# 等价于a[0:6:2]=-1000,从start索引位到end索引位以间隔为2将对应索引位的值置为-1000
a[::-1] 	 		# 将array颠倒顺序,反转。

多维array每一个axis是一个索引值,这些索引在一个以逗号分隔的元组中给出。

def f(x, y):
	return 10*x+y
	
b = np.fromfunction(f, (5, 4), dtype=int)
b[2, 3] 	 	# 取第1个axis(row)的第2个索引位和第2个axis(column)的第3个索引位的值
b[0:5, 1] 		# 取b中0-4行上index=1的列上的值
b[:, 1] 	 	# 等价于上一行(数组b的shape=(5, 4))
b[1:3, :] 		# 取b中aixs=1的1,2索引位对应的axis=2的所有索引位的值(取第2,3行与对应的全部列的值)

当比array中axes少的索引值被提供时,缺失的索引被认为是完整的切片。

b[-1] 	# b中最后一行,等价于b[-1, :]

多维数组的迭代是关于第一个轴(axis)的:

for row in b:
	print b

但是,如果要对数组中的每个元素执行操作,可以使用flat属性,它是数组中所有元素的迭代器:

for element in b.flat:
	print element 	# 会将数组b中的每一个元素打印出来。

数组的shape操作

改变array的shape

一个array的shape由array中每个轴axis上元素数量决定。

a = np.floor(10*np.random.random((3, 4)))
a.shape 	 	# out:(3, 4)

array的shape可由多种方式改变,以下三种方式将返回一个修改了的array,同时不会修改源array.

a.ravel() 		# 返回新的array,将源array中的值进行拉伸成一维array,like list
a.reshape(6, 2) or a.reshape((6, 2)) 	 	# 返回源array被用指定数值修改shape的后新的array
a.T 	 		# 返回源array转置后的新的array

ndarray.reshape 和 ndarray.resize 方法的区别:

.shape返回修改后的新array,不对源array进行修改
.resize修改源数组自身。
a 	# out: array([[0., 7., 2., 2.],
	# 			[5., 1., 5., 5.],
	#			[5., 4., 8., 9.]])
a.resize((2, 6)) 	 	# out: array([[0., 7., 2., 2., 5., 1.],
						#		[5., 5., 5., 4., 8., 9.]])

如果在reshape中任一维度值被置为-1,则其他的维度将会自动计算,被指定为-1的维度会被自动推测为合理的值。

a.reshape((3, -1))  				# out: array([[0., 7., 2., 2.],
									#				[5., 1., 5., 5.],
									#				[5., 4., 8., 9.]])

数组叠加

数组可以沿着不同的维(axes)堆叠在一起:

	>>> a = np.floor(10*np.random.random((2,2)))
	>>> a
	array([[ 8.,  8.],
	       [ 0.,  0.]])
	>>> b = np.floor(10*np.random.random((2,2)))
	>>> b
	array([[ 1.,  8.],
	       [ 0.,  4.]])
	>>> np.vstack((a,b))  	 	# 纵向叠加
	array([[ 8.,  8.],
	       [ 0.,  0.],
	       [ 1.,  8.],
	       [ 0.,  4.]])
	>>> np.hstack((a,b)) 	 	# 水平叠加
	array([[ 8.,  8.,  1.,  8.],
	       [ 0.,  0.,  0.,  4.]])

函数column_stack将1D-array以列的形式叠加成2D-array,等价于2Darray中的hstack方法。

	>>> a = np.floor(10*np.random.random((2, 2)))
	>>> b = np.floor(10*np.random.random((2, 2)))
	>>> a
	array([[3., 9.],
		[1., 9.]])
	>>> b
	array([[3., 6.],
		[9., 0.]])
	>>> np.column_stack((a, b)) 	# 操作2Darray
	array([[3., 9., 3., 6.],
		[1., 9., 9., 0.]])
	>>> a = np.array([4., 2.])
	>>> b = np.array([3., 8.])
	>>> np.column_stack((a, b)) 	# 返回一个2Darray
	array([[4., 3.],
		[2., 8.]])
	>>> np.hstack((a, b)) 	 	# 与上一条的结果会不一致。
	array([4., 2., 3., 8.])
	>>> a[:,np.newaxis] 	 	# 将一维换成二维
	>>> np.column_stack((a[:, np.newaxis], b[:, np.newaxis]))
	>>> np.hstack((a[:, np.newaxis], b[:, np.newaxis])) 	 	#与上一条命令结果一致,此时等价关系

与column_stack相对的是row_stack函数,row_stack等价于vstack方法。通常,对于那些二维以上的array,
hstack沿着array的第二维(axes)进行叠加,vstack沿着array的第一个维(axes)进行叠加,
concatenate函数允许一个可选参数,给出连接(concatenation)应该沿着哪个轴进行。

切记:复杂情况下,r_和c_方法是一种通过沿着一个轴axis叠加数字来创建array.允许接收范围符号(“:”)
np.r_[1:4, 0, 4, 3] 	 	# out: array([1, 2, 3, 0, 4, 3])

分割数组

hsplit:沿着水平轴进行split,参数:目标array和要分割成的子数组数目或指定在那几列后面进行分割,
分割后生成list,子数组作为list的元素。

>>> a = np.floor(10*np.random.random((2, 12)))
array([[9., 1., 6., 9., 3., 3., 5., 2., 2., 1., 8., 7.],
      [3., 7., 6., 4., 4., 5., 7., 5., 5., 3., 2., 5.]])

>>> np.hsplit(a, 3) 	 	# 将a分割成3个子数组
[array([[9., 1., 6., 9.],
       [3., 7., 6., 4.]]), 
 array([[3., 3., 5., 2.],
       [4., 5., 7., 5.]]), 
 array([[2., 1., 8., 7.],
       [5., 3., 2., 5.]])]

>>> np.hsplit(a, (3, 4))  	# 将a在第3和第4列后分割(分割点分别在第3和第4列后)
[array([[9., 1., 6.],
       [3., 7., 6.]]), 
 array([[9.],
       [4.]]), 
 array([[3., 3., 5., 2., 2., 1., 8., 7.],
       [4., 5., 7., 5., 5., 3., 2., 5.]])]

>>> np.hsplit(a, (3, 7))  #
[array([[9., 1., 6.],
       [3., 7., 6.]]), 
 array([[9., 3., 3., 5.],
       [4., 4., 5., 7.]]), 
 array([[2., 2., 1., 8., 7.],
       [5., 5., 3., 2., 5.]])]

vsplit:沿着垂直轴axis分割数组。
array_aplit:接收指定哪条轴axis来进行分割操作。


数组的副本与视图

在对array进行处理时,array内的数据有时会被复制进一个新的array中进行处理,有时则不会,以下分别就几种情况进行介绍:

  1. 不做复制 (no copy at all)
    简单的复制操作不会对array对象或者array的值进行复制。

    >>> a = np.arange(9)
    >>> b = a 	# 不会有新的对象b被创建
    >>> b is a 	# out: True, a and b 是同一个ndarray对象的两个名称
    >>> b.shape = 3, 3 	# change the shape of a, 与b.reshape((3, 3))(.shape修改对象本身;.reshape返回新对象不修改对象本身), a, b同时指向同一个对象,所以修改b也就是修改a
    >>> a.shape 	 	# out: (3, 4)
    

    Python将可变对象作为引用传递,因此函数调用不进行复制。

    >>> def f(x):
    		print(id(x)) 	 	# id 是一个对象的唯一标识符
    >>> id(a) 	# out:379790104
    >>> f(a) 	# out:379790104
    
  2. 视图或者是浅层复制 (view or shallow copy)
    浅层复制:对象间元素的一一对应复制,新建立的对象的指针指向被复制对象所指向的内存单元,两个对象指向同一个内存单元,与之相对应的是深层复制(deep copy)
    不同的数组对象可以共享相同的数据。view方法创建一个新的数组对象,该对象查看相同的数据。

    >>> c = a.view()
    >>> c is a 			# out:false
    >>> c.base is a 	# out:true, c是a拥有的数据的视图
    >>> c.flags.owndata # out:false
    >>> c.shape=(3,3) 	# 修改对象c的shape,对象a的shape维持不变;同理此时修改对象a的shape,对象c的shape维持不变
    >>> c[0, 0] = 1024 	# 修改对象c中的某位置的值,会同时修改对象a中对应位置的值(当c.shape时,视图c与母体a是互为独立的,当修改c中某个值时视图c与母体a同时改变, 同理修改母体a中某个值时视图c也会做相应改变)
    

    数组切片返回一个视图。修改视图的值会同时修改母体对象的值。

    >>> s = a[:, 2] 	# s是对象a的一个切片
    >>> s[:] = 10 		# s[:]是对象s的一个视图。
    
  3. 深层复制 (deep copy)
    copy方法对数组及其数据进行一次完整复制。相对于view(or 浅层复制),copy创建新的内存空间,新对象和源对象指向不同的内存单元。

    >>> d = a.copy() 	 	# 创建一个包含新数据的新数组对象
    >>> d is a 				# out:false
    >>> d.base is a 	 	# out:false copy后的新对象和母体对象a之间不共用任何值
    >>> d[0, 0] = 100 		# 仅对新对象d进行修改,不会对源对象a做任何修改,新对象d和源对象a互为独立不存在"d引用a的情况",分别指向不同的内存单元,有不同的唯一id标识。
    

    建议:当切片完成后源array不会再被使用时,切片调用copy方法ndarray[start:end].copy()。比如,array_a是一个很大的对象,由array_a切片后生成一个很小的array_b对象,利用切片操作生成array_b后对切片调用copy方法,然后就可以利用del()方法删除不会被再次读取的array_a,释放内存。

    >>> a = np.arange(int(1e8))
    >>> b = a[:100].copy()
    >>> del a or del(a) 	# 删除对象a,释放对象a所占用的内存。
    
    若b=a[:100],则由于b引用了对象a即使执行了del(a),虽然对象已经不存在但是其值仍会驻留在内存中,内存未能成功释放。
    

写在最后

对于基于python的数据科学笔者还是小学生一枚,不敢高谈教化,只希冀将自己的理解能解释清楚。
读到此处的您,如果我的理解对解答您的问题有所帮助,那我将是很开心的。
能力一般,水平有限,可优化的地方千千…请指正!

祝好!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值