本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是Python专题第6篇文章,给大家介绍的是Python当中三个非常神奇的方法:map、reduce和filter。

不知道大家看到map和reduce的时候有没有什么感觉,如果看过之前我们大数据系列介绍MapReduce文章的同学,想必有些印象。这个MapReduce不是一个分布式的计算方法么,怎么又变成Python中的方法了?其实原因很简单,因为Python是一门很年轻的语言,它在发展的过程当中吸收了很多其他领域的精华,MapReduce就是其中之一。

对之前文章感兴趣的同学可以点击下方的链接,回顾一下之前MapReduce的内容。

大数据基石——Hadoop与MapReduce

map

map除了地图之外,另一个英文本意是映射。在C++和Java一些语言当中,将map进一步引申成了存储key和value映射结构的容器。Python对这点做了区分,KV结构的容器命名成了dict,即字典,而map则回到了它的本意,也就是映射

我们都知道,在数学领域,映射也是函数的定义。一个自变量通过某种映射,对应到一个因变量。同样,在Python当中,map操作本质也是函数,不过它作用的范围不再是单个变量,而是一个序列。换句话说,通过map我们可以省去循环操作,可以自动将一个容器当中的元素套用一个函数。

举个简单的例子,比如我们有一个坐标,我们希望知道它距离原点的距离。这个问题很简单,我们写一个计算距离的函数就可以解决:

def dis(point):
return math.sqrt(point[0]**2 + point[1]**2)

那如果我有多个点需要计算距离,在map出现之前,我们只能用循环来解决问题:

points = [[0, 1], [2, 4], [3, 2]]

for point in points:
print(dis(point))

但是有了map之后, 我们可以省去循环的操作,整个代码简化成了一行:

map(dis, points)

但是要注意,我们调用完map之后得到的结果不是一个list而是一个迭代器。我们直接将map返回的内容print出来,可以得到这样一个结果:

>>> print(map(dis, points))
<map object at 0x107aad1d0>

这是一个类的标准输出,其实它返回的不是最后的结果,而是一个迭代器。我们在之前的文章当中已经介绍过了迭代器和生成器的相关概念,这里不多做赘述了,遗忘的同学可以点击下方链接回顾一下之前的内容:

Python——五分钟带你弄懂迭代器与生成器

我们想要获得完整的内容也很容易,我们只需要将它转化成list类型即可:

>>> print(list(map(dis, points)))
[1.0, 4.47213595499958, 3.605551275463989]

以上过程还可以进一步简化,还记得我们之前介绍过的匿名函数吗?由于dis函数在我们的程序当中只会在map中用到,我们完全没有必要单独创建一个函数,我们可以直接传入一个匿名函数搞定运算:

map(lambda x: math.sqrt(x[0]**2 + x[1] ** 2), points)

简单总结一下,map操作其实执行的是一个映射。它可以自动地将一个序列当中的内容通过制定的函数映射成另一个序列,从而避免显式地使用循环来调用,在很多场景下可以大大地简化代码的编写,可以很方便地将一个序列整体转变成另一个结果。

reduce

相比于map,reduce的操作稍稍难理解一点点。它也是规定一个映射,不过不是将一个元素映射成一个结果。而是将两个元素归并成一个结果。并且它并不是调用一次,而是依次调用,直到最后只剩下一个结果为止。

比如说我们有一个数组[a, b, c, d]和一个函数f,我们计算reduce(f, [a, b, c, d])其实就等价于f(f(f(a, b), c), d)。和map不同的是,reduce最后得到一个结果,而不是一个迭代器或者是list。

我们光说有些抽象,不妨来看一个例子,就看最简单的一个例子:reduce函数接收两个数,返回两个数的和。那么显然,我们依次调用reduce,得到的就是原数组的和。

from functools import reduce

def f(a, b):
return a + b

print(reduce(f, [1, 2, 3, 4]))

最终得到的结果当然是10,同样,我们也可以将reduce中的方法定义成匿名函数,一样不影响最终的结果。

print(reduce(lambda x, y: x + y, [1, 2, 3, 4]))

MapReduce

既然我们map和reduce都有了,显然我们可以将它们串联起来使用,也就是分布式系统当中MapReduce的做法。虽然如果不手动使用线程池的话,Python并不会起多个线程来加速运算,但是至少可以简化我们实现的代码。我们还是举经典的wordCount的例子,也就是文本计算词频

