盘一盘 Python 系列 2 - NumPy (下)(一)

简介: 盘一盘 Python 系列 2 - NumPy (下)(一)

本文首发于“生信补给站”公众号  https://mp.weixin.qq.com/s/0-9zUTxZC6qefyjLyw1SSA



本文是 Python 系列的第四篇


  • Python 入门篇 (上)
  • Python 入门篇 (下)
  • 数组计算之 NumPy (上)
  • 数组计算之 NumPy (下)
  • 科学计算之 SciPy
  • 数据结构之 Pandas
  • 基本可视化之 Matplotlib
  • 统计可视化之 Seaborn
  • 交互可视化之 Bokeh
  • 炫酷可视化之 PyEcharts
  • 机器学习之 Sklearn
  • 深度学习之 TensorFlow
  • 深度学习之 Keras
  • 深度学习之 PyTorch
  • 深度学习之 MXnet


接着上篇继续后面两个章节,数组变形和数组计算。






4数组的变形


本节介绍四大类数组层面上的操作,具体有


  1. 重塑 (reshape) 和打平 (ravel, flatten)
  2. 合并 (concatenate, stack) 和分裂 (split)
  3. 重复 (repeat) 和拼接 (tile)
  4. 其他操作 (sort, insert, delete, copy)


4.1 重塑和打平



重塑 (reshape) 和打平 (ravel, flatten) 这两个操作仅仅只改变数组的维度


  • 重塑是从低维到高维
  • 打平是从高维到低维



重塑


reshape()函数将一维数组 arr 重塑成二维数组。




arr = np.arange(12)print( arr )print( arr.reshape((4,3)) )
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[[ 0 1 2]
 [ 3 4 5]
 [ 6 7 8]
 [ 9 10 11]]


思考:为什么重塑后的数组不是


[[ 0 4 8]
 [ 1 5 9]
  [ 2 6 10]
   [ 3 7 11]]


当你重塑高维矩阵时,不想花时间算某一维度的元素个数时,可以用「-1」取代,程序会自动帮你计算出来。比如把 12 个元素重塑成 (2, 6),你可以写成 (2,-1) 或者 (-1, 6)。



print( arr.reshape((2,-1)) )print( arr.reshape((-1,6)) )
[[ 0 1 2 3 4 5]
 [ 6 7 8 9 10 11]]
[[ 0 1 2 3 4 5]
 [ 6 7 8 9 10 11]]




打平


ravel()flatten() 函数将二维数组 arr 打平成一维数组。


arr = np.arange(12).reshape((4,3))print( arr )
ravel_arr = arr.ravel()print( ravel_arr )
flatten_arr = arr.flatten()print( flatten_arr )
[[ 0 1 2]
 [ 3 4 5]
 [ 6 7 8]
 [ 9 10 11]]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]


思考:为什么打平后的数组不是


[ 0 3 6 9 1 4 7 10 2 5 8 11]


要回答本节两个问题,需要了解 numpy 数组在内存块的存储方式。




行主序和列主序


行主序 (row-major order) 指每行的元素在内存块中彼此相邻,而列主序 (column-major order) 指每列的元素在内存块中彼此相邻。


在众多计算机语言中,


  • 默认行主序的有 C 语言(下图 order=‘C’ 等价于行主序)
  • 默认列主序的有 Fortran 语言(下图 order=‘F’ 等价于列主序)



numpy 数组中,默认的是行主序,即 order ='C'。现在可以回答本节那两个问题了。


如果你真的想在「重塑」和「打平」时用列主序,只用把 order 设为 'F',以重塑举例:


print( arr.reshape((4,3), order='F') )
[[ 0 1 2]
 [ 3 4 5]
 [ 6 7 8]
 [ 9 10 11]]




细心的读者可能已经发现为什么「打平」需要两个函数 ravel()flatten()?它们的区别在哪里?





知识点

