Python学习系列之Map,Reduce和 Filter

Map,Filter 和 Reduce 三个高阶函数能为函数式编程提供便利。

首先看一下什么是MapReduce?

摘自wiki中关于MapReduce的解释:

MapReduceGoogle提出的一个软件架构,用于大规模数据集(大于1TB)的并行运算。概念“Map(映射)”和“Reduce(归纳)”,及他们的主要思想,都是从函数式编程语言借来的,还有从矢量编程语言借来的特性。

当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归纳)函数,用来保证所有映射的键值对中的每一个共享相同的键组。 

简单来说,一个映射函数就是对一些独立元素组成的概念上的列表(例如,一个测试成绩的列表)的每一个元素进行指定的操作(比如,有人发现所有学生的成绩都被高估了一分,他可以定义一个“减一”的映射函数,用来修正这个错误)。事实上,每个元素都是被独立操作的,而原始列表没有被更改,因为这里创建了一个新的列表来保存新的答案。这就是说,Map操作是可以高度并行的,这对高性能要求的应用以及并行计算领域的需求非常有用。
而归纳操作指的是对一个列表的元素进行适当的合并(继续看前面的例子,如果有人想知道班级的平均分该怎么做?他可以定义一个归纳函数,通过让列表中的奇数(odd)或偶数(even)元素跟自己的相邻的元素相加的方式把列表减半,如此递归运算直到列表只剩下一个元素,然后用这个元素除以人数,就得到了平均分)。虽然他不如映射函数那么并行,但是因为归纳总是有一个简单的答案,大规模的运算相对独立,所以归纳函数在高度并行环境下也很有用。

一、Map

1.1 定义及基础用法

Map会将一个函数映射到一个输入序列的所有元素上。Map() 会根据提供的函数对指定序列做映射。

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

func是None的情况,它的目的是将多个列表相同位置的元素归并到一个元组,在现在已经有了专用的函数zip()了。

不同长度的多个seq是无法执行map函数的,会出现类型错误。

基础语法:

map(function_to_apply, list_of_inputs)
map(*function*, *iterable*, *...*) -> list

参数:

function_to_apply:映射函数,也就是处理输入迭代类型的的每一个元素的函数。

list_of_inputs:一个或多个序列。输入的序列类型:列表,集合,元组,字典及字符串

返回值: 

    Python2 :map直接返回列表,不管输入的迭代类型是列表,集合,元组,字典还是字符串类型。

    Python3:map返回迭代器,可以利用list()转为列表,只能转为列表,set(), tuple()等函数不起作用。

Iterator是惰性序列,因此通过list函数让它把整个序列都计算出来并返回一个list。

示例:

Python 2:

a = [1, 2, 3, 4]
b = map(lambda x: x*2, a)   # 处理列表
Out[4]: [2, 4, 6, 8]

b = map(lambda x: x*2, (1,2,3))  # 处理元组
Out[6]: [2, 4, 6]

b = map(lambda x: x*2, {1:2, 3:4, 5:6})  # 处理字典,只处理键
Out[8]: [2, 6, 10]
b = map(lambda x: x*2, {1:2, 3:4, 5:6}.keys())
Out[17]: [2, 6, 10]

b = map(lambda x: x*2, {1:2, 3:4, 5:6}.values())
Out[15]: [4, 8, 12]

b = map(lambda x: x*2, set([1, 2, 3, 4]))  # 处理集合
Out[11]: [2, 4, 6, 8]

b = map(lambda x: x+"1", "12345") # 处理字符串,字符串也是迭代类型
Out[19]: ['11', '21', '31', '41', '51']

Python 3

>>> a = [1, 2, 3, 4]
>>> b = map(lambda x: x*2, a)   # 处理列表
>>> b
<map object at 0x0000000002B68A20>   # map对象
>>> list(b)
[2, 4, 6, 8]

1.2 高级用法

1.2.1 处理多个序列

想要输入和处理多个序列,需要可以支持多个参数的函数,注意的是各序列的长度必须一样,否则报错。
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]
>>> map(add,'zhoujy','Python','test')     #'test'的长度比其他2个小
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() takes exactly 2 arguments (3 given)

 

单个序列:seq中的每个元素都经过了func函数的作用,得到了func(seq[n])组成的列表。

 

多个序列:每个seq的同一位置的元素在执行过一个多元的func函数之后,得到一个返回值,这些返回值放在一个结果列表中。

