前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >盘一盘 NumPy (上)

盘一盘 NumPy (上)

作者头像
石晓文
发布于 2019-07-17 01:53:53
发布于 2019-07-17 01:53:53
3K00
代码可运行
举报
文章被收录于专栏:小小挖掘机小小挖掘机
运行总次数:0
代码可运行

0

引言

Numpy 是 Python 专门处理高维数组 (high dimensional array) 的计算的包,每次使用它遇到问题都会它的官网 (www.numpy.org). 去找答案。在使用 numpy 之前,需要引进它,语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import numpy

这样你就可以用 numpy 里面所有的内置方法 (build-in methods) 了,比如求和与均值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
numpy.sum()
numpy.mean()

但是每次写 numpy 字数有点多,通常我们给 numpy 起个别名 np,用以下语法,这样所有出现numpy 的地方都可以用 np 替代。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import numpy as np

为什么要专门学习数组呢?看下面「numpy 数组」和「列表」之间的计算效率对比:两个大小都是 1000000,把每个元素翻倍,运行 10 次用 %time 记时。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
my_arr = np.arange(1000000)
my_list = list(range(1000000))
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
%time for _ in range(10): my_arr2 = my_arr * 2
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Wall time: 48.9 ms
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Wall time: 1.33 s

我们发现「numpy 数组」效率是「列表」效率的 27 (1.33*1000/48.9) 倍左右。如果元素全是数值型变量 (numerical variable),那么 numpy 数组明显是个很好的数据结构

学习 numpy 还是遵循的 Python 里「万物皆对象」的原则,既然把数组当对象,我们就按着数组的创建、数组的存载、数组的获取、数组的变形、和数组的计算来盘一盘 NumPy,目录如下:

有些读者可能会说,NumPy 都什么好学的,数组都弄不懂的人还能干什么,那我来问你个问题,知道「转置操作」吧,那么下面这个二维数组转置后是什么?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr = np.array([[1,2,3],[4,5,6]])
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1, 2, 3], [4, 5, 6]])

太简单了,是 [[1,4], [2,5], [3,6]],来看看是不是。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr.T
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1, 4], [2, 5], [3, 6]])

答对了,你牛,再看一道转置的题

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr = np.arange(16).reshape((2, 2, 4))
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[[ 0, 1, 2, 3],
        [ 4, 5, 6, 7]],

       [[ 8, 9, 10, 11],
        [12, 13, 14, 15]]])

等等,现在有三维,转置通常不是转两个维度吗?转三个维度也可以?当然,比如把第 1, 2, 3 维度转置到第 2, 1, 3 维度,可以用 transpose 函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr.transpose(1,0,2)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[[ 0, 1, 2, 3],
        [ 8, 9, 10, 11]],

       [[ 4, 5, 6, 7],
        [12, 13, 14, 15]]])

如果不知道上面答案怎么来的,我觉得你还是有必要看看本帖的。由于篇幅原因,NumPy 系列也分两贴,上贴讲前三节的内容,下帖讲后两节的内容。

1

数组的创建

1.1

初次印象

数组 (array) 是相同类型的元素 (element) 的集合所组成数据结构 (data structure)。numpy 数组中的元素用的最多是「数值型」元素,平时我们说的一维、二维、三维数组长下面这个样子 (对应着线、面、体)。四维数组很难被可视化。

注意一个关键字 axis,中文叫「轴」,一个数组是多少维度就有多少根轴。由于 Python 计数都是从 0 开始的,那么

  • 第 1 维度 = axis 0
  • 第 2 维度 = axis 1
  • 第 3 维度 = axis 2

但这些数组只可能在平面上打印出来,那么它们 (高于二维的数组) 的表现形式稍微有些不同。

分析上图各个数组的在不同维度上的元素:

  • 一维数组:轴 0 有 3 个元素
  • 二维数组:轴 0 有 2 个元素,轴 1 有 3 个元素
  • 三维数组:轴 0 有 2 个元素 (2 块),轴 1 有 2 个元素,轴 2 有 3 个元素
  • 四维数组:轴 0 有 2 个元素 (2 块),轴 1 有 2 个元素 (2 块),轴 2 有 2 个元素,轴 3 有 3 个元素