函数 ravel() flatten() 的不同之处是


  1. ravel() 按「行主序」打平时没有复制原数组,按「列主序」在打平时复制了原数组
  2. flatten() 在打平时复制了原数组




用代码验证一下,首先看 flatten(),将打平后的数组 flatten 第一个元素更新为 10000,并没有对原数组 arr 产生任何影响 (证明 flatten() 复制了原数组)







arr = np.arange(6).reshape(2,3)print( arr )flatten = arr.flatten()print( flatten )flatten_arr[0] = 10000print( arr )
[[0 1 2]
 [3 4 5]]
[0 1 2 3 4 5]
[[0 1 2]
 [3 4 5]]



再看 ravel() 在「列主序」打平,将打平后的数组 ravel_F 第一个元素更新为 10000,并没有对原数组 arr 产生任何影响 (证明 ravel(order='F')复制了原数组)





ravel_F = arr.ravel( order='F' )ravel_F[0] = 10000print( ravel_F )print( arr )
[10000 3 1 4 2 5]
[[0 1 2]
 [3 4 5]]



最后看 ravel() 在「行主序」打平,将打平后的数组 ravel_C 第一个元素更新为 10000,原数组 arr[0][0] 也变成了 10000 (证明 ravel() 没有复制原数组)





ravel_C = arr.ravel()ravel_C[0] = 10000print( ravel_C )print( arr )


[10000 1 2 3 4 5]
[[10000 1 2]
 [ 3 4 5]]





4.2 合并和分裂



合并 (concatenate, stack) 和分裂 (split) 这两个操作仅仅只改变数组的分合


  • 合并是多合一
  • 分裂是一分多



合并


使用「合并」函数有三种选择


  1. 有通用的 concatenate
  2. 有专门的 vstack, hstack, dstack
  3. 有极简的 r_, c_


用下面两个数组来举例:



arr1 = np.array([[1, 2, 3], [4, 5, 6]])arr2 = np.array([[7, 8, 9], [10, 11, 12]])


concatenate


np.concatenate([arr1, arr2], axis=0)np.concatenate([arr1, arr2], axis=1)
[[ 1 2 3]
 [ 4 5 6]
 [ 7 8 9]
 [10 11 12]]
[[ 1 2 3 7 8 9]
 [ 4 5 6 10 11 12]]


concatenate() 函数里通过设定轴,来对数组进行竖直方向合并 (轴 0) 和水平方向合并 (轴 1)。


vstack, hstack, dstack

通用的东西是好,但是可能效率不高,NumPy 里还有专门合并的函数


  • vstack:v 代表 vertical竖直合并,等价于 concatenate(axis=0)
  • hstack:h 代表 horizontal水平合并,等价于 concatenate(axis=1)
  • dstack:d 代表 depth-wise,按深度合并,深度有点像彩色照片的 RGB 通道


一图胜千言:



用代码验证一下:


print( np.vstack((arr1, arr2)) )print( np.hstack((arr1, arr2)) )print( np.dstack((arr1, arr2)) )
[[ 1 2 3]
 [ 4 5 6]
 [ 7 8 9]
 [10 11 12]]
-----------------------
[[ 1 2 3 7 8 9]
 [ 4 5 6 10 11 12]]
-----------------------
[[[ 1 7]
  [ 2 8]
  [ 3 9]]
 [[ 4 10]
  [ 5 11]
  [ 6 12]]]


和 vstack, hstack 不同,dstack 将原数组的维度增加了一维。


np.dstack((arr1, arr2)).shape
(2, 3, 2)


r_, c_

此外,还有一种更简单的在竖直和水平方向合并的函数,r_()c_()



print( np.r_[arr1,arr2] )print( np.c_[arr1,arr2] )
[[ 1 2 3]
 [ 4 5 6]
 [ 7 8 9]
 [10 11 12]]
[[ 1 2 3 7 8 9]
 [ 4 5 6 10 11 12]]


