Python学习笔记(7-2):若干高级功能——各种迭代函数
文章导读
- 课程难度:★★★☆☆
- 重要度:★★★★☆
- 预计学习时间:40分钟
- 简介:本节主要讲解了数据分析时使代码更优美且精简的各种迭代函数的使用技巧,包括:(1)可替代定义函数的lambda表达式;(2)可替代循环的map()函数;(3)过滤函数filter();(4)reduce()函数迭代处理序列对象的元素;
(5)列表推导式的实现;(6)apply()和applymap()函数处理pandas和Dataframe数据。
- 重点涉及的函数和内容:lambda表达式、map()、filter()、reduce()、列表推导式、apply()、applymap()以及lambda表达式分别与map()、filter()、reduce()、apply()和applymap()函数的结合使用;
一、lambda表达式
lambda
是定义一个“匿名函数”的关键字。所谓匿名函数即这个函数没有函数名称,常用在临时需要一个函数的功能但又不想定义函数的场合。lambda
表达式只能包含一个表达式,不允许包含选择、循环等语法结构。语法格式如下:
lambda 参数 : 表达式
参数中若包含多个参数的话应该用逗号,
分隔,不需要用括号。我们可以将lambda
函数赋给某个对象,这个对象就可以像函数一样使用:
funl = lambda x, y: x * 3 + y * 2
funl(3, 2)
输出结果如下:
13
另外,我们可以完全撇开其中的一个或多个参数,也可以给某个参数设置一个默认值,和def
的函数定义十分相似:
funl1 = lambda x, y = 2: x + y
print(funl1(2)),
print(funl1(2, 3))
print(funl1([1, 2, 3], [4, 5, 6]))
输出结果如下:
4
5
[1, 2, 3, 4, 5, 6]
同时,当我们将lambda
匿名函数写进列表里,循环调用列表的每一个函数。这样可以对某个值,乃至某个维度构建同一套特征,这在数据分析的特征构建过程中很便利:
funlist = [
lambda x : x ** 2,
lambda x : x + 1,
lambda x : x * 3
]
x, x_list = 10, []
for fun in funlist:
x_list.append(fun(x))
x_list
输出结果如下:
[100, 11, 30]
讲解lambda
函数是为了后面做铺垫用的,当然本身它也是一个很重要的函数,接下来我们讲解map()
、filter()
和reduce()
函数,这三个都是接收一个序列化(sequence)的对象(list、numpy数组等),执行某种操作。
二、map()
map()
方法是将一个函数或匿名函数应用到一个或多个序列化的对象上,并返回map
对象。
它的语法格式为:
map(func,seq1,seq2....)
seq1 = [1, 2, 3, 4, 5]
seq2 = [1, 3, 5, 7, 9, 11]
def funm1(x):
return x ** 2
def funm2(x, y):
return x + y * 2
print(map(funm1, seq1))
print(list(map(funm1, seq1)))
print(list(map(funm2, seq1, seq2)))
输出结果如下:
<map object at 0x000001E34DD4C668>
[1, 4, 9, 16, 25]
[3, 8, 13, 18, 23]
从上面的例子中可以看出,使用map()
函数后输出的是map
对象,只有依次输出、或是执行其他操作例如将之转化成为列表的时候,才会产出具体的值。另外,传递的函数fun()
可以是一个接收多个参数的函数,这时map
便接收传递多个序列,后续各个序列的值分别按照参数的位置对应第一、二乃至后续的参数。类似于zip
,传入多个序列时,会在较短序列终止时结束map
操作。
同时,我们还将lambda
定义匿名函数引入进map
中。这里,lambda
函数就可以替代funm1
、funm2
,它可以替换map
中函数名称的位置,而直接对函数操作进行描述:
print(list( map(lambda x: x**2, seq1) ))
print(list( map(lambda x, y: x + y * 2, seq1, seq2) ))
输出结果如下:
[1, 4, 9, 16, 25]
[3, 8, 13, 18, 23]
这个过程,map
代替了循环,而lambda
代替了函数定义,让代码变得相对简洁。
三、filter()
顾名思义,它用于过滤一个序列中的某些值。它的作用:用指定函数的规则对序列中的元素进行过滤,返回filter
对象,其中包含原序列中使函数func
返回值为True
的元素。若func
为None
,则只保留原序列中非空、且不为0或False
的值。
它的语法格式为:
filter(func or None, seq)
我们判断的是返回值a的bool(a)
值是True
还是False
,因此如果是def
了一个函数,必须显式地有一个返回值。返回值允许是各种各样的值,但务必注意filter保留的是原值,而不是返回值。
seq2 = 'ilqweuhgfasdnfvaaalighnaqweouyrlamlonsaldhngiolweuhygtaoqwsdhngvalsndfqowlr'
def funf1(s):
if (s == 'a') | (s == 's') :
return s
else:
return None
list(filter(funf1, seq2))
输出结果如下:
['a', 's', 'a', 'a', 'a', 'a', 'a', 's', 'a', 'a', 's', 'a', 's']
最后补充一点,filter
不支持对多个序列进行同时筛选。我们也是利用lambda
匿名函数构建一个类似的filter
,继续熟悉lambda
操作:
seq1 = [1, 3, 5, 7, 9]
list(filter(lambda x : x ** 2 > 30, seq1))
输出结果如下:
[7, 9]
四、reduce()
reduce
函数也是接收一个函数和一个序列,处理的时候,先处理第一个和第二个,然后将返回结果作为一个新值,和第三个一起处理。再将与第三个值处理的结果作为一个新值,和第四个值一起处理,一直到整个列表循环完。类似于:f(f(f(x1, x2), x3), x4)
它的语法格式为:
reduce(func, seq[, initial])
第一个参数func
是处理函数或方法,第二参数是需要处理的序列或可迭代对象。这个函数可以将一个接收2个参数的函数以迭代的方式从左到右依次作用到一个序列或可迭代对象的所有元素上,并且每次计算的中间结果直接参与下一次的计算。
reduce()
方法需要单独从functools
库中引入:
from functools import reduce
首先,reduce
也是支持匿名函数的,下面举一个实现阶乘的例子:
a = 10
reduce(lambda x, y: x * y, range(1, a + 1))
输出结果如下:
3628800
另外,我们再讲一个数据分析时常用的需求,即:想要处理一个带有扩展名的文件,访问它不带扩展名的文件名,一般情况下,可以直接用split()
函数:
'filename.csv'.split('.')[0]
但是若文件名中也包含了点.
,分割后会将产出许多个子串,但我们希望除了最后一个之外,把前面的字符串拼起来,这时候就用reduce()
函数。实现代码如下,我们的序列,是根据.
分隔之后的除了最后一个值之前的所有值,我们的函数,是以.
拼接先后连续的子串:
string = '907.18.11.12.csv'
reduce(lambda x, y: x + '.' + y, string.split('.')[:-1])
输出结果如下:
'907.18.11.12.csv'
如上使用lambda
与reduce
函数相结合的方法,一行操作代码便满足了我们的需求。
五、列表推导式
所谓列表推导,其实就是拿着一个现有的列表,按照规则,筛选并映射出一个新的列表。
语法格式:
[expression for expr1 in seq1 if condition1
for expr2 in seq2 if condition2]
它的作用是:对列表等可迭代对象的元素进行遍历、过滤或再计算,生成新的列表
1、它的一般例子是这样的:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[x ** 3 for x in a if x % 2 == 0] # 如果x为偶数,输出x的立方
输出结果如下:
[8, 64, 216, 512, 1000]
2、其次是中间的循环,我们在这里也可以像一般for
循环一样,用zip
、enumerate
等组织多个循环序列。下面是一个输出所有同号(同正负)的元素组的例子:
a = [2, -3, -4, 6, 10]
b = [3, 5, -1, 9, -4]
c = [8, 6, -7, -5, -2]
[[at, bt, ct] for at, bt, ct in zip(a, b, c) if abs(sum([at, bt, ct])) == abs(at) + abs(bt) + abs(ct)]
输出结果如下:
[[2, 3, 8], [-4, -1, -7]]
3、另外,关于右边的判断条件,我们可以像一般的条件一样,也可以用def
返回一个值,也可以直接返回一个True
或者False
(虽然这样写的话就没有写if
的必要)。右边的判断条件也是通过bool()
函数判断的,只有bool()
函数返回为True
的才将它加入新列表中,下面是一个输出所有位的数字之和大于等于20的值的例子:
d = [541, 834, 695, 768, 439, 215, 187, 357]
def funl(x):
SUM = 0
while x / 10 > 0:
SUM += x % 10
x = (x - x % 10) / 10
return SUM >= 20
[x for x in d if funl(x)]
[x for x in d if True] #等同于就是输出所有的x
输出结果如下:
[541, 834, 695, 768, 439, 215, 187, 357]
补充:不要在后面的表达式里写匿名函数,想着用判断条件经过这个lambda
函数的结果再来进行判断。这是因为此时的if
也是通过bool(a)
判断是否纳入新列表的,和filter
一样。进行bool()
运算的是这个lambda
函数本身。对于一个lambda
函数,只要这个函数成功建立,就必然是一个存在的对象,传入bool()
函数时永远会返回True
,对某个函数名也同理。
4、最后,除列表推导式之外,同样也有集合推导式和和字典推导式,使用方式相同。简单各举一个例子:
# 注意循环的同时也要注意集合的特性
{x % 3 for x in [1, 2, 3, 4, 5]}
# 注意字典的元素用 key:value 定义
{index : value for index, value in enumerate(['one', 'two', 'three', 'four', 'five'])}
输出结果如下:
{0, 1, 2}
{0: 'one', 1: 'two', 2: 'three', 3: 'four', 4: 'five'}
这种推导式,结合了map
和filter
等功能,在比较短的时候写起来也很清爽,值得利用。但请注意不要滥用,不要为了短而短、忽视了代码的可阅读性。
六、apply()和applymap()函数
这两个函数是在pandas、dataframe中使用的。
1、apply()
它是对整列、整行的值进行计算,返回结果。语法结构为:
frame.apply(func,axis=) # axis=1为按行操作,axis=0为按列操作
这个函数可以看成将每一列(一行)看成一个元素,将所有的元素(即对每一个完整的列或行)进行map
运算,下面是它的基本操作示例:
import pandas as pd
frame = pd.DataFrame([[1, 2, 3], [5, 10, 15], [10, 20, 30]], columns = ['COL1', 'COL2', 'COL3'])
print(frame.apply(lambda x : np.sum(x), axis= 1)) # np.sum为计算整段数据的和
print(frame.apply(lambda x : np.sum(x), axis= 0))
输出结果如下:
0 6
1 30
2 60
dtype: int64
COL1 16
COL2 32
COL3 48
dtype: int64
2、applymap()
applymap()
函数则是对dataframe中的每一个元素进行计算,并返回各自计算结果的整列值。这个函数可以看成对每一列中的各个元素进行map
处理,例如:
print(frame.applymap(lambda x : x ** 2))
输出结果如下:
COL1 COL2 COL3
0 1 4 9
1 25 100 225
2 100 400 900
这里有一个需注意的地方,即apply
方法若传递一个返回等长数组、列表的函数,则其效果和applymap
相同。而applymap
若是传递一个返回单一值的结果,则会将每一个元素看成一个数组或列表并予以运算,比如我们这里把向apply
和applymap
传递的对象进行调换,观察结果:
print(frame.apply(lambda x : x ** 2, axis = 1))
print(frame.applymap(lambda x : np.sum(x)))
输出结果如下:
COL1 COL2 COL3
0 1 4 9
1 25 100 225
2 100 400 900
COL1 COL2 COL3
0 1 2 3
1 5 10 15
2 10 20 30
至此,python的一些比较常用且高级的系统工具就讲到这里,在下节我们将讲一下python数据分析时必备的技能:数据读取和预处理操作。
对于缺乏Python基础的同仁,可以通过免费专栏🔥《Python学习笔记(基础)》从零开始学习Python
结语
请始终相信“Done is better than perfect”
,不要过分追求完美,即刻行动就是最好的开始, 一个模块一个模块地积累和练习,必将有所收获。
还有其他的问题欢迎在评论区留言📝!
[版权申明] 非商业目的注明出处可自由转载,转载请标明出处!!!
博客:butterfly_701c