2.1

创建数组

带着上面这个对轴的认识,接下来我们用代码来创建 numpy 数组,有三种方式:

  1. 按步就班的 np.array() 用在列表和元组上
  2. 定隔定点的 np.arange() 和 np.linspace()
  3. 一步登天的 np.ones(), np.zeros(), np.eye() 和 np.random.random()

按步就班法

给了「列表」和「元组」原材料,用 np.array() 包装一下便得到 numpy 数组。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
l = [3.5, 5, 2, 8, 4.2]
np.array(l)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([3.5, 5. , 2. , 8. , 4.2])
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
t = (3.5, 5, 2, 8, 4.2)
np.array(t)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([3.5, 5. , 2. , 8. , 4.2])

注意,numpy 数组的输出都带有 array() 的字样,里面的元素用「中括号 []」框住。

定隔定点法

更常见的两种创建 numpy 数组方法:

  • 定隔的 arange:固定元素大小间隔
  • 定点的 linspace:固定元素个数

先看 arange 例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print( np.arange(8) )
print( np.arange(2,8) )
print( np.arange(2,8,2))
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[0 1 2 3 4 5 6 7]
[2 3 4 5 6 7]
[2 4 6]

函数 arange 的参数为起点 , 终点 , 间隔

arange(start , stop , step)

其中 stop 必须要有,start 和 step 没有的话默认为 1。对着这个规则看看上面各种情况的输出。

注:用函数 print 打印 numpy 数组就没有 array() 的字样了,只用其内容,而且元素之间的「逗号」也没有了。

再看 linspace 的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print( np.linspace(2,6,3) )
print( np.linspace(3,8,11) )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[2. 4. 6.]
[3. 3.5 4. 4.5 5. 5.5 6. 6.5 7. 7.5 8. ]

函数 linspace 的参数为起点 , 终点 , 点数

linspace (start , stop , num)

其中 start 和 stop 必须要有,num 没有的话默认为 50。对着这个规则看看上面各种情况的输出。

一步登天法

NumPy 还提供一次性

  • 用 zeros() 创建全是 0 的 n 维数组
  • 用 ones() 创建全是 1 的 n 维数组
  • 用 random() 创建随机 n 维数组
  • 用 eye() 创建对角矩阵 (二维数组)

对于前三种,由于输出是 n 为数组,它们的参数是一个「标量」或「元组类型的形状」,下面三个例子一看就懂了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print( np.zeros(5) ) # 标量5代表形状(5,)
print( np.ones((2,3)) )
print( np.random.random((2,3,4)) )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[0. 0. 0. 0. 0.]

[[1. 1. 1.]
 [1. 1. 1.]]

[[[0.15684866 0.33684519 0.85095027 0.67827412]
  [0.58041935 0.12639616 0.33509142 0.99561644]
  [0.59581471 0.92043399 0.56731046 0.76811703]]

 [[0.74276133 0.85278489 0.32392871 0.40553182]
  [0.7718898 0.35496469 0.20061144 0.00351225]
  [0.49957334 0.48449498 0.62835324 0.29610557]]]

对于函数 eye(),它的参数就是一个标量,控制矩阵的行数或列数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.eye(4)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])

此外还可以设定 eye() 里面的参数 k

  • 默认设置 k = 0 代表 1 落在对角线上
  • k = 1 代表 1 落在对角线右上方
  • k = -1 代表 1 落在对角线左下方
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.eye(4, k=1)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.],
       [0., 0., 0., 0.]])

1.3

数组性质

还记得 Python 里面「万物皆对象」么?numpy 数组也不例外,那么我们来看看数组有什么属性 (attributes) 和方法 (methods)。

一维数组

用按步就班的 np.array() 带列表生成数组 arr

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr = np.array([3.5, 5, 2, 8, 4.2])
arr

