前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python进阶之NumPy快速入门(二)

Python进阶之NumPy快速入门(二)

作者头像
HuangWeiAI
发布2019-09-30 15:50:57
9320
发布2019-09-30 15:50:57
举报
文章被收录于专栏:浊酒清味

前言

NumPy是Python的一个扩展库,负责数组和矩阵运行。相较于传统Python,NumPy运行效率高,速度快,是利用Python处理数据必不可少的工具

这个NumPy快速入门系列分为四篇,包含了NumPy大部分基础知识,每篇阅读时间不长,但内容含量高。大家最好亲自码一遍代码,这样可以更有收获。

概要

学会数组的运算,轻松应对数学公式

学会数组的索引,瞬间定位数组位置

学会数组的迭代,快速遍历数组元素

01

NumPy数组运算

基础运算

NumPy数组的基本运算,即加减乘除。我们分成两种情况:

  • 数组形状相同时,即对对应元素进行运算,
  • 数组形状不一致的时候有广播机制来弥补

我们先看两个形状一样的数组基础运算:

代码:

代码语言:javascript
复制
import numpy as np

a = np.array([1, 2, 3])
b = np.arange(10,13)
print (a+b, a-b)
print (a*b, a/b)

讲解:

我们建立了a,b两个一维数组,分别采用直接创建和用arange函数创建的方法。对于同样大小的数组之间的加减乘除运算,运算规则是对位元素一一对应。也就是说a的第一个元素和b的第一个元素进行运算,a的第二个元素和b的第二个元素进行运算,以此类推,所有对位的元素进行运算。

运行结果:

[11 13 15] [-9 -9 -9]

[10 22 36] [0.1 0.18181818 0.25]

广播机制

如果a,b两个数组的形状(shape)并不一样,那么运算规则又是什么样子的呢?Numpy对于两个不同形状的数组的运算采用一种叫做广播(broadcast)的机制负责运算:

代码:

代码语言:javascript
复制
a = np.array([[1, 2, 3],[4, 5, 6]])
b = np.arange(0,3)
print (a+b)

讲解:

a是一个2*3的数组,而b的形状是1*3,广播机制会让他们之间的加法得到一个相对合理的结果:

运行结果:

[[1 3 5]

[4 6 8]]

不难发现广播让a中第一个维度[1,2,3]加上b之后成为结果的第一个维度,让a中的第二个维度[4,5,6]加上之后成为结果的第二个维度。广播的规律总结起来有以下几点:

  1. 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。
  2. 输出数组的形状是输入数组形状的各个维度上的最大值。
  3. 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。
  4. 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。

对于NumPy的广播,我给大家的建议是会多少用多少,尽量不要超出自己知识范围内使用。

高级运算:

  • 乘方:numpy.power,用法是numpy(a,n),其中a是NumPy数组,n是幂
  • 取余:numpy.mod(a,b),数组a对于数组b除法后取余数。
  • 三角函数:numpy.sin(), numpy.cos()等

代码:

代码语言:javascript
复制
a = np.array([1, 2, 3])
b = np.arange(10,13)
print (a**2, np.power(a,2))
print (np.sin(a))
print (np.mod(b,a))

讲解:

我们建立了a,b两个数组,第一个运算是求a每个元素的平方,有两种方法实现,二者结果相同。第二个运算,我们尝试了一下三角函数中的正弦函数。最后,我们用数组b对于数组a取余运算,除了11对于2取余等于1之外,其余都是0。

运行结果:

[1 4 9] [1 4 9]

[0.84147098 0.90929743 0.14112001]

[0 1 0]

02

NumPy索引

索引就是像是GPS导航,可以直接到数组中的特定位置的元素。我们把数组的索引按方式不同分成两种,然后分别介绍:

  • 数字索引
  • 布尔(条件)索引

数字索引

数字索引,顾名思义,就是根据数字来定位数组中元素,这个十分好理解。我们将数字索引分成两种方式:

  1. 单个数字索引
  2. 范围数字索引

对于一维数组,单个数字索引和列表方法一样。比如我们有一个数组A,那么A[x]就是索引A数组中的第x个元素,这里切记x从0开始计数,所以准确来讲是索引第x+1个元素。

对于二维的NumPy数组,我们也可以用一维索引的方法,这时我们会索引出某一行。

代码:

代码语言:javascript
复制
import numpy as np
A = np.arange(0,12)
print (A[3])
A = A.reshape((2,6))
print (A[1])

讲解:

我们首先建立了一个0到11的数组A,我们试图索引它的第四个元素。接着我们利用了一个变形技术reshape把A转换成一个二维数组,然后用一维索引得到变形后的第二行所有元素。

运行结果:

3

[ 6 7 8 9 10 11]