print map( lambda x, y: x * y, [1, 2, 3], [4, 5, 6] )  # [4, 10, 18]
print map( lambda x, y: ( x * y, x + y), [1, 2, 3], [4, 5, 6] )  # [(4, 5), (10, 7), (18, 9)]

 返回值是一个值,也可以是一个元组。

1.2.2 处理函数序列

大多数时候,我们使用匿名函数(lambdas)来配合map,  不仅可以用于一序列的输入, 也可以用于一列表的函数。

def multiply(x):
        return (x*x)
def add(x):
        return (x+x)

funcs = [multiply, add]
for i in range(5):
    value = map(lambda x: x(i), funcs)
    print(list(value))
    # 译者注:上面print时,加了list转换,是为了python2/3的兼容性
    #        在python2中map直接返回列表,但在python3中返回迭代器
    #        因此为了兼容python3, 需要list转换一下

# Output:
# [0, 0]
# [1, 2]
# [4, 4]
# [9, 6]
# [16, 8]

二、Reduce

2.1 定义及基础用法

       reduce() 函数会对参数序列中元素进行累积。

函数将一个数据集合(列表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果,逐步迭代

   也就是reduce函数把前两个元素的计算结果继续和序列的下一个元素做累积计算,直到处理完所有元素,返回结果。以下两个函数等价:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

基本语法:

reduce(function, iterable[, initializer])
reduce(*function*, *iterable*[, *initializer*]) -> value

参数:

  • function -- 函数,有两个参数
  • iterable -- 可迭代对象
  • initializer -- 可选,初始参数。第一次时为init的元素,如没有init则为seq的第一个元素。

返回值:

返回函数计算结果。

示例:

>>>def add(x, y) :            # 两数相加
...     return x + y
... 
>>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
15
>>> reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
15

上述执行过程是:1+2=3,3+3=6,6+4=10,10+5=15。

2.2 高级用法

2.2.1 处理函数序列

使用匿名函数(lambdas)来配合reduce,  不仅可以用于一序列的输入, 也可以用于一个函数序列,但是要注意lambda表达式的参数是两个。

def multiply(x):
    return (x * x)


def add(x):
    return (x + x)


funcs = [multiply, add]
    for i in range(5):
        value = reduce(lambda x, y: x(i) + y(i), funcs)
        print(value)

0
3
8
15
24

三、Filter

3.1 定义及基础用法    

Python 2:      

 filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表

Python 3:

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换,利用set转换为集合,tuple转换为元组,str转为字符串。

另外注:集合是{},列表是[],元组是(),字典是{k1:v1, k2:v2}

       注意: Pyhton2.7 返回列表,Python3.x 返回迭代器对象。

       Filter是一个内置函数,并且比for循环更快。

基本语法:

filter(function, iterable)
filter(function or None, sequence) -> list, tuple, or string

参数:

       其接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

  • function -- 判断函数。过滤序列中每个元素的函数。
  • iterable -- 可迭代对象。需要过滤的序列。

返回值:

       Python 2:返回序列,不只是列表,也可能是元组,字符串。

       列表,集合,字典返回列表元组返回元组字符串返回字符串。字典处理键。

       Python 3:返回一个迭代器对象,filter object(filter对象)。

Python3 中返回到是一个 filter 类对象。filter 类实现了 __iter__ 和 __next__ 方法, 可以看成是一个迭代器, 有惰性运算的特性(类似于生成器), 相对 Python2.x 提升了性能, 可以节约内存。

a = filter(lambda x: x % 2 == 0, range(10))
print(a)

输出:

<filter object at 0x0000022EC66BB128>

示例:

Python 2:

过滤出列表中的所有奇数:

def is_odd(n):
    return n % 2 == 1
 
newlist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(newlist)

[1, 3, 5, 7, 9]

Python 3:

def is_odd(n):
    return n % 2 == 1
 
tmplist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
newlist = list(tmplist)
print(newlist)

[1, 3, 5, 7, 9]

四、总结:

(1)map函数和map函数与MapReduce的机制原理相同。map函数对序列的每一个元素进行处理,而resuce函数则两两迭代逐步计算。

(2)reduce函数和filter函数只能处理一个序列。

(3)注意在 python2 和 python3 中,map/reduce/filter 的返回值类型有所不同,python2 返回的是基本数据类型,而 python3 则返回了迭代器。

(4)filter函数很容易被for循环和for in if推导式所替代。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章