套用map和reduce的功能,整个流程非常清晰,我们只需要在map阶段对文本进行分词,在reduce阶段对分词之后的结果进行汇总即可。

听着好像非常容易,但是你实际去上手是写不出来的。原因也很简单,因为hadoop当中的Map和Reduce中间还有一层shuffle的操作,会自动地将key值相同的结果放到同一个reducer当中。在这个问题当中,key自然就是我们的word,由于相同的word被放到同一个reducer当中,我们只需要累加就行了。但是如果我们自己编写mapreduce的话,由于缺少了中间数据重排的步骤,所以导致不能实现。

要解决也简单,我们可以人为增加一个map阶段代替hadoop当中的重排。相当于做了一个MapMapReduce,我们来看代码:

from collections import Counter, defaultdict

texts = ['apple bear peach grape', 'grape orange pear']

# 第一次map,将字符串转成数组,每个单词对应1
def mp1(text):
ret = []
words = text.split(' ')
for word in words:
ret.append((word, 1))
return ret

# 第二次map,将数组转成dict
def mp2(arr):
d = defaultdict(int)
for k, v in arr:
d[k] += v
return d

# reduce,合并dict
def rd(x, y):
x.update(y)
return x

print(reduce(rd, map(mp2, map(mp1, texts))))

那如果我们不用多次MapReduce呢?也不是没有办法,需要取点巧,方法也简单只要使用之前我们讲解过的Counter类,就可以完美解决这个问题。我们来看代码:

from collections import Counter

texts = ['apple bear peach grape', 'grape orange pear']

def mp(text):
words = text.split(' ')
return Counter(words)

print(reduce(lambda x, y: x + y, map(mp, texts)))

由于我们使用了Counter,所以我们在map阶段返回的结果就已经是词频的dict了,而在reduce阶段我们只需要将它们全部累加起来就OK了。

最后,我们来看下filter

filter

filter的英文是过滤,所以它的使用就很明显了。它的用法和map有些类似,我们编写一个函数来判断元素是否合法。通过调用filter,会自动将这个函数应用到容器当中所有的元素上,最后只会保留运行结果是True的元素,而过滤掉那些是False的元素

举个例子,假设我们想要保留list当中的奇数而过滤掉偶数,我们当然可以直接操作,比如:

arr = [1, 3, 2, 4, 5, 8]

[i for i in arr if i % 2 > 0 ]

而使用filter会非常方便:

list(filter(lambda x: x % 2 > 0, arr))

从这个例子当中可能看不出便捷,但是有的时候判断的条件可能非常复杂,我们判断的逻辑不能简单地在list定义当中表达出来,这个时候使用filter则会容易得多。

最后, 我们再看一个类似的用法。在itertools当中有一个方法叫做 compress,通过compress我们可以实现根据一个序列的条件过滤另一个序列。

举个简单的例子,假设,我们有两个数组:

student = ['xiaoming', 'xiaohong', 'xiaoli', 'emily']
scores = [60, 70, 80, 40]

我们想要获取所有考试及格的同学的list,如果用常规做法基本上免不了使用循环,但是使用compress可以很方便地通过一行代码实现:

from itemtools import compress

>>> pass = [i > 60 for i in scores]
>>> print(pass)
[False, True, True, False]

>>> list(compress(student, pass))
['xiaohong', 'xiaoli']

需要注意的是filter和compress返回的都是一个迭代器,我们要获取它们的值,需要手动转换成list

虽然在日常的开发当中不使用这三样神器同样可以工作,但是用上它们之后,会提升很多代码的可读性,节省很多无用的代码。尤其是在面试的时候,很有可能就会给面试官留下不一样的印象,也许结果也会不同。

今天的文章就是这些,如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。

