此处对Python的基本使用方法做一个简单的介绍。限于篇幅,本文只针对本书涉及的数据挖掘案例所用到的代码进行基本讲解。如果你是初步接触Python,并且使用Python的目的就是数据挖掘,那么本节的介绍已经够用了。如果你需要进一步了解Python,或者需要运行更加复杂的任务,那么请自行阅读相应的Python教程。
本节示例代码使用的Python版本为Python 3.6。运行Python代码有两种方式:一种方式是启动Python,然后在命令窗口下直接输入相应的命令;另一种方式是将完整的代码写成.py脚本,如hello.py,然后在对应的路径下通过python hello.py执行。hello.py脚本中的代码如下:
脚本的执行结果如图2-3所示。
图2-3 hello.py脚本执行结果
在编写脚本的时候,可以添加适当的注释。在每一行中,可以用井号“#”来添加注释,添加单行注释的方法如下:
如果注释有多行,可以在两个“'''”(三个英文状态单引号)之间添加注释内容。添加多行注释的方法如下:
如果脚本中带有中文(中文注释或者中文字符串),那么需要在文件头注明编码,还要将脚本保存为utf-8编码格式。注明编码的方法如下:
初步认识Python时,可以把它当作一个方便的计算器。读者可以打开Python,试着输入代码清单2-1所示的命令。
代码清单2-1 Python基本运算
代码清单2-1所示的命令是Python的几个基本运算,第一个命令是赋值运算,第二个命令是乘法运算,最后一个命令是幂运算(即a 2 ),这些基本上是所有编程语言通用的。不过Python支持多重赋值,方法如下:
这句多重赋值命令相当于如下命令:
Python支持对字符串的灵活操作,如代码清单2-2所示。
代码清单2-2 Python字符串操作
判断与循环是所有编程语言的基本命令,Python的判断语句格式如下:
需要特别指出的是,Python一般不用花括号{},也没有end语句,它用缩进对齐作为语句的层次标记。同一层次的缩进量要一一对应,否则会报错。下面是一个错误的缩进示例,如代码清单2-3所示。
代码清单2-3 错误的缩进
不管是哪种语言,正确的缩进都是一个优雅的编程习惯。
相应的,Python的循环有while循环和for循环,while循环如代码清单2-4所示。
代码清单2-4 while循环
for循环如代码清单2-5所示。
代码清单2-5 for循环
这里我们看到了in和range语法。in是一个非常方便而且非常直观的语法,用来判断一个元素是否在列表/元组中;range用来生成连续的序列,一般语法为range(a,b,c),表示以a为首项、c为公差且不超过b-1的等差数列,如代码清单2-6所示。
代码清单2-6 使用range生成等差数列
Python用def来自定义函数,如代码清单2-7所示。
代码清单2-7 自定义函数
与一般编程语言不同的是,Python的函数返回值可以是各种形式,如返回列表,甚至返回多个值,如代码清单2-8所示。
代码清单2-8 返回列表和返回多个值的自定义函数
有时候,用def来正式地定义add2()这类简单的函数显得稍有点麻烦。Python支持用lambda对简单的功能定义“行内函数”,这有点像MATLAB中的“匿名函数”,如代码清单2-9所示。
代码清单2-9 使用lambda定义函数
Python有4个内建的数据结构——List(列表)、Tuple(元组)、Dictionary(字典)以及Set(集合),它们可以统称为容器(Container),因为它们实际上是一些“东西”组合而成的结构,而这些“东西”可以是数字、字符、列表或者它们的组合。通俗来说,容器里边是什么都行,而且容器里边的元素类型不要求相同。
列表和元组都是序列结构,它们本身很相似,但又有一些不同的地方。
从外形上看,列表与元组存在一些区别。列表是用方括号标记的,如a=[1,2,3],而元组是用圆括号标记的,如b=(4,5,6)。访问列表和元组中的元素的方式是一样的,如a[0]等于1,b[2]等于6,等等。刚刚已经谈到,容器里边是什么都行,因此,以下定义也是成立的:
从功能上看,列表与元组的区别在于:列表可以被修改,而元组不可以。比如,对于a=[1,2,3],那么执行语句a[0]=0,就会将列表a修改为[0,2,3];而对于元组b=(4,5,6),执行语句b[0]=1则会报错。需要注意的是,如果已经有了一个列表a,想复制a并命名为变量b,那么b=a是无效的,这时候b仅仅是a的别名(或者说引用),修改b也会修改a。正确的复制方法应该是b=a[:]。
跟列表有关的函数是list,跟元组有关的函数是tuple,它们的用法和功能几乎一样,都是将某个对象转换为列表/元组,如list('ab')的结果是['a','b'],tuple([1,2])的结果是(1,2)。一些常见的与列表/元组相关的函数如表2-1所示。
表2-1 与列表/元组相关的函数
此外,列表本身自带了很多实用的方法(元组不允许修改,因此方法很少),如表2-2所示。
表2-2 列表相关的方法
最后,不能不提的是“列表解析”这一功能,它能够简化我们对列表内元素逐一进行操作的代码。使用append函数对列表元素进行操作,如代码清单2-10所示。
代码清单2-10 使用append函数对列表元素进行操作
使用列表解析进行简化,如代码清单2-11所示。
代码清单2-11 使用列表解析进行简化
这样的语法不仅方便,而且直观。这充分体现了Python语法的人性化。在本书中,我们将会较多地用到这样简洁的代码。
Python引入了“自编”这一方便的概念。从数学的角度来讲,它实际上是一个映射。通俗来讲,它也相当于一个列表,然而它的“下标”不再是以0开头的数字,而是自己定义的“键”(Key)。
创建一个字典的基本方法如下:
这里的today、tomorrow就是字典的“键”,它在整个字典中必须是唯一的,而20、30就是“键”对应的值。访问字典中的元素的方法也很直观,如代码清单2-12所示。
代码清单2-12 访问字典中的元素
要创建一个字典,还有其他一些比较方便的方法,如通过dict()函数转换,或者通过dict.fromkeys()函数创建,如代码清单2-13所示。
代码清单2-13 通过dict或者dict.fromkeys创建字典
很多字典相关的函数和方法与列表的相同,这里不再赘述。
Python内置了集合这一数据结构,它的概念跟数学中的集合的概念基本上一致。集合跟列表的区别在于:①它的元素是不重复的,而且是无序的;②它不支持索引。一般我们通过花括号{}或者set()函数来创建一个集合,如代码清单2-14所示。
代码清单2-14 创建集合
集合具有一定的特殊性(特别是无序性),因此它有一些特别的运算,如代码清单2-15所示。
代码清单2-15 集合运算
在本书中,集合这一对象并不常用,所以这里仅仅简单地介绍一下,并不进行详细的说明,如果读者会进一步使用集合这一对象,请自行搜索相关教程。
函数式编程(Functional Programming)或者函数程序设计又称泛函编程,是一种编程范型,它将计算机运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。简单来讲,函数式编程是一种“广播式”编程,通常会结合前面提到的lambda定义函数用于科学计算中,以便更简洁方便。
在Python中,函数式编程主要由几个函数构成:lambda、map、reduce、filter,其中前面已经介绍过lambda,它主要用来自定义“行内函数”,所以现在我们逐一介绍后面3个函数。
假设有一个列表a=[1,2,3],要给列表中的每个元素都加2得到一个新列表。利用前面已经谈及的列表解析,我们可以这样写,如代码清单2-16所示。
代码清单2-16 使用列表解析操作列表元素
我们也可以利用map函数这样写,如代码清单2-17所示。
代码清单2-17 使用map函数操作列表元素
也就是说,我们首先要定义一个函数,然后再用map命令将函数逐一应用到(map)列表中的每个元素,最后返回一个数组。map命令也接收多参数的函数,如map(lambda x,y:x*y,a,b)表示将a、b两个列表的元素对应相乘,再把结果返回新列表。
也许有的读者会有疑问:有了列表解析,为什么还要用map命令呢?虽然列表解析的代码比较短,但是它本质上还是for命令,运行效率并不高,而map函数实现了相同的功能,且效率更高,从原则上来说,它的运行速度类似于C语言的速度。
reduce有点像map,但map用于逐一遍历,而reduce用于递归计算。在Python 3.x中,reduce函数已经被移出了全局命名空间,被置于functools库中,使用时需要通过from functools import reduce导入reduce函数。先看一个例子,这个例子可以算出 n 的阶乘,如代码清单2-18所示。
代码清单2-18 使用reduce函数计算 n 的阶乘
其中range(1,n+1)相当于给出了一个列表,元素是1~ n 这 n 个整数。lambda x,y:x*y构造了一个二元函数,返回两个参数的乘积。reduce函数首先将列表的头两个元素作为函数的参数进行运算,然后将运算结果与第三个数字作为函数的参数,然后再将运算结果与第四个数字作为函数的参数……依此递推,直到列表结束,返回最终结果。如果用循环命令,那就要写成如代码清单2-19所示的形式。
代码清单2-19 使用循环命令计算 n 的阶乘
顾名思义,它是一个过滤器,用来筛选列表中符合条件的元素,如代码清单2-20所示。
代码清单2-20 使用filter函数筛选列表元素
使用filter函数时,首先需要一个返回值为bool型的函数,如上述“lambda x: x>5 and x<8”定义了一个函数,判断x是否大于5且小于8,然后将这个函数作用到range(10)的每个元素中,如果为True,则“挑出”那个元素,最后将满足条件的所有元素组成一个列表并返回。
当然,也可以使用列表解析筛选列表元素,如代码清单2-21所示。
代码清单2-21 使用列表解析筛选列表元素
它并不比filter函数复杂。但是要注意,我们使用map、reduce或filter函数的最终目的都是兼顾简洁和效率,因为map、reduce或filter函数的循环速度比Python内置的for循环或while循环要快得多。
前面已经讲述了Python基本平台的搭建和使用,然而在默认情况下它并不会将所有的功能都加载进来。我们需要把更多的库(或者叫作模块、包等)加载进来,甚至需要安装第三方扩展库,以丰富Python的功能,实现我们的目的。
Python本身内置了很多强大的库,如数学相关的math库,可以为我们提供更加丰富且复杂的数学运算,如代码清单2-22所示。
代码清单2-22 使用math库进行数学运算
导入库的方法,除了直接根据库名导入之外,还可以使用别名导入库,如代码清单2-23所示。
代码清单2-23 使用别名导入库
此外,如果并不需要导入库中的所有函数,可以通过名称导入指定函数,如代码清单2-24所示。
代码清单2-24 通过名称导入指定函数
当然,可以直接导入库中的所有函数,如代码清单2-25所示。
代码清单2-25 导入库中所有函数
我们可以通过help('modules')命令来获得已经安装的所有模块名。
Python 2.x与Python 3.x之间的差别不仅表现在内核上,也部分表现在代码的实现中。比如,在Python 2.x中,print是作为一个语句出现的,用法为print a;但是在Python 3.x中,它是作为函数出现的,用法为print(a)。为了保证兼容性,本书的基本代码是基于Python 3.x的语法编写的,使用Python 2.x的读者可以通过引入future特征的方式兼容代码,如代码清单2-26所示。
代码清单2-26 导入future特征
Python自带了很多库,但不一定可以满足我们的所有需求。就数据分析和数据挖掘而言,还需要添加一些第三方库来拓展它的功能。这里介绍一下常见的安装第三方库的方法,如表2-3所示。
表2-3 常见的安装第三方库的方法
这些安装方式将在2.3节中实际展示。