为什么学习NumPy
最近在学习机器学习的时候,发现许多地方都需要操作数组,尤其是在做数据预处理的时候,需要对数据去均值、对图像做增强(分割、水平翻转等)。这都需要我们了解python中数组数据处理,而NumPy是python中一个有名的科学计算库,所以我准备系统的学习一下NumPy。
当然我们并不只是学习NumPy这一个软件,Python科学计算生态系统中的其他软件,如数值计算库SciPy、绘图库Matplotlib和各种scikit项目(机器学习中大量用的这个)等。
学习的过程中,需要安装多种软件库,这里我建议直接安装一个Python的发行版,我安装的就是Anaconda(TensorFlow安装也需要),省了很多事。
使用Ipython
IPython是一个工具集合,因为它的Shell而为人熟知,在Shell中,我们可以交互式的学习,想必用惯了matlab或者IDLE,会很习惯使用IPython。现在IPython中基于Web的notebook的一个非常好用的工具,且集成了许多其他功能。IPython中包括各种组件,其中两个主要的组件是:
- 基于终端方式和基于Qt的交互式Python shell
- 支持多媒体和绘图功能的基于web的notebook
打开IPython的shell界面
一般使用IPython做科学计算时,我们会增加pylab选项开关,增加pylab选项可以自动引入SciPy/NumPy和Matplotlib软件包,这样可以方便我们调用。
使用命令
ipython -pylab
保存会话
在使用IPyhon的时候,有时我们可能需要回溯做过的操作,这需要我们开始记录我们的操作,在IPython中我们可以保存会话以供将来查看,这需要使用以下命令:
%logstart #开始记录文件
... # 我们的操作实验
%logoff #停止记录文件
执行系统的shell命令
在调试程序时,我们可以需要使用默认设置的IPython环境中执行系统shell命令时,;;例如获取当前系统时间(可以使用time模块,很麻烦),这时我们可以在交互语句中使用!shell指令,即在系统命令前加!前缀,例如:
!date #使用shell命令 查看当前时间
# 也可以将shell命令执行后的结果保存
thedate = !date
print thedate[0]
显示历史记录
上面我们讲了如果记录操作记录,记录是为了后续的查看使用,这里我们可以使用%hist命令显示操作的历史记录:
%hist
使用%hist -g选项,可以实现对历史记录的搜索
%hist -g xxxx # 对历史记录搜索xxxx
使用notebook
IPython有一个特性-基于Web的notebook,它可以通过Web方式提供notebook界面,我们可以启动一个notebook服务器,获得一个基于Web的IPython运行环境,notebook除了具备常规IPython环境中的大多数特性,还包括以下特性。
- 显示图像和嵌入式图标
- 在文本单元格中使用HTML和Markdown
- notebook的导入和导出
运行notebook
使用如下命令,启动notebook:
ipython notebook
基于web的notebook界面如下(浏览器下):
初试notebook
1. 创建一个notebook对话
在notebook开始页面上,我们可以选中New|Python2 创建一个新的基于Web的Python对话框:
2. 导入pylab模型
上面我们讲到了,在使用IPython做科学计算时,我们会选用pylab选项,当然在notebook中我们也可以选用pylab选项,使用命令如下:
%pylab # 输入完%pylab后,使用shift+enter执行
操作结果如下:
3. 创建一个数组并使用plot绘制
在notebook中键入程序,并使用shift+enter执行程序:
a = range(10)
plot(sqrt(a)) #绘制图片
可以看到,和终端对话框的界面类似,比终端对话框好看的多~!
导出基于Web的notebook
notebook下也可以记录操作指令,用于查看。操作过程是:在File菜单下,可以将notebook工程保存成html或者pdf供别人查看;或者保存成Python或.ipynb(json格式)。
导入基于Web的notebook
相对于记录操作指令,也就会有查看指令,一般的pdf或者html直接打开就可以。还有一些其他文件格式,例如ipynb文件,我们可以使用Home下的Upload将需要导入的文件导入,在目录下打开即可.
SciPy和PIL
上一节我们简单的介绍了NumPy,NumPy以高效率的数组著称,这主要归功于索引的易用性。我们将以图像处理为例展示高级的索引技巧。在深入研究索引之前,我们要先做好准备工作–安装必备的软件SciPy和PIL.
- SciPy:用于科学计算
- PIL:Python Imaging Libraryd的缩写,因为实例中涉及图像处理,为此,我们需要用到Python图像库PIL
安装软件
安装软件 |
安装SciPy
使用python自带工具pip安装
pip install scipy
ubuntu下使用apt安装
sudo apt-get install python-scipy
检查安装是否完成
在python环境中尝试导入scipy包,使用version和file等BIL方法查看scipy包的信息.下图是在notebook中导入scipy包,并查看scipy包信息.
安装PIL
使用Ubuntu下的apt工具安装:
sudo apt-get install python-imaging
使用pip工具安装:
pip install pillow
# Pillow是PIL的一个派生分支,但如今已经发展成为比PIL本身更具活力的图像处理库,且原本的PIL只支持到2.7
检查安装是否完成
在python环境中尝试导入PIL包,使用version和file等BIL方法查看PIL包的信息.下图是在notebook中导入PIL包,并查看PIL包信息.
SciPy和PIL的实例
调整图像大小
调整图像大小 |
1. 加载图像face到数组中
import scipy.misc as misc
face = misc.face()
2. 检查数组的形状
face.shape
可以看到图片的大小是3通道768*1024;
3. 调整数组的大小
我们使用numpy.repeat函数来调整大小,repeat是将数组的元素重复以达到调整数组大小的目的(重复倍数需整数倍)
resized = face.repeat(1,axis=0).repeat(3,axis=1)
# 第一位维度元素不变,第二维度元素重复3遍
函数说明:numpy.repeat(a, repeats, axis=None)
项目 | 含义 |
---|---|
参数 | |
a | int or array of ints. 输入array |
repeats | int 或ints 每个元素的重复次数。重复以适应给定轴的shape |
axis | int, optional 制定要重复的数轴(取值不能超过输入的shape) |
返回值 | 修改后的array |
Examples
>>> np.repeat(3, 4)
array([3, 3, 3, 3])
>>> x = np.array([[1,2],[3,4]])
>>> np.repeat(x, 2)
array([1, 1, 2, 2, 3, 3, 4, 4])
>>> np.repeat(x, 3, axis=1)
array([[1, 1, 1, 2, 2, 2],
[3, 3, 3, 4, 4, 4]])
>>> np.repeat(x, [1, 2], axis=0)
array([[1, 2],
[3, 4],
[3, 4]])
4. 绘制数组对应的图像
matplotlib中的subplot函数用来创建子图。这个函数以一个3位的整数作为参数,最高位代表行数,第二数字代表列数,最后一个数字代表子图的位置索引(从1开始编号)。imshow用来显示图像,show用来显示结果.
matplotlib.pyplot.subplot(121)
matplotlib.pyplot.imshow(face)
matplotlib.pyplot.subplot(122)
matplotlib.pyplot.imshow(resized)
本小节完整代码
import scipy.misc as misc
import matplotlib.pyplot as plot
face = misc.face()
print(face.shape)
resized = face.repeat(1,axis=0).repeat(3,axis=1)
resized.shape
plot.subplot(121)
matplotlib.pyplot.imshow(face)
plot.subplot(122)
plot.imshow(resized)
创建视图和副本
创建视图和副本 |
我们在与一个共享的数组视图打交道时,要注意视图不是只读的,在应用中,一个切片(slice)对应一个视图。如果你把一个切片赋值给一个变量,随后改变了切片所在数组的内容,那么变量的值也会改变。(可以理解为变量是切片的引用)
1.下面我们将创建一张图片的副本,同时创建一张图片的视图
acopy = face.copy()
aview = face.view()
plot.subplot(211)
plot.imshow(acopy)
plot.subplot(212)
plot.imshow(aview)
2.使用flat迭代器,把视图中所有的值清零
如果想对每个数组中元素进行运算,我们可以使用flat属性,该属性是数组元素的一个迭代器.
aview.flat = 0
plot.subplot(211)
plot.imshow(acopy)
plot.subplot(212)
plot.imshow(face)
可以看见,图像已经被清空了
本小节所有的代码
import scipy.misc as misc
import matplotlib.pyplot as plot
face = misc.face()
acopy = face.copy()
aview = face.view()
aview.flat = 0 #使用flat迭代器,把视图中所有的值都清零
plot.subplot(221)
plot.imshow(face) # 处理视图,会影响原图像
plot.subplot(222)
plot.imshow(acopy)
plot.subplot(223)
plot.imshow(aview)
plot.subplot(224)
plot.imshow(aview)
翻转图像
翻转图像 |
有时在图像处理时,我们需要对图像做翻转操作(例如做图像样本增强操作),处了图像翻转,还会绘制出它的一部分以及对其应用遮罩的效果。
1.绘制翻转后的图像
plot.subplot(111)
plot.imshow(face[:,::-1]) #切片操作 列不变 行翻转 图像水片翻转
plot.imshow(face[::-1,:]) #切片操作 行不变 列翻转 图像上下翻转
2.绘制图像的一部分
plot.subplot(111)
plot.imshow(face[:face.shape[0]/2,:face.shape[1]/2]) #行列各取一半
3.对图像应用遮罩的效果
mask = face % 2 ==0 #每个像素点每个通道值是否被2整除
masked_face = face.copy()
masked_face[mask] = 0 # 为true的点像素值置黑
plot.subplot(111)
plot.imshow(masked_face)
本小节所有代码
import scipy.misc as misc
import matplotlib.pyplot as plot
face = misc.face()
plot.subplot(221)
plot.imshow(face)
plot.subplot(222)
plot.imshow(face[:,::-1])
plot.subplot(223)
plot.imshow(face[:face.shape[0]/2,:face.shape[1]/2])
mask = face % 2 ==0
masked_face = face.copy()
masked_face[mask] = 0
plot.subplot(224)
plot.imshow(masked_face)
高级索引
高级索引 |
本节将使用高级索引技术,在图像的对角线位置上的数值值零,效果就是在图像上画两条对角线,没有啥实际意义。与常规索引不同的是,高级索引不使用整数或切片作为索引值。
1.把第一条对角线位置上的数值置为白点
ascent = misc.ascent()
xmax = ascent.shape[0]
ymax = ascent.shape[1]
ascent[range(xmax),range(ymax)] =255 #使用range从0循环到xmax
2.两条对角线的值都置为白点
ascent[range(xmax-1,-1,-1),range(ymax)] =255 #列变量倒序循环
本小节所有代码
import scipy.misc as misc
import matplotlib.pyplot as plot
ascent = misc.ascent()
xmax = ascent.shape[0]
ymax = ascent.shape[1]
ascent[range(xmax),range(ymax)] =255
ascent[range(xmax-1,-1,-1),range(ymax)] =255
plot.subplot(111)
plot.imshow(ascent)
布尔型索引
布尔型索引 |
布尔型索引就是基于布尔数组的索引,属于高级索引技术的范畴。
1.在图像中添加点状的对角线
建立函数get_indices用于创建可以整数4的bool型数组,对图像的处理与先前讲的遮盖类似
def get_indices(size):
arr = numpy.arange(size) # 扩充一个数组,数组值为0-size
return arr % 4 == 0 #返回是的bool型数组
copy = ascent.copy()
xindices = get_indices(ascent.shape[0])
yindices = get_indices(ascent.shape[1])
copy[xindices,yindices] = 255
2.把指定范围内的数值置255(带阻功能)
ascent[(ascent > ascent.max()/4) & (ascent <3 * ascent.max()/4)] = 255 # 将像素值大于最大像素值的1/4小于3/4的像素点设置为白点
本小节所有代码
import scipy.misc as misc
import matplotlib.pyplot as plot
ascent = misc.ascent()
def get_indices(size):
arr = numpy.arange(size) # 扩充一个数组,数组值为0-size
return arr % 4 == 0 #返回是的bool型数组
copy = ascent.copy()
xindices = get_indices(ascent.shape[0])
yindices = get_indices(ascent.shape[1])
copy[xindices,yindices] = 255
plot.subplot(211)
plot.imshow(copy)
ascent[(ascent > ascent.max()/4) & (ascent <3 * ascent.max()/4)] = 255
plot.subplot(212)
plot.imshow(ascent)
参考资料
《NumPy攻略》 -Ivan Idris