除此之外,r_()c_() 有什么特别之处么?(如果完全和 vstack() hstack() 一样,那也没有存在的必要了)





知识点

1. 参数可以是切片。


print( np.r_[-2:2:1, [0]*3, 5, 6] )


[-2 -1 0 1 0 0 0 5 6]


2. 第一个参数可以是控制参数,如果它用 'r' 或 'c' 字符可生成线性代数最常用的 matrix (和二维 numpy array 稍微有些不同)


np.r_['r', [1,2,3], [4,5,6]]


matrix([[1, 2, 3, 4, 5, 6]])


3. 第一个参数可以是控制参数,如果它写成 ‘a,b,c’ 的形式,其中
a:代表轴,按「轴 a」来合并b:合并后数组维度至少是 bc:在第 c 维上做维度提升
看不懂吧?没事,先用程序感受一下:



print( np.r_['0,2,0', [1,2,3], [4,5,6]] )print( np.r_['0,2,1', [1,2,3], [4,5,6]] )print( np.r_['1,2,0', [1,2,3], [4,5,6]] )print( np.r_['1,2,1', [1,2,3], [4,5,6]] )


[[1]
 [2]
 [3]
 [4]
 [5]
 [6]]
----------------
[[1 2 3]
 [4 5 6]]
----------------
[[1 4]
 [2 5]
 [3 6]]
----------------
[[1 2 3 4 5 6]]


还看不懂吧 (但至少知道完事后的维度是 2,即字符串 ‘a,b,c’ 的 b 起的作用 )?没事,我再画个图。



还没懂彻底吧?没事,我再解释下。

字符串 ‘a,b,c’ 总共有四类,分别是

  • '0, 2, 0'
  • '0, 2, 1'
  • '1, 2, 0'
  • '1, 2, 1'



函数里两个数组 [1,2,3], [4,5,6] 都是一维

  • c = 0 代表在「轴 0」上升一维,因此得到 [[1],[2],[3]] 和 [[4],[5],[6]]
  • c = 1 代表在轴 1」上升一维因此得到 [[1,2,3]] 和 [[4,5,6]]



接下来如何合并就看 a 的值了

  • a = 0, 沿着「轴 0」合并
  • a = 1, 沿着「轴 1」合并
相关文章
|
7月前
|
Python
见鬼了Python
见鬼了Python
28 0
见鬼了Python
|
3月前
|
存储 索引 Python
30天拿下Python之numpy模块
30天拿下Python之numpy模块
23 0
|
3月前
|
API Python
30天拿下Python之matplotlib模块
30天拿下Python之matplotlib模块
16 0
|
4月前
|
移动开发 缓存 前端开发
秀啊,90行Python代码开发个人云盘应用
秀啊,90行Python代码开发个人云盘应用
|
存储 机器学习/深度学习 数据可视化
盘一盘 Python 系列 2 - NumPy (上)(二)
盘一盘 Python 系列 2 - NumPy (上)(二)
233 0
盘一盘 Python 系列 2 - NumPy (上)(二)
|
索引 Python
盘一盘 Python 系列 2 - NumPy (上)(三 )
盘一盘 Python 系列 2 - NumPy (上)(三 )
109 0
|
机器学习/深度学习 Python
盘一盘 Python 系列 2 - NumPy (下)(四)
盘一盘 Python 系列 2 - NumPy (下)(四)
104 0
|
机器学习/深度学习 数据可视化 PyTorch
盘一盘 Python 系列 2 - NumPy (上)(一)
盘一盘 Python 系列 2 - NumPy (上)(一)
|
机器学习/深度学习 Serverless Python
盘一盘 Python 系列 2 - NumPy (下)(三)
盘一盘 Python 系列 2 - NumPy (下)(三)
121 0
|
索引 Python
盘一盘 Python 系列 2 - NumPy (下)(二)
盘一盘 Python 系列 2 - NumPy (下)(二)