单个数字也可以扩展到二维甚至更高维度,例如对于二维数组索引方式一般可以写成A[1,1]或者A[1][1]。

现在我们着重介绍一下用冒号进行范围索引,因为我们有时候想要一段的数组,这时候范围索引就显得很方便实用。具体而言,有两种方式:

  • a:b,从a位置出发到b位置结束。
  • a:b:c,a是起始值,b是终止值,c是步长。

代码:

代码语言:javascript
复制
A = np.eye(5)
print (A[2,2], A[2][2])
print (A[1][0:4:2])

讲解:

我们首先用numpy.eye()函数建立了一个5乘以5的单位矩阵。先测试一下二维索引中单体索引,A[2,2]和A[2][2]两种方式都是可以的。接着我们测试一下范围索引,第一个[1]表示A矩阵的第二行:[0 1 0 0 0];后面的[0:4:2]其实只能索引出来两个数字,就是0和3两个位置上的数字。

运行结果:

1.0 1.0

[0. 0.]

布尔索引

这是一种通过布尔(逻辑)运算来获得符合条件元素的索引方式。简单来说,你可以通过给定一定的条件,筛选出满足条件的元素。这种索引方式是我们日常使用Numpy数组较为常用和使用的方法。

代码:

代码语言:javascript
复制
A = np.arange(0,9).reshape(3,3)
B = np.array([1,  2+6j,  5,  3.5+5j])
print (A>5)
print (A[A>5])
print (B[np.iscomplex(B)])

讲解:

我们先用两行代码给大家展示一下布尔索引的运算过程,第18行代码其实才是完整的操作,打印出A数组中大于5的元素,以一个一维数组的形式数出来。第17代码其实给出布尔运算的一步,输出结果为:大于5的位置是True而小于5的位置是False,接着通过真假关系带入A数组,最终把真的元素挑出来。这就是布尔索引的运算过程。B是一个打印出复数元素的例子,原理是一样的。

03

数组迭代

这一节课我们尝试用循环的方式,遍历数组中所有元素。考虑到常见的数组往往不止一个维度,因此单纯用while和for循环写起来很费事,所以我们有必要学习NumPy自带的遍历方法。

迭代数组 nditer

Numpy自带一个数组迭代器,叫nditer,可以让我们灵活访问数组中元素:

代码:

代码语言:javascript
复制
import numpy as np
A = np.arange(0,12).reshape(3,4)
for n in np.nditer(A):
 print (n, end=' ')

讲解:

我们照例创建了一个形状为(3,4)的二维数组A,利用nditer配合for循环的格式,依次迭代访问数组A中的元素。注意到在print函数中,我们给参数end赋值了一个空格字符串,目的是让打印出来的元素可以被空格间隔。

运行结果:

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

大家可以尝试一下给end赋值别的字符串,例如逗号,换行等等。

控制顺序

事实上,nditer有一个参数来控制遍历顺序。这个参数叫order,有两个值可以选择:

  • 如果order='C',那么就会按行优先的顺序访问;
  • 如果order='F',那么则会按列顺序优先访问。

我们来看个例子:

代码:

代码语言:javascript
复制
B = np.arange(0,9).reshape(3,3)
for n in np.nditer(B,order='C'):
 print (n, end=' ')
print ('\n')   
for n in np.nditer(B, order='F'):
 print (n, end=' ') 

讲解:

正如我们上面所说,'C'和'F'分别代表行和列优先。值得一提的这里的C,F并不是我们常见的row和column的缩写,而是代码C语言标准格式和Fortran格式,二者都是一种程序语言。

运行结果:

0 1 2 3 4 5 6 7 8

0 3 6 1 4 7 2 5 8

修改元素

nditer在遍历数组的时候,给我们提供了一个读写的选项,也就是说,我们根据这个读写开关可以改变数组的数值。这个参数叫做op_flags,默认值是只读模式'read-only',此时不可以修改元素。但是我们可以让op_flags赋值'readwrite' 或者 'writeonly':

代码:

代码语言:javascript
复制
C = np.arange(0,8).reshape(2,4)
for n in np.nditer( C,op_flags=['writeonly'] ):
 n[...] = 2*n
print (C) 

讲解:

我们利用'writeonly'将遍历的读写模式变成只写模式,大家也可以尝试'readwrite'一下看看效果如何。对于每个元素,我们都让它扩大两倍。有一点,我们用了n[...]格式,让乘以两倍后的元素重新赋值回去,[...]不可或缺。

运行结果:

[[ 0 2 4 6]

[ 8 10 12 14]]

总结回顾

1

学习了基础运算,以及当数组形状不一致时候的广播机制;高级运算。

2

学习用数字和逻辑索引两种基本数组索引方式。

3

学习了数组的迭代器,以及迭代顺序的控制。

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

本文分享自 Python与机器学习之路 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档