现在你应该会用 dir(arr) 来查看数组的属性了吧,看完之后我们对 type, ndim, len(), size, shape,stride, dtype 几个感兴趣,一把梭打印出来看看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print( 'The type is', type(arr) )
print( 'The dimension is', arr.ndim )
print( 'The length of array is', len(arr) )
print( 'The number of elements is', arr.size )
print( 'The shape of array is', arr.shape )
print( 'The stride of array is', arr.strides )
print( 'The type of elements is', arr.dtype )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
The type is <class 'numpy.ndarray'>
The dimension is 1
The length of array is 5
The number of elements is 5
The shape of array is (5,)
The stride of array is (8,)
The type of elements is float64

根据结果我们来看看上面属性到底是啥:

  • type:数组类型,当然是 numpy.ndarray
  • ndim:维度个数是 1
  • len():数组长度为 5 (注意这个说法只对一维数组有意义)
  • size:数组元素个数为 5
  • shape:数组形状,即每个维度的元素个数 (用元组来表示),只有一维,元素个数为 5,写成元组形式是 (5,)
  • strides:跨度,即在某一维度下为了获取到下一个元素需要「跨过」的字节数 (用元组来表示),float64 是 8 个字节数 (bytes),因此跨度为 8
  • dtype:数组元素类型,是双精度浮点 (注意和 type 区分)

注意我黄色高亮了 strides,这个概念对于解决引言的「转置高维数组」问题很重要。一图胜千言。

咦,为什么有个 Python View 和 Memory Block 啊?这两个不是一样的么?对一维数组来说,「Python 视图」看它和「内存块」存储它的形式是一样的,但对二维数组甚至高维数组呢?

二维数组

还是用按步就班的 np.array() 带二维列表生成二维数组 arr2d

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
l2 = [[1, 2, 3], [4, 5, 6]]
arr2d = np.array(l2)
arr2d
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1, 2, 3],
       [4, 5, 6]])

一把梭打印属性出来看看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print( 'The type is', type(arr2d) )
print( 'The dimension is', arr2d.ndim )
print( 'The length of array is', len(arr2d) )
print( 'The number of elements is', arr2d.size )
print( 'The shape of array is', arr2d.shape )
print( 'The stride of array is', arr2d.strides )
print( 'The type of elements is', arr2d.dtype )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
The type is <class 'numpy.ndarray'>
The dimension is 2
The length of array is 2
The number of elements is 6
The shape of array is (2, 3)
The stride of array is (12, 4)
The type of elements is int32

同样,我们来分析一下上面属性:

  • type:数组类型 numpy.ndarray
  • ndim:维度个数是 2
  • len():数组长度为 2 (严格定义 len 是数组在「轴 0」的元素个数)
  • size:数组元素个数为 6
  • shape:数组形状 (2, 3)
  • strides:跨度 (12, 4) 看完下图再解释
  • dtype:数组元素类型 int32

对于二维数组,Python 视图」看它和「内存块」存储它的形式是不一样的,如下图所示:

numpy 数组中,默认的是行主序 (row-major order),意思就是每行的元素在内存块中彼此相邻,而列主序 (column-major order) 就是每列的元素在内存块中彼此相邻。

回顾跨度 (stride) 的定义,即在某一维度下为了获取到下一个元素需要「跨过」的字节数。注:每一个 int32 元素是 4 个字节数。对着上图:

  • 第一维度 (轴 0):沿着它获取下一个元素需要跨过 3 个元素,即 12 = 3×4 个字节
  • 第二维度 (轴 1):沿着它获取下一个元素需要跨过 1 个元素,即 4 = 1×4 个字节

因此该二维数组的跨度为 (12, 4)。

n 维数组

用 np.random.random() 来生成一个多维数组:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr4d = np.random.random( (2,2,2,3) )

里面具体元素是什么不重要,一把梭 arr4d 的属性比较重要:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print( 'The type is', type(arr4d) )
print( 'The dimension is', arr4d.ndim )
print( 'The length of array is', len(arr4d) )
print( 'The number of elements is', arr4d.size )
print( 'The shape of array is', arr4d.shape )
print( 'The stride of array is', arr4d.strides )
print( 'The type of elements is', arr4d.dtype )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
The type is <class 'numpy.ndarray'>
The dimension is 4
The length of array is 2
The number of elements is 24
The shape of array is (2, 2, 2, 3)
The stride of array is (96, 48, 24, 8)
The type of elements is float64

