MapReduce理解

anyiya發表於2024-11-02

解釋

下面是轉載的一篇程式設計師給妻子講解什麼是MapReduce的對話,看完能大概明白

我問妻子:“你真的想要弄懂什麼是MapReduce?” 她很堅定的回答說“是的”。 因此我問道:

我:你是如何準備洋蔥辣椒醬的?(以下並非準確食譜,請勿在家嘗試)

妻子:我會取一個洋蔥,把它切碎,然後拌入鹽和水,最後放進混合研磨機裡研磨。這樣就能得到洋蔥辣椒醬了。

妻子:但這和MapReduce有什麼關係?

我:你等一下。讓我來編一個完整的情節,這樣你肯定可以在15分鐘內弄懂MapReduce.

妻子:好吧。

我:現在,假設你想用薄荷、洋蔥、番茄、辣椒、大蒜弄一瓶混合辣椒醬。你會怎麼做呢?

妻子:我會取薄荷葉一撮,洋蔥一個,番茄一個,辣椒一根,大蒜一根,切碎後加入適量的鹽和水,再放入混合研磨機裡研磨,這樣你就可以得到一瓶混合辣椒醬了。

我:沒錯,讓我們把MapReduce的概念應用到食譜上。Map和Reduce其實是兩種操作,我來給你詳細講解下。

Map(對映): 把洋蔥、番茄、辣椒和大蒜切碎,是各自作用在這些物體上的一個Map操作。所以你給Map一個洋蔥,Map就會把洋蔥切碎。 同樣的,你把辣椒,大蒜和番茄一一地拿給Map,你也會得到各種碎塊。 所以,當你在切像洋蔥這樣的蔬菜時,你執行就是一個Map操作。 Map操作適用於每一種蔬菜,它會相應地生產出一種或多種碎塊,在我們的例子中生產的是蔬菜塊。在Map操作中可能會出現有個洋蔥壞掉了的情況,你只要把壞洋蔥丟了就行了。所以,如果出現壞洋蔥了,Map操作就會過濾掉壞洋蔥而不會生產出任何的壞洋蔥塊。

Reduce(化簡):在這一階段,你將各種蔬菜碎都放入研磨機裡進行研磨,你就可以得到一瓶辣椒醬了。這意味要製成一瓶辣椒醬,你得研磨所有的原料。因此,研磨機通常將map操作的蔬菜碎聚集在了一起。

妻子:所以,這就是MapReduce?

我:你可以說是,也可以說不是。 其實這只是MapReduce的一部分,MapReduce的強大在於分散式計算。

妻子:分散式計算? 那是什麼?請給我解釋下吧。

我:沒問題。

我:假設你參加了一個辣椒醬比賽並且你的食譜贏得了最佳辣椒醬獎。得獎之後,辣椒醬食譜大受歡迎,於是你想要開始出售自制品牌的辣椒醬。假設你每天需要生產10000瓶辣椒醬,你會怎麼辦呢?

妻子:我會找一個能為我大量提供原料的供應商。

我:是的..就是那樣的。那你能否獨自完成製作呢?也就是說,獨自將原料都切碎? 僅僅一部研磨機又是否能滿足需要?而且現在,我們還需要供應不同種類的辣椒醬,像洋蔥辣椒醬、青椒辣椒醬、番茄辣椒醬等等。

妻子:當然不能了,我會僱傭更多的工人來切蔬菜。我還需要更多的研磨機,這樣我就可以更快地生產辣椒醬了。

我:沒錯,所以現在你就不得不分配工作了,你將需要幾個人一起切蔬菜。每個人都要處理滿滿一袋的蔬菜,而每一個人都相當於在執行一個簡單的Map操作。每一個人都將不斷的從袋子裡拿出蔬菜來,並且每次只對一種蔬菜進行處理,也就是將它們切碎,直到袋子空了為止。

這樣,當所有的工人都切完以後,工作臺(每個人工作的地方)上就有了洋蔥塊、番茄塊、和蒜蓉等等。

妻子:但是我怎麼會製造出不同種類的番茄醬呢?

我:現在你會看到MapReduce遺漏的階段—攪拌階段。MapReduce將所有輸出的蔬菜碎都攪拌在了一起,這些蔬菜碎都是在以key為基礎的 map操作下產生的。攪拌將自動完成,你可以假設key是一種原料的名字,就像洋蔥一樣。 所以全部的洋蔥keys都會攪拌在一起,並轉移到研磨洋蔥的研磨器裡。這樣,你就能得到洋蔥辣椒醬了。同樣地,所有的番茄也會被轉移到標記著番茄的研磨器裡,並製造出番茄辣椒醬。

