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推導式所替代。

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