除了 stride,都好理解,请根据下图好好想想为什么 stride 是 (96, 48, 24, 8)?[Hint: 一个 float64 的元素占 8 个字节]

算了还是分析一下吧 (免得掉粉

)。回顾跨度 (stride) 的定义,即在某一维度下为了获取到下一个元素需要「跨过」的字节数。注:每一个 float64 元素是 8 个字节数

  • 第一维度 (轴 0):沿着它获取下一个元素需要跨过 12 个元素,即 96 = 12×8 个字节
  • 第二维度 (轴 1):沿着它获取下一个元素需要跨过 6 个元素,即 48 = 6×8 个字节
  • 第三维度 (轴 2):沿着它获取下一个元素需要跨过 3 个元素,即 24 = 3×8 个字节
  • 第四维度 (轴 3):沿着它获取下一个元素需要跨过 1 个元素,即 8 = 1×8 个字节

因此该四维数组的跨度为 (96, 48, 24, 8)。

留一道思考题,strides 和 shape 有什么关系?

strides = (96, 48, 24, 8)

shape = (2, 2, 2, 3)

总不能每个高维数组都用可视化的方法来算 strides 把。

2

数组的存载

本节讲数组的「保存」和「加载」,我知道它们没什么技术含量,但是很重要。假设你已经训练完一个深度神经网络,该网络就是用无数参数来表示的。比如权重都是 numpy 数组,为了下次不用训练而重复使用,将其保存成 .npy 格式或者 .csv 格式是非常重要的。

numpy 自身的 .npy 格式

用 np.save 函数将 numpy 数组保存为 .npy 格式,具体写法如下:

np.save( ‘’文件名”,数组 )

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr_disk = np.arange(8)
np.save("arr_disk", arr_disk)
arr_disk

arr_disk.npy 保存在 Jupyter Notebook 所在的根目录下。要加载它也很简单,用 np.load( "文件名" ) 即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.load("arr_disk.npy")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([0, 1, 2, 3, 4, 5, 6, 7])

文本 .txt 格式

用 np.savetxt 函数将 numpy 数组保存为 .txt 格式,具体写法如下:

np.save( ‘’文件名”,数组 )

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr_text = np.array([[1., 2., 3.], [4., 5., 6.]])
np.savetxt("arr_from_text.txt", arr_text)

arr_from_text.txt 保存在 Jupyter Notebook 所在的根目录下,用 Notepad 打开看里面确实存储着 [[1,2,3], [4,5,6]]。

用 np.loadtxt( "文件名" ) 即可加载该文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.loadtxt("arr_from_text.txt")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1., 2., 3.],
       [4., 5., 6.]])

文本 .csv 格式

另外,假设我们已经在 arr_from_csv 的 csv 文件里写进去了 [[1,2,3], [4,5,6]],每行的元素是由「分号 ;」来分隔的,展示如下:

用 np.genfromtxt( "文件名" ) 即可加载该文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.genfromtxt("arr_from_csv.csv")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([nan, nan])

奇怪的是数组里面都是 nan,原因是没有设定好「分隔符 ;」,那么函数 genfromtxt 读取的两个元素是

  • 1;2;3
  • 4;5;6

它们当然不是数字拉,Numpy 只能用两个 nan (Not a Number) 来代表上面的四不像了。

带上「分隔符 ;」再用 np.genfromtxt( "文件名",分隔符 ) 即可加载该文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.genfromtxt("arr_from_csv.csv", delimiter=";")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1., 2., 3.],
       [4., 5., 6.]])

3

数组的获取

获取数组是通过索引 (indexing) 和切片 (slicing) 来完成的,

  • 切片是获取一段特定位置的元素
  • 索引是获取一个特定位置的元素