Python专题——五分钟带你了解map、reduce和filter的更多相关文章

  1. Python的函数式编程: map, reduce, sorted, filter, lambda

    Python的函数式编程 摘录: Python对函数式编程提供部分支持.由于Python允许使用变量,因此,Python不是纯函数式编程语言. 函数是Python内建支持的一种封装,我们通过把大段代码 ...

  2. 马士兵hadoop第五课:java开发Map/Reduce

    马士兵hadoop第一课:虚拟机搭建和安装hadoop及启动 马士兵hadoop第二课:hdfs集群集中管理和hadoop文件操作 马士兵hadoop第三课:java开发hdfs 马士兵hadoop第 ...

  3. 马士兵hadoop第五课:java开发Map/Reduce(转)

    马士兵hadoop第一课:虚拟机搭建和安装hadoop及启动 马士兵hadoop第二课:hdfs集群集中管理和hadoop文件操作 马士兵hadoop第三课:java开发hdfs 马士兵hadoop第 ...

  4. Python——五分钟带你弄懂迭代器与生成器,夯实代码能力

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是周一Python专题,给大家带来的是Python当中生成器和迭代器的使用. 我当初第一次学到迭代器和生成器的时候,并没有太在意,只是觉 ...

  5. Python学习:函数式编程(lambda, map() ,reduce() ,filter())

    1. lambda: Python 支持用lambda对简单的功能定义“行内函数” 2.map() : 3.reduce() : 4.filter() : map() ,reduce() , filt ...

  6. python笔记十四(高阶函数——map/reduce、filter、sorted)

    一.map/reduce 1.map() map(f,iterable),将一个iterable对象一次作用于函数f,并返回一个迭代器. >>> def f(x): #定义一个函数 ...

  7. Python学习 Day 5 高阶函数 map/reduce filter sorter 返回函数 匿名函数 装饰器 偏函数

    高阶函数Higher-orderfunction 变量可以指向函数 >>> abs #abs(-10)是函数调用,而abs是函数本身 <built-in function ab ...

  8. map,reduce和filter函数

    numArray = [1, 2, 3, 4, 5] def ercifang(x): return x ** 2 def map_test(func, numArray): li = [] for ...

  9. 五分钟带你深入了解Redis

    相信phper都知道Redis是什么,既然如此,为表仪式感,首先我还是得说说什么是Redis. Redis是什么 redis是一个高性能的key-value数据库,它是完全开源免费的,而且redis是 ...

随机推荐

  1. Tomcat源码解析-启动过程分析之主干流程

    Tomcat启动入口就在脚本startup.sh中,具体脚本可以看tomcat的源码,这个启动脚本主要用来判断环境,找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行.ca ...

  2. c语言函数指针的理解与使用(学习)

    1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 1 2 3 A) char * (*fun1)(char * p1,char * p2); B) char  ...

  3. python3之urllib代理池

    1.常见状态吗 301:重定向到新的URL,永久性302:重定向到临时URL,非永久性304:请求的资源未更新400:非法请求401:请求未经授权403:禁止访问404:没找到对应页面500:服务器内 ...

  4. Java IO: PipedInputStream

    原文链接 作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) PipedInputStream可以从管道中读取字节流数据,代码如下: 01 InputSt ...

  5. Welcome to Fan Ouyang’s website!

    Welcome to Fan Ouyang's website! 欧阳璠,哲学博士,湖南娄底人. 目前为浙江大学教育学院课程与学习科学系教育技术专业百人计划研究员. 2013-2018年 明尼苏达大学 ...

  6. IT男频繁猝死背后的心理探秘

    "深圳36岁IT男猝死酒店马桶上"这条新闻再次成为人们眼球的焦点,每每发生这样的事情,难免让人扼腕唏嘘,他们本该是风华正茂的年纪,家有老母贤妻爱子,甚至房子车子票子都不缺,该是一边 ...

  7. (五)mybatis-spring的集成

    mybatis-spring的集成 源码下载(数据库使用derby,具体数据库结构参考这里) 在src下新建applicationContext.xml 配置内容:数据源.SqlSessionFact ...

  8. Leetcode刷题记录 剑指offer

    面试题3:数组中重复数字 # 使用set,时间复杂度O(n),空间复杂度O(n)class Solution(object): def findRepeatNumber(self, nums): &q ...

  9. 将js进行到底:node学习6

    开始真正的node web开发--express框架 为何说现在才是web开发的真正开始呢? 首先任何企业都不会用原生的http协议API去开发一个完整的网站,除非她们先开发一个框架出来,其次我们之前 ...

  10. xampp安装后启动apache出现端口占用问题

    apache默认监听电脑80端口,当端口被占用时,xampp无法正常启动apache.我们需要将端口解除占用再启动. xampp报错: Problem detected!19:36:24 [Apach ...