科学计算基础软件包NumPy入门讲座:操作数组
Posted 天元浪子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了科学计算基础软件包NumPy入门讲座:操作数组相关的知识,希望对你有一定的参考价值。
文章目录
1. 索引和切片
NumPy数组对象的内容可以通过索引或切片来访问和修改。对于一维数组的索引和切片,NumPy数组和Python的列表一样灵活。
a = np.arange(9)
>>> a[-1] # 最后一个元素
8
>>> a[2:5] # 返回第2到第5个元素
array([2, 3, 4])
>>> a[:7:3] # 返回第0到第7个元素,步长为3
array([0, 3, 6])
>>> a[::-1] # 返回逆序的数组
array([8, 7, 6, 5, 4, 3, 2, 1, 0])
对于多维数组操作,NumPy数组比 Python的列表更加灵活、强大。假设有一栋2层楼,每层楼内的房间都是3行4列,那我们可以用一个三维数组来保存每个房间的居住人数(当然,也可以是房间面积等其他数值信息)。
>>> a = np.arange(24).reshape(2,3,4) # 2层3行4列
>>> a
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[1][2][3] # 虽然可以这样
23
>>> a[1,2,3] # 但这才是规范的用法
23
>>> a[:,0,0] # 所有楼层的第1排第1列
array([ 0, 12])
>>> a[0,:,:] # 1楼的所有房间,等价与a[0]或a[0,...]
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> a[:,:,1:3] # 所有楼层所有排的第2到4列
array([[[ 1, 2],
[ 5, 6],
[ 9, 10]],
[[13, 14],
[17, 18],
[21, 22]]])
>>> a[1,:,-1] # 2层每一排的最后一个房间
array([15, 19, 23])
提示:
- 对多维数组切片或索引得到的结果,维度不是确定的;
- 切片返回的数组不是原始数据的副本,而是指向与原始数组相同的内存区域。数组切片不会复制内部数组数据,只是产生了原始数据的一个新视图。
>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> b = a[1:,2:] # 数组b是数组a的切片
>>> b
array([[ 6, 7],
[10, 11]])
>>> b[:,:] = 99 # 改变数组b的值,也会同时影响数组a
>>> b
array([[99, 99],
[99, 99]])
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 99, 99],
[ 8, 9, 99, 99]])
2. 改变结构
NumPy数组的存储顺序和数组的视图是相互独立的,因此改变数组的维度是非常便捷的操作,这一类操作不会改变所操作的数组本身的存储顺序, resize() 除外。
- reshape() - 按照指定的结构(形状)返回数组的新视图,但不会改变数组
resize()
- 按照指定的结构(形状)改变数组
,无返回值- ravel() - 返回多维数组一维化的视图,但不会改变原数组
- transpose() - 返回行变列的视图,但不会改变原数组
- rollaxis() - 翻滚轴,返回新的视图
>>> a = np.arange(12)
>>> b = a.reshape((3,4)) # reshape()返回数组a的一个新视图,但不会改变数组a
>>>> a.shape
(12,)
>>> b.shape
(3, 4)
>>> b is a
False
>>> b.base is a
True
a.resize([4,3]) # resize()则真正改变了数组a的结构
>>> a.shape
(4, 3)
>>> a.ravel() # 返回多维数组一维化的视图,但不会改变原数组
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> a.transpose() # 返回行变列的视图,但不会改变原数组
array([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
>>> a.T # 返回行变列的视图,等价于transpose()
array([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
>>> np.rollaxis(a, 1, 0) # 翻滚轴,1轴变0轴
array([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
3. 合并与拆分
NumPy数组一旦创建就不能再改变其元素数量了。如果要动态改变数组元素数量,只能通过合并或者拆分的方法,生成新的数组。对于刚刚上手NumPy的程序员来说,最大的困惑就是不能使用append() 方法向数组内添加元素,甚至连 append() 方法都找不到了。其实,NumPy仍然保留了append() 方法,只不过这个方法不再是NumPy数组的方法,而是是升级到最外层的NumPy命名空间,并且该方法的功能不再是追加元素,而是合并数组。
>>> np.append([[1, 2, 3]], [[4, 5, 6]])
array([1, 2, 3, 4, 5, 6])
>>> np.append([[1, 2, 3]], [[4, 5, 6]], axis=0)
array([[1, 2, 3],
[4, 5, 6]])
>>> np.append([[1, 2, 3]], [[4, 5, 6]], axis=1)
array([[1, 2, 3, 4, 5, 6]])
不过,这个append()委实不够好用,我给大家推荐的是stack()方法。
>>> a = np.arange(4).reshape(2,2)
>>> b = np.arange(4,8).reshape(2,2)
>>> np.hstack((a,b)) # 水平合并
array([[0, 1, 4, 5],
[2, 3, 6, 7]])
>>> np.vstack((a,b)) # 垂直合并
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
>>> np.dstack((a,b)) # 深度合并
array([[[0, 4],
[1, 5]],
[[2, 6],
[3, 7]]])
stack 函数原型为 stack(arrays, axis=0),请注意体会下面例子中的 axis 的用法。
>>> a = np.arange(60).reshape(3,4,5)
>>> b = np.arange(60).reshape(3,4,5)
>>> a.shape, b.shape
>>> np.stack((a,b), axis=0).shape
(2, 3, 4, 5)
>>> np.stack((a,b), axis=1).shape
(3, 2, 4, 5)
>>> np.stack((a,b), axis=2).shape
(3, 4, 2, 5)
>>> np.stack((a,b), axis=3).shape
(3, 4, 5, 2)
因为数组切片非常简单,所以数组拆分应用较少。拆分是合并的逆过程,最常用的方法是split()。
>>> a = np.arange(8).reshape(2,4)
>>> np.vsplit(a, 2) # 垂直方向拆分成2部分
[array([[0, 1, 2, 3]]), array([[4, 5, 6, 7]])]
>>> np.hsplit(a, 2) # 水平方向拆分成2部分
[array([[0, 1],
[4, 5]]), array([[2, 3],
[6, 7]])]
4. 复制
改变数组结构返回的是原元数据的一个新视图,而不是原元数据的副本。浅复制(view)和深复制(copy)则是创建原数据的副本,但二者之间也有细微差别:浅复制(view)是共享内存,深复制(copy)则是独享。
>>> a = np.arange(6).reshape((2,3))
>>> b = a.view()
>>> b is a
False
>>> b.base is a
False
>>> b.flags.owndata
False
>>> c = a.copy()
>>> c is a
False
>>> c.base is a
False
>>> c.flags.owndata
True
5. 排序
NumPy 数组排序函数有两个,一个是sort(),一个是argsort()。sort()返回输入数组的排序副本,argsort()返回的是数组值从小到大的索引号。从函数原型看,这两个函数的参数是完全一样的。
numpy.sort(a, axis=-1, kind=‘quicksort’, order=None)
numpy.argsort(a, axis=-1, kind=‘quicksort’, order=None)
- a - 要排序的数组
- axis - 沿着它排序数组的轴,如果没有,则沿着最后的轴排序
- kind - 排序方法,默认为’quicksort’(快速排序),其他选项还有 ‘mergesort’(归并排序)和 ‘heapsort’(堆排序)
- order - 如果数组包含字段,则是要排序的字段
>>> a = np.random.random((2,3))
>>> a
array([[0.79658569, 0.14507096, 0.63016223],
[0.24983103, 0.98368325, 0.71092079]])
>>> np.argsort(a) # 返回行内从小到大排序的索引序号(列排序),相当于axis=1(最后的轴)
array([[1, 2, 0],
[0, 2, 1]], dtype=int64)
>>> np.sort(a) # 返回行内从小到大排序的一个新数组(列排序)
array([[0.14507096, 0.63016223, 0.79658569],
[0.24983103, 0.71092079, 0.98368325]])
>>> np.sort(a,axis=0) # 返回列内每一行都是从小到大排序(行排序)
array([[0.24983103, 0.14507096, 0.63016223],
[0.79658569, 0.98368325, 0.71092079]])
我们再看看排序字段的使用。先定义一个新的数据类型dt:dt类似于一个字典,有两个键值对,一个是姓名name,一个是年龄age,姓名长度10个字符,年龄是整型。
>>> dt = np.dtype([('name', 'S10'),('age', int)])
>>> a = np.array([("zhang",21),("wang",25),("li", 17), ("zhao",27)], dtype = dt)
>>> np.sort(a, order='name') # 如果指定姓名排序,结果是李王张赵
array([(b'li', 17), (b'wang', 25), (b'zhang', 21), (b'zhao', 27)],
dtype=[('name', 'S10'), ('age', '<i4')])
>>> np.sort(a, order='age') # 如果指定年龄排序,结果则是李张王赵
array([(b'li', 17), (b'zhang', 21), (b'wang', 25), (b'zhao', 27)],
dtype=[('name', 'S以上是关于科学计算基础软件包NumPy入门讲座:操作数组的主要内容,如果未能解决你的问题,请参考以下文章