索引和切片的方式和列表一模一样,参考 Python 入门篇 (上) 的 2.3 节。对于一维数组 arr,

  • 切片写法是 arr[start : stop : step]
  • 索引写法是 arr[index]

因此,切片的操作是可以用索引操作来实现的 (一个一个总能凑成一段),只是没必要罢了。为了简化,我们在本章三节标题里把切片和索引都叫做索引。

索引数组有三种形式,正规索引 (normal indexing)、布尔索引 (boolean indexing) 和花式索引 (fancy indexing)。

3.1

正规索引

虽然切片操作可以由多次索引操作替代,但两者最大的区别在于

  • 切片得到的是原数组的一个视图 (view) ,修改切片中的内容改变原数组
  • 索引得到的是原数组的一个复制 (copy),修改索引中的内容不会改变原数组

请看下面一维数组的例子来说明上述两者的不同。

一维数组

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr = np.arange(10)
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

用 arr[6] 索引第 7 个元素 (记住 Python 是从 0 开始记录位置的)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr[6]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
6

把它赋给变量 a,并重新给 a 赋值 1000,但是元数组 arr 第 7 个元素的值还是 6,并没有改成 1000。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
a = arr[6]
a = 1000
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

用 arr[5:8] 切片第 6 到 8 元素 (记住 Python 切片包头不包尾)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr[5:8]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([5, 6, 7])

把它赋给变量 b,并重新给 b 的第二个元素赋值 12,再看发现元数组 arr 第 7 个元素的值已经变成 12 了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
b = arr[5:8]
b[1] = 12
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([ 0, 1, 2, 3, 4, 5, 12, 7, 8, 9])

这就证实了切片得到原数组的视图 (view),更改切片数据会更改原数组,而索引得到原数组的复制 (copy), 更改索引数据不会更改原数组。希望用下面一张图可以明晰 view 和 copy 的关系。

了解完一维数组的切片和索引,类比到二维和多维数组上非常简单。

二维数组

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

索引

情况一:用 arr2d[2] 来索引第三行,更严格的说法是索引「轴 0」上的第三个元素。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([7, 8, 9])

情况二:用 arr2d[0][2] 来索引第一行第三列

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[0][2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
3

索引二维数组打了两个中括号好麻烦,索引五维数组不是要打了五个中括号?还有一个简易方法,用 arr2d[0, 2] 也可以索引第一行第三列

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[0,2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
3

切片

情况一:用 arr2d[:2] 切片前两行,更严格的说法是索引「轴 0」上的前两个元素。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[:2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1, 2, 3],
       [4, 5, 6]])

情况二:用 arr2d[:, [0,2]] 切片第一列和第三列

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[:,[0,2]]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[1, 3],
       [4, 6],
       [7, 9]])

情况三:用 arr2d[1, :2] 切片第二行的前两个元素

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[1, :2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([4, 5])

情况四:用 arr2d[:2, 2] 切片第三列的前两个元素

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr2d[:2, 2]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([3, 6])

3.2

布尔索引

布尔索引,就是用一个由布尔 (boolean) 类型值组成的数组来选择元素的方法。

假设我们有阿里巴巴 (BABA),脸书 (FB) 和京东 (JD) 的

  • 股票代码 code 数组
  • 股票价格 price 数组:每行记录一天开盘最高收盘价格。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
code = np.array(['BABA', 'FB', 'JD', 'BABA', 'JD', 'FB'])
price = np.array([[170,177,169],[150,159,153],
                  [24,27,26],[165,170,167],
                  [22,23,20],[155,116,157]])
price
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[170, 177, 169],
       [150, 159, 153],
       [ 24, 27, 26],
       [165, 170, 167],
       [ 22, 23, 20],
       [155, 161, 157]])

假设我们想找出 BABA 对应的股价,首先找到 code 里面是 'BABA' 对应的索引 (布尔索引),即一个值为 True 和 False 的布尔数组。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
code == 'BABA'
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([ True, False, False, True, False, False])

用该索引可以获取 BABA 的股价:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
price[ code == 'BABA' ]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[170, 177, 169],
       [165, 170, 167]])