作者:永遠的冷冽 | 連結:https://www.jianshu.com/p/c2affdca8ed8 | 來源:簡書

對應修飾符

理解了spark中map和reduce的含義,那麼在pyspark程式碼中,哪些修飾符操作分別要進行map或reduce呢?

在 PySpark 中,map 和 reduce 操作的概念來自於函數語言程式設計中的 MapReduce 模型。雖然 PySpark 提供了許多高階運算子,但它們本質上都可以歸類為 map 或 reduce 操作。

Map 操作

map 操作是對每個元素應用一個函式,並返回一個新的 RDD。以下是一些常見的 map 操作:
map(func)
• 對 RDD 中的每個元素應用 func 函式,並返回一個新的 RDD。
• 示例:

rdd = sc.parallelize([1, 2, 3, 4])
mapped_rdd = rdd.map(lambda x: x * 2)
print(mapped_rdd.collect())  # 輸出: [2, 4, 6, 8]

flatMap(func)
• 對 RDD 中的每個元素應用 func 函式,但 func 返回的是一個列表,然後將所有列表中的元素展平成一個 RDD。
• 示例:

rdd = sc.parallelize(["hello world", "hi there"])
flat_mapped_rdd = rdd.flatMap(lambda x: x.split())
print(flat_mapped_rdd.collect())  # 輸出: ['hello', 'world', 'hi', 'there']

filter(func)
• 對 RDD 中的每個元素應用 func 函式,返回一個只包含滿足條件的元素的新 RDD。
• 示例:

rdd = sc.parallelize([1, 2, 3, 4, 5])
filtered_rdd = rdd.filter(lambda x: x % 2 == 0)
print(filtered_rdd.collect())  # 輸出: [2, 4]

union(other)
• 返回兩個 RDD 的並集。
• 示例:

rdd1 = sc.parallelize([1, 2, 3])
rdd2 = sc.parallelize([3, 4, 5])
union_rdd = rdd1.union(rdd2)
print(union_rdd.collect())  # 輸出: [1, 2, 3, 3, 4, 5]

Reduce 操作

reduce 操作是對 RDD 中的所有元素應用一個函式,最終返回一個單一的結果。以下是一些常見的 reduce 操作:
reduce(func)
• 對 RDD 中的所有元素應用 func 函式,最終返回一個單一的結果。
• 示例:

rdd = sc.parallelize([1, 2, 3, 4])
result = rdd.reduce(lambda x, y: x + y)
print(result)  # 輸出: 10

fold(zero_value, func)
• 類似於 reduce,但提供了一個初始值 zero_value。
• 示例:

rdd = sc.parallelize([1, 2, 3, 4])
result = rdd.fold(0, lambda x, y: x + y)
print(result)  # 輸出: 10

aggregate(zero_value, seq_op, comb_op)
• 更通用的聚合操作,適用於更復雜的聚合邏輯。
• 示例:

rdd = sc.parallelize([1, 2, 3, 4])
result = rdd.aggregate(
    (0, 0),  # 初始值 (sum, count)
    lambda acc, value: (acc[0] + value, acc[1] + 1),  # 序列操作
    lambda acc1, acc2: (acc1[0] + acc2[0], acc1[1] + acc2[1])  # 合併操作
)
print(result)  # 輸出: (10, 4)

groupByKey()
• 對鍵值對 RDD 中的鍵進行分組,返回一個包含每個鍵及其對應值的迭代器的 RDD。
• 示例:

rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
grouped_rdd = rdd.groupByKey().mapValues(list)
print(grouped_rdd.collect())  # 輸出: [('a', [1, 3]), ('b', [2])]

reduceByKey(func)
• 對鍵值對 RDD 中的每個鍵應用 func 函式,返回一個包含每個鍵及其聚合值的 RDD。
• 示例:

rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
reduced_rdd = rdd.reduceByKey(lambda x, y: x + y)
print(reduced_rdd.collect())  # 輸出: [('a', 4), ('b', 2)]

join(other)
• 對兩個鍵值對 RDD 進行內連線,返回一個包含共同鍵及其對應值的 RDD。
• 示例:

rdd1 = sc.parallelize([("a", 1), ("b", 2)])
rdd2 = sc.parallelize([("a", 3), ("c", 4)])
joined_rdd = rdd1.join(rdd2)
print(joined_rdd.collect())  # 輸出: [('a', (1, 3))]

總結

• Map 操作:對每個元素應用一個函式,返回一個新的 RDD。常見的操作包括 map、flatMap、filter、union 等。
• Reduce 操作:對 RDD 中的所有元素應用一個函式,最終返回一個單一的結果。常見的操作包括 reduce、fold、aggregate、groupByKey、reduceByKey、join 等。

相關文章