简易目录:
1 NumPy数组基础:数组的创建、属性、索引、切分、变形、拼接与分裂
2 NumPy数组的计算:通用函数、广播功能
3 比较、掩码和布尔逻辑:和通用函数类似的比较操作、操作布尔数组
4 花哨的索引:有关索引、组合索引
5 数组的排序
6 结构化数组
1 NumPy数组基础
1.1 数组的创建
最直接的:np.array([1, 2, 3])
创建一个长度为10、值都是0的数组:np.zeros(10, dtype = int)
创建一个3×5、值都是1的浮点型数组:np.ones((3, 5), dtype = float)
创建一个3×5、值都是3.14的浮点型数组:np.full((3, 5), 3.14)
创建一个从0开始、到20结束、步长为2的数组:np.arange(0, 20, 2)
创建一个5个数均匀地分配到0~1的数组:np.linspace(0, 1, 5)
创建一个3×3、在0~1均匀分布的随机数组成的数组:np.random.random((3, 3))
创建一个3×3、均值为0方差为1的正态分布的随机数数组:np.random.normal(0,1,(3,3))
创建一个3×3、[0, 10)区间的随机整型数组:np.random.randint(0, 10, (3, 3))
创建一个3×3的单位矩阵:np.eye(3)
利用现有的数据来创建:np.asarray(x, dtype = None, order = None)
1.2 数组的属性
每个数组有ndim(数组的维度)、shape(数组每个维度的大小)、size(数组的总大小)属性,其他属性包括每个数组元素字节大小的itemsize,以及表示数组总字节大小的nbytes,一般来说,可以认为nbytes 跟itemsize 和size 的乘积大小相等。
1.3 数组的索引
一位数组:
通过中括号指定索引获取第i 个值(从0 开始计数),如x1[0]
为了获取数组的末尾索引,可以用负值索引:x1[-1]
多维数组:
用逗号分隔的索引元组获取元素,如x2[2, 0]
1.4 数组的切分
用切片(slice)符号获取子数组,切片符号用冒号(:)表示:x[start:stop:step]
获取数组的行和列:将索引与切片组合起来实现,如x2[:, 0]
表示x2的第一列
数组切片返回的是数组数据的视图,而不是数组数据的副本,如果修改这个子数组,原始数组也会被修改。在处理非常大的数据集时,可以获取或处理这些数据集的片段,而不用复制底层的数据缓存。
如果想明确复制数组里的数据或子数组,可以通过copy()
方法实现,如x2[:2, :2].copy()
1.5 数组的变形
数组变形最灵活的实现方式是通过reshape()
函数实现。
另一种常见的变形模式是将一个一维数组转变为二维的行或列的矩阵,也可以通过reshape()方法实现,或者在一个切片操作中利用newaxis
关键字:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16In[1]: x = np.array([1, 2, 3])
In[2]: x.reshape((1, 3))
out[2]: array([[1, 2, 3]])
In[3]: x[np.newaxis, :]
out[3]: array([[1, 2, 3]])
In[4]: x.reshape((3, 1))
out[4]: array([[1],
[2],
[3]])
In[5]: x[:, np.newaxis]
out[5]: array([[1],
[2],
[3]])
数组上的一维迭代器:flat()
返回折叠为一维的数组副本:flatten()
,参数order有'C'
按行,'F'
按列,'A'
原顺序,'K'
元素在内存中的出现顺序
返回连续的展开数组:ravel()
1
2
3
4
5
6
7
8
9
10
11
12In[6]: a = np.arange(8).reshape(2, 4)
a
Out[6]: [[0 1 2 3]
[4 5 6 7]]
In[7]: a.flat[5] #返回展开数组中的下标的对应元素
Out[7]: 5
In[8]: a.flatten(order = 'F')
Out[8]: [0 4 1 5 2 6 3 7]
In[9]: a.ravel(order = 'F')
Out[9]: [0 4 1 5 2 6 3 7]
1.6 数组的拼接与分裂
数组的拼接:np.concatenate()
、np.vstack()
(垂直)、np.hstack()
(水平)
数组的分裂:np.split()
、np.hsplit()
、np.vsplit()
,可以向这些函数传递一个索引列表作为参数,索引列表记录的是分裂点的位置,如np.split(x, [3, 5])
2 NumPy数组的计算
NumPy可以通过通用函数和广播功能进行向量化操作以减少缓慢的Python 循环。
2.1 通用函数
2.1.1 通用函数基础
NumPy 中的向量操作是通过通用函数实现的。通用函数的主要目的是对NumPy 数组中的值执行更快的重复操作。
通用函数有两种存在形式:一元通用函数(unary ufunc)对单个输入操作,二元通用函数(binary ufunc)对两个输入操作。
NumPy实现的算术运算符及其对应的通用函数:
绝对值:np.absolute()
或np.abs()
三角函数:sin()
、cos()
、tan()
、arcsin()
、arccos()
、arctan()
指数和对数:^
、np.log()
、np.log2()
、np.log10()
还有一个更加专用,也更加晦涩的通用函数优异来源是子模块scipy.special。
2.1.2 高级的通用函数特性
1.指定输出:
所有的通用函数都可以通过out 参数来指定计算结果的存放位置,如np.multiply(x, 10, out=y)
2.聚合:reduce
方法会对给定的元素和操作重复执行,直至得到单个的结果。如np.add.reduce(x)
会返回数组中所有元素的和,如果需要存储每次计算的中间结果,可以使用accumulate
,如np.add.accumulate(x)
。同理对对np.multiply()
通用函数也一样。
注意:在一些特殊情况中,NumPy 提供了专用的函数(np.sum、np.prod、np.cumsum、np.cumprod),它们也可以实现以上reduce 的功能。
对于min、max、sum 和其他NumPy聚合,一种更简洁的语法形式是数组对象直接调用这些方法,如x.sum()
。
在多维数组中,还可以通过axis参数指定沿着哪个轴的方向进行聚合。axis关键字指定的是数组将会被折叠的维度,而不是将要返回的维度。因此指定axis=0
意味着第一个轴将要被折叠——对于二维数组,这意味着每一列的值都将被聚合。axis=1
指定每一行,如x.max(axis=1)
找到每一行的最大值。
3.外积:
任何通用函数都可以用outer
方法获得两个不同输入数组所有元素对的函数运算结果。这意味着你可以用一行代码实现一个乘法表。如np.multiply.outer(x, x)
。
2.2 广播
广播可以简单理解为用于不同大小数组的二进制通用函数(加、减、乘等)的一组规则。
对于同样大小的数组,二进制操作是对相应元素逐个计算,而广播则允许这些二进制操作可以用于不同大小的数组。下图是对一些广播例子进行的可视化:
广播规则:
-规则1:如果两个数组的维度数不相同,那么小维度数组的形状将会在最左边补1。
-规则2:如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度为1 的维度扩展以匹配另外一个数组的形状。
-规则3:如果两个数组的形状在任何一个维度上都不匹配并且没有任何一个维度等于1,那么会引发异常。
来看两个数组均需要广播的示例:
现在来看一个两个数组不兼容的示例:
3 比较、掩码和布尔逻辑
3.1 和通用函数类似的比较操作
3.2 操作布尔数组
1.统计记录的个数
统计布尔数组中True记录的个数:np.count_nonzero()
、np.sum()
快速检查任意或者所有这些值是否为True:np.any()
、np.all()
沿着特定的坐标轴可加参数axis:每行axis=1
,每列axis=0
2.布尔运算符
逐位逻辑运算符:&
、|
、^
、~
,需要加上括号表示运算优先级规则
3.将布尔数组作为掩码
将符合某个条件的值从数组中选出,可以进行简单的索引,即掩码操作,如x[x < 5]
,返回的值是掩码数组对应位置为True的值。
4 花哨的索引
4.1 有关索引
简单的索引值:如x[0]
;切片,如x[:5]
;布尔掩码,如x[x > 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#单一维度:
In[10]: x = [51 92 14 71 60 20 82 86 74 74]
ind = [3, 7, 4]
x[ind]
Out[10]: array([71, 86, 60])
In[11]: ind = np.array([[3, 7],
[4, 5]])
x[ind]
Out[11]: array([[71, 86],
[60, 20]])
#多个维度
In[12]: X = np.arange(12).reshape((3, 4))
X
Out[12]: array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In[13]: row = np.array([0, 1, 2])
col = np.array([2, 1, 3])
X[row, col]
Out[13]: array([ 2, 5, 11])
In[14]: X[row[:, np.newaxis], col]
Out[14]: array([[ 2, 1, 3],
[ 6, 5, 7],
[10, 9, 11]])
4.2 组合索引
花哨的索引可以和其他索引结合起来形成更强大的索引操作:1
2
3
4
5
6
7
8
9
10
11
12
13#与简单索引的组合:
In[15]: X[2, [2, 0, 1]]
Out[15]: array([10, 8, 9])
#与切片的组合:
In[16]: X[1:, [2, 0, 1]]
Out[16]: array([[ 6, 4, 5],
[10, 8, 9]])
#与掩码的组合:
In[17]: mask = np.array([1, 0, 1, 0], dtype=bool)
X[row[:, np.newaxis], mask]
Out[17]: array([[ 0, 2],
[ 4, 6],
[ 8, 10]])
5 数组的排序
1.快速排序
在不修改原始输入数组的基础上返回一个排好序的数组:np.sort(x)
用排好序的数组替代原始数组:x.sort()
返回原始数组排好序的索引值:np.argsort(x)
,索引值可以通过花哨的索引创建有序的数组
沿着行或列排序:axis参数,将行或列当做独立的数组,任何行列的值之间的关系将会丢失
2.部分排序:分隔
找到数组中第K小的值:np.partition(x, k)
,输出新数组的最左边是第K小的值,往右是任意顺序的其他值
6 结构化数组
结构化数组的生成:字典的方法,或者元组列表1
2
3
4
5
6
7
8
9
10In[18]: np.dtype({'names': ('name', 'age', 'weight'),
'formats': ('U10', 'i4', 'f8')})
Out[18]: dtype([('name', '<U10'), ('age', '<i4'), ('weight', '<f8')])
#为了简明起见,数值数据类型可以用Python 类型或NumPy 的dtype 类型指定:
In[19]: np.dtype({'names': ('name', 'age', 'weight'),
'formats': ((np.str_, 10), int, np.float32)})
Out[19]: dtype([('name', '<U10'), ('age', '<i8'), ('weight', '<f4')])
#元组列表
In[20]: np.dtype([('name', 'S10'), ('age', 'i4'), ('weight', 'f8')])
Out[20]: dtype([('name', 'S10'), ('age', '<i4'), ('weight', '<f8')])
- 本笔记参考书目:Jake VanderPlas的《Python数据科学手册》
- 更详细版