用该索引还可以获取 BABA 的最高和收盘价格:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
price[ code == 'BABA', 1: ]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[177, 169],
       [170, 167]])

再试试获取 JD 和 FB 的股价:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
price[ (code == 'FB')|(code == 'JD') ]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[150, 159, 153],
       [ 24, 27, 26],
       [ 22, 23, 20],
       [155, 161, 157]])

虽然下面操作没有实际意义,试试把股价小于 25 的清零。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
price[ price < 25 ] = 0
price
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[170, 177, 169],
       [150, 159, 153],
       [ 0, 27, 26],
       [165, 170, 167],
       [ 0, 0, 0],
       [155, 161, 157]])

注:这种布尔索引的操作在 Pandas 更常用也更方便,看完 pandas 那帖后就可以忽略这一节了。

3.3

花式索引

花式索引是获取数组中想要的特定元素的有效方法。考虑下面数组:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr = np.arange(32).reshape(8,4)
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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, 25, 26, 27],
       [28, 29, 30, 31]])

假设你想按特定顺序来获取第 5, 4 和 7 行时,用 arr[ [4,3,6] ]

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr[ [4,3,6] ]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[16, 17, 18, 19],
       [12, 13, 14, 15],
       [24, 25, 26, 27]])

假设你想按特定顺序来获取倒数第 4, 3 和 6 行时 (即正数第 4, 5 和 2 行),用 arr[ [-4,-3,-6] ]

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr[ [-4,-3,-6] ]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[16, 17, 18, 19],
       [20, 21, 22, 23],
       [ 8, 9, 10, 11]])

此外,你还能更灵活的设定「行」和「列」中不同的索引,如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr[ [1,5,7,2], [0,3,1,2] ]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([ 4, 23, 29, 10])

检查一下,上行代码获取的分别是第二行第一列、第六行第四列、第八行第二列、第三行第三列的元素,它们确实是 4, 23, 29 和 10。如果不用花式索引,就要写下面繁琐但等价的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
np.array( [ arr[1,0], arr[5,3], 
arr[7,1], arr[2,2] ] )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([ 4, 23, 29, 10])

最后,我们可以把交换列,把原先的 [0,1,2,3] 的列换成 [0,3,1,2]。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr[:,[0,3,1,2]]
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[ 0, 3, 1, 2],
       [ 4, 7, 5, 6],
       [ 8, 11, 9, 10],
       [12, 15, 13, 14],
       [16, 19, 17, 18],
       [20, 23, 21, 22],
       [24, 27, 25, 26],
       [28, 31, 29, 30]])

4

总结

本帖讨论了 NumPy 的前三节,数组创建、数组存载和数组获取。同样把 numpy 数组当成一个对象,要学习它,无非就是学习怎么

  • 创建它:按步就班法、定隔定点法、一步登天法
  • 存载它:保存成 .npy, .txt 和 .csv 格式,下次加载即用
  • 获取它:一段用切片,一个用索引;有正规法、布尔法、花式法

等等,你好像还没教什么 numpy 数组硬核的东西呢,下帖讨论 NumPy 的后两节就教怎么

  • 变形它:重塑和打平,合并和分裂,元素重复和数组重复
  • 计算它:元素层面计算,线性代数计算,广播机制计算

回到引言的「数组转置」问题:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr = np.arange(16).reshape((2, 2, 4))
arr
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[[ 0, 1, 2, 3],
        [ 4, 5, 6, 7]],

       [[ 8, 9, 10, 11],
        [12, 13, 14, 15]]])

第 1, 2, 3 维度转置到第 2, 1, 3 维度,即将轴 0, 1, 2 转置到轴 1, 0, 2

解答:

数组转置的本质:交换每个轴 (axis) 的形状 (shape) 和跨度 (stride)。

四幅图解决问题:

原数组

内存块的样子

轴 0 和轴 1 互换

转置结果

用代码验证一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
arr.transpose(1,0,2)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
array([[[ 0, 1, 2, 3],
        [ 8, 9, 10, 11]],

       [[ 4, 5, 6, 7],
        [12, 13, 14, 15]]])

