基于股票大数据分析的Python入门实战(视频教学版)
上QQ阅读APP看书,第一时间看更新

2.4 针对数据结构对象的常用操作

在数据分析等应用场景中,比较关键的就是数据存储与数据操作。在前文中,讲述了以列表、元组和字典的形式存储数据的方法,下面将讲述Python中常用的操作数据的方法。

2.4.1 映射函数map

通过调用map函数,就能根据指定的函数对指定序列执行映射运算,它的语法如下。

    map(function, parameter)

其中,第一个参数function表示映射运算的规则,第二个参数parameter则表示待映射的对象。在下面的MapDemo.py范例程序中演示的是映射的效果。

1    # !/usr/bin/env python
2    # coding=utf-8
3    def square(x):     # 计算平方数
4        return x ** 2
5    print (list(map(square, [1,3,5])))   # 输出[1, 9, 25]
6    
7    def strToLowCase(str):
8        return str.lower()
9    strList=["Company","OFFICE"]
10    strList=map(strToLowCase,strList)
11    print(list(strList))      # ['company', 'office']
12    
13    def tagCustomer(num):
14        if num>5000:
15            return "VIP"
16        else:
17            return "Normal"
18    print (list(map(tagCustomer,[1000])))     # ['Normal']
19    #print map(tagCustomer,1000)             # 会报错

在第3行和第4行中通过def定义了一个计算平方数的函数square,在第5行的map方法中,第一个参数是这个函数,第二个参数则是待计算平方数的列表,从第5行的打印语句来看,输出的是1,3,5的平方数。从中可知,map方法会把用第1个参数指定的函数运用于第2个参数指定的序列,并把计算好的结果返回。

在第7行和第8行的函数中实现了“把输入参数转为小写字母”的功能,在第10行中,同样调用了map方法,把strList这个列表里的元素转成了小写字母,从第11行输出语句的结果中即可看到“转为小写字母”的效果。

通过调用map方法还可以实现基于规则的映射,比如在某个项目中有这样的定义,凡是消费金额高于5000元的客户,将加上VIP的标识,否则是一般用户。通过第13行到第18行的代码,调用map函数即可实现这一映射的效果。

不过要注意的是,map方法第二个参数需要是“可以遍历”(或可迭代)的对象,比如列表元素或字典等,如果像第19行那样,把整数数据类型作为参数,则会出现如下的错误提示,因为第二个参数不具有可迭代特性(Iteration)。

    TypeError: argument 2 to map() must support iteration

2.4.2 筛选函数filter

该函数的语法为filter(function, iterable)。具体的含义是,根据第1个参数指定的函数,过滤掉第2个参数指定对象中不符合要求的元素。在下面的FilterDemo.py范例程序中来演示一下filter函数的相关用法。

1    # !/usr/bin/env python
2    # coding=utf-8
3    # 判断输入参数是否是小写字母的函数
4    def isLowCase(str):
5        return str.lower() == str
6    strlist=filter(isLowCase, ["Hello","world"])
7    print(list(strlist)) # ['world']
8    # 判断输入参数是否为空的函数
9    def filterNull(empNo):
10        return empNo.strip() !=''
11    dataFromFile=['101','102','103','']
12    empList=filter(filterNull,dataFromFile)
13    print(list(empList)) # ['101', '102', '103']

在第4行和第5行中定义了isLowCase函数,在其中判断输入参数是否为小写字母。

在第6行中定义的filter方法第一个参数即为isLowCase ,指定判断的规则是“都为小写字母”,第二个参数是一个包含字符串的列表。这里filter方法的作用是,依次对"Hello"和"world"这两个字符串运行isLowCase函数,如果返回false则过滤掉,返回true则保留。这样strList对象就只包含了小写字母的字符串,从第7行打印语句的输出结果中即可看到这一过滤结果。

在第9行和第10行的filterNull方法中,用来判断输入参数是否为空,在第11行中定义的dataFromFile列表里,包含了一个空的元素,这样执行第12行的filter方法,就能把其中空元素过滤掉,从第13行print语句的输出结果中即可看到过滤掉空元素之后的效果。

2.4.3 累计处理函数reduce

Python中的reduce函数会调用指定的函数对参数序列中的元素从左到右进行累计处理,这句话看上去有些难懂,下面通过ReduceDemo.py范例程序来看看这个函数的作用。

1    # coding=utf-8
2    from functools import reduce
3    # 定义一个加法的函数add
4    def add(x, y):
5        return x + y
6    print(reduce(add, [1,2,3,4,5]))         # 输出15
7    print(reduce(add, [1,2,3,4,5],100))     # 输出115
8    # 定义乘法的函数
9    def multiply(x,y):
10        return x*y
11    print(reduce(multiply, [1,2,3,4,5]))         # 输出120
12    # 定义拼接数字的函数
13    def combineNumber(x, y):
14        return x * 10 + y
15    print(reduce(combineNumber, [1,2,3,4,5]))     # 输出12345

在第4行和第5行中定义了一个实现加法功能的add函数(或称为方法),在第6行中,reduce的第一个参数即为add,第二个参数是一个包含1到5的序列。

这里reduce函数的含义是,先取左边的两个参数1和2,把它们作为add方法的两个输入参数,经add函数返回的结果是3,再把3和从左到右的第3个序列(也是3)作为add函数的两个输入参数,这时add函数的返回值是6,再用6和第4个序列(数字4)作为add函数的两个输入参数,以此类推。所以结果是1到5的累加和,即15,第7行的输出语句验证了这一结果。同理,通过第11行的reduce方法,实现了1到5的阶乘效果。

在第13行和第14行的combineNumber方法中,定义了拼装两个数字的函数,比如输入参数是1和2,那么会返回12。在第15行中通过调用reduce方法,从左到右拼装了1到5这个序列,返回结果是12345。

从这个范例程序的代码中可知,reduce具有“递归操作”的功能,即从左到右读取序列,把两个数值(或上一次运算的结果和下一个数值)作为参数传入指定的函数,由此完成针对整个序列的操作或运算。

2.4.4 通过Lambda表达式定义匿名函数

在之前的范例程序中,我们通过def语句定义函数时,会指定函数的名字,但在某些场合,函数内的代码非常简单,不值得“大张旗鼓”地定义函数名以及用return返回结果,这时就可以通过Lambda表达式来定义匿名函数,以此来简化代码。

在下面的LambdaSimpleDemo.py范例程序中演示了Lambda的用法,请大家注意Lambda和map等函数整合使用的编程逻辑。

1    # !/usr/bin/env python
2    # coding=utf-8
3    # 通过lambda表达式定义了一个匿名函数
4    add=lambda a,b,c:a+b+c
5    print(add(1,2,3)) # 输出6
6    # 计算奇数
7    numbers=[1,3,6,7,10,11]
8    # 与filter整合使用
9    numbers=filter(lambda input: input%2!=0, numbers)
10    print numbers #[1, 3, 7, 11]
11    numbers=[2, 3, 4]
12    # 与map整合使用
13    numbers=map(lambda x: x*x, numbers)
14    print numbers #[4, 9, 16]
15    # 与reduce整合使用
16    numbers=[1,2,3,4,5]
17    sum=reduce(lambda x, y: x + y, numbers)
18    print sum # 输出15
19    # 与sorted整合使用
20    numbers=[1,-2, 3, -4,5]
21    numbers=sorted(numbers, lambda x, y: abs(y)-abs(x))
22    print numbers # [5, -4, 3, -2, 1]

在第4行中演示了Lambda定义匿名函数的基本做法。这里没有定义函数名,也即是定义了个匿名函数。在lambda关键字之后有3个变量,即为匿名函数的三个参数,在冒号之后则定义了该Lambda表达式(也就是匿名函数)的返回值,这个匿名函数是计算三个输入参数的和,而在等号左边的add则是这个lambda表达式的名字。第5行的程序语句用到了add来调用第4行定义的Lambda表达式。

在第9行中把Lambda表达式和filter函数整合到一起。前面介绍过,filter函数的第一个参数指定了过滤规则,这里通过Lambda表达式指定“只保留满足input%2!=0条件”的数,即只保留奇数,第10行的print语句验证了这个filter函数的效果。

在第13行中整合使用了Lambda表达式和map函数,依次对列表中的每个元素进行了“乘积”的操作,对第16行定义的numbers列表中的每个值进行了累加的操作,在第17行中整合了Lambda表达式和reduce函数。

在第21行中,在sorted函数的第二个参数中,通过Lambda表达式定义了针对numbers序列的排序规则。通过Lambda表达式定义的排序规则是:如果y的绝对值大于x的绝对值,则y排在x之前,反之则y在x之后。通过第22行的输出即可验证这一结果。

除了能定义匿名函数,Lambda表达式的另一个用法是把函数作为“输入参数”,即可以定义“高级函数”,在下面的LambdaSeniorDemo.py范例程序中示范了这种用法。

1    # !/usr/bin/env python
2    # coding=utf-8
3    # 第3个参数是Lambda表达式
4    def add(x,y,func):
5        return func(x) + func(y)
6    print(add(2,4,lambda a:a*a)) # 2的平方加4的平方等于20
7    
8    print("My Stock List".find("stock")) # 输出-1,表示没找到
9    def existKey(key,words,func):
10        return func(words).find(key)
11    # 输出3,表示找到了
12    print(existKey("stock","My Stock List" ,lambda words:words.lower()))

在第4行定义的add函数中,第3个参数func其实是个函数,在第6行的调用中,我们传入的func函数是对输入参数a进行平方运算,所以add函数返回的结果是x和y的平方和。

在字符串比较的过程中,一般不会区分字母大小写,一般的做法是把目标字符串转成小写字母,而把待比较的字符串也转换成小写字母。

在第9行的existKey函数中,通过第3个参数定义了针对words输入参数的操作。而在第12行的调用时,用Lambda编写的操作是通过lower把输入参数转小写,所以在existKey的函数中,首先是调用func函数,把输入参数words转成小写,随后再看其中是否存在key(即stock),由于存在,因此第12行的print语句会输出3,表示stock在字符串中所处的位置。

从上述两个范例程序中,大家看到了Lambda作为函数输入参数的做法。请注意,一般通过Lambda表达式只会定义功能比较简单的匿名函数。

如果函数需要实现的功能比较复杂,那么应该采用比较复杂的def方式来定义函数,因为用Lambda表达式定义复杂的逻辑,第一是实现起来很难,第二则是可读性很差。