上篇盘点结束,敬请关注下篇。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小小挖掘机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
盘一盘 Python 系列 2 - NumPy (下)
重塑 (reshape) 和打平 (ravel, flatten) 这两个操作仅仅只改变数组的维度
用户5753894
2019/07/05
2.6K0
盘一盘 Python 系列 2 - NumPy (下)
Numpy 笔记-基础篇
除非显式说明,np.array会尝试为新建的这个数组判断一个较为合适的数据类型。数据类型保存在特殊的dtype对象中。比如上面的两个例子中。我们有:
Ewdager
2020/07/14
4450
Numpy基础知识点汇总
1、概述 Numpy是高性能科学计算和数据分析的基础包,它的部分功能如下: 1)ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。 2)对整组数据进行快速运算的标准数学函数 3)用于读写磁盘数据的工具以及用于操作内存映射文件的工具。 2、ndarray的创建 这一节,我们主要关注ndarray数组的创建,我们主要有以下几种方式: 数组转换 创建数组的最简单的方法就是使用array函数,将Python下的list转换为ndarray。 #通过数组创建一个ndarray data1
石晓文
2018/04/11
1.6K0
Python 数据处理:NumPy库
✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的博客 🍊个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 🥭本文内容:Python 数据处理:NumPy库 ---- Python 数据处理:NumPy库 1.NumPy简介 2.NumPy的ndarray:一种多维数组对象 2.1 创建ndarray 2.2 ndarray的数据类型 2.3 NumPy数组的运算 2.4 基本的索引和切片 2.5 切片索引 2.6 布尔型索引 2
小嗷犬
2022/11/15
5.8K0
Python 数据处理:NumPy库
NumPy教程(Numpy基本操作、Numpy数据处理)
介绍几种 numpy 的属性: • ndim:维度 • shape:行数和列数 • size:元素个数 使用numpy首先要导入模块
用户7886150
2021/01/05
1.7K0
Numpy教程第1部分 - 阵列简介(常用基础操作总结)
【导读】这里是numpy教程的基础部分,涵盖了使用numpy的ndarrays执行数据操作和分析的一些操作。众所周知,Numpy是Python中最基本和最强大的科学计算和数据处理软件包,下面是关于专知
WZEARW
2018/04/08
8070
Numpy教程第1部分 - 阵列简介(常用基础操作总结)
清晰易懂的Numpy入门教程
Numpy是python语言中最基础和最强大的科学计算和数据处理的工具包,如数据分析工具pandas也是基于numpy构建的,机器学习包scikit-learn也大量使用了numpy方法。本文介绍了Numpy的n维数组在数据处理和分析的所有核心应用。
Python数据科学
2019/08/28
1.6K0
AI基础:Numpy简易入门
NumPy(Numeric Python)提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库。专为进行严格的数字处理而产生。多为很多大型金融公司使用,以及核心的科学计算组织如:Lawrence Livermore,NASA 用其处理一些本来使用 C++,Fortran 或 Matlab 等所做的任务。
Ai学习的老章
2019/12/05
7290
初探Numpy中的花式索引
Numpy中对数组索引的方式有很多(为了方便介绍文中的数组如不加特殊说明指的都是Numpy中的ndarry数组),比如:
触摸壹缕阳光
2020/04/08
2.4K0
初探Numpy中的花式索引
猿创征文|数据导入与预处理-第2章-numpy
numpy作为高性能科学计算和数据分析的基础包,它是众多数据分析、机器学习等工具的基础架构,掌握numpy的功能及其用法将有助于后续其他数据分析工具的学习。
IT从业者张某某
2022/11/12
5.9K0
猿创征文|数据导入与预处理-第2章-numpy
numpy使用
主要是针对《利用python进行数据分析-第二版》进行第三次的学习 将其中关于numpy和pandas的部分代码进行整理 numpy import numpy as np my_arr = np.arange(1000000) my_list = list(range(1000000)) %time for _ in range(10): my_arr2 = my_arr * 2 CPU times: user 21.2 ms, sys: 11.2 ms, total: 32.4 ms Wall t
皮大大
2021/03/02
6410
《利用Python进行数据分析·第2版》第4章 NumPy基础:数组和矢量计算4.1 NumPy的ndarray:一种多维数组对象4.2 通用函数:快速的元素级数组函数4.3 利用数组进行数据处理4.
NumPy(Numerical Python的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。 NumPy的部分功能如下: ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。 用于对整组数据进行快速运算的标准数学函数(无需编写循环)。 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。 线性代数、随机数生成以及傅里叶变换功能。 用于集成由C、C++、Fortran等语言编写的代码的A C API。 由于NumPy提供了一个
SeanCheney
2018/04/24
5K0
《利用Python进行数据分析·第2版》第4章 NumPy基础:数组和矢量计算4.1 NumPy的ndarray:一种多维数组对象4.2 通用函数:快速的元素级数组函数4.3 利用数组进行数据处理4.
【数据分析从入门到“入坑“系列】利用Python学习数据分析-Numpy中的索引
NumPy数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多。一维数组很简单。从表面上看,它们跟Python列表的功能差不多:
天道Vax的时间宝藏
2021/08/11
1.6K0
【Python数据分析】NumPy基础,看这一篇就够了!
NumPy是Python的一种开源的数值计算扩展库,包含很多功能,如创建n维数组(矩阵)、对数组进行函数计算、数学计算等等。
Skrrapper
2025/05/09
1780
【Python数据分析】NumPy基础,看这一篇就够了!
科学计算Python库:Numpy入门
NumPy 是 Python 中科学计算的基础包。它是一个 Python 库,提供多维数组对象、各种派生对象(例如掩码数组和矩阵)以及用于对数组进行快速操作的各种例程,包括数学、逻辑、形状操作、排序、选择、I/O 、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等等。科学计算离不开numpy,学习数据分析必先学numpy!!! 本文由浅入深,对numpy进行入门介绍。讲解了创建数组、索引数组、运算等使用。
Crayon鑫
2023/10/10
5030
科学计算Python库:Numpy入门
NumPy之:ndarray多维数组操作
NumPy一个非常重要的作用就是可以进行多维数组的操作,多维数组对象也叫做ndarray。我们可以在ndarray的基础上进行一系列复杂的数学运算。
程序那些事
2021/05/19
1.1K0
NumPy 1.26 中文官方指南(二)
NumPy(Numerical Python)是一个开源的 Python 库,几乎在每个科学和工程领域中都被使用。它是 Python 中处理数值数据的通用标准,在科学 Python 和 PyData 生态系统的核心地位不可撼动。NumPy 的用户包括从初学者程序员到经验丰富的从事最前沿的科学和工业研究与开发的研究人员。NumPy API 在 Pandas、SciPy、Matplotlib、scikit-learn、scikit-image 和大多数其他数据科学和科学 Python 软件包中得到广泛应用。
ApacheCN_飞龙
2024/04/26
7500
NumPy 1.26 中文官方指南(二)
NumPy 1.26 中文官方指南(一)
NumPy 是 Python 中科学计算的基础包。 这是一个提供多维数组对象、各种派生对象(如掩码数组和矩阵)以及一系列用于数组快速操作的例程的 Python 库,包括数学、逻辑、形状操作、排序、选择、I/O、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等。
ApacheCN_飞龙
2024/04/26
1.4K0
NumPy 1.26 中文官方指南(一)
NumPy知识速记
由于NumPy提供了一个简单易用的C API,因此很容易将数据传递给由低级语言编写的外部库,外部库也能以NumPy数组的形式将数据返回给Python。这个功能使Python成为一种包装C/C++/Fortran历史代码库的选择,并使被包装库拥有一个动态的、易用的接口。
timerring
2023/05/07
1.2K0
NumPy知识速记
Python Numpy基础教程
本文是一个关于Python numpy的基础学习教程,其中,Python版本为Python 3.x
oYabea
2020/09/07
8580
相关推荐
盘一盘 Python 系列 2 - NumPy (下)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档