python寫hadoop的MapReduce(以數據按要求合併、重排爲例)

現在已有的很多博客demo都是以wordcount爲例,衆所周知這是一個非常簡單的功能,但凡遇到一些高階一點的操作我都會大腦一片空白,今天正好有相關的需求,就來學習了一下。
http://www.zhangdongshengtech.com/article-detials/236
上面的鏈接是記錄頻次的demo,寫的非常的好,相信各位看了它就會了解mapreduce核心的寫法

Intro:wordcount

說在前面:mapreduce程序的調試可以單獨分別運行mapper和reducer,直接在命令行輸入你指定好的輸入格式,就會打印出輸出

mapper.py

輸入文件的形式就是

word1
word2
word1
word3

# coding=utf-8
import sys
 
for line in sys.stdin:
	words = line.strip()
	if not word: continue
	print(word)

reducer.py

這裏實現的就是一個簡單的計數並把頻次寫到文件中的操作。
如果你只需要實現計數操作,那麼只用修改mapper.py的print的值即可

# coding=utf-8
import sys

count = 0
key = ""
current_key = ""

for line in sys.stdin:
    line =  line.rstrip()
    if not line:
        sys.stderr.write("data is wrong")
        sys.exit(1)
    line = line.rstrip()
    items = line.split("\t")
    current_key = items[3]
    cur_timestamp = items[2]
    if current_key == key:
        if cur_timestamp < timestamp:
            print "%s\t%d" % (key, count)
        count = 0
        key = current_key
    count += 1

if key:
    print "%s\t%d" % (key, count)
run.sh

運行環境配置這一塊我可能沒有辦法講清楚,因爲是別人寫好的腳本,我只修改了上面2個代碼
在這裏修改你的輸入路徑和輸出路徑

#!/bin/bash
HADOOP_bin='/your/path/hadoop-2.7.3/bin/hadoop'
INPUT_PATH="input_data"
OUTPUT_PATH="test"

$HADOOP_bin fs -rmr $OUTPUT_PATH


$HADOOP_bin jar /your/path/hadoop-2.7.3/share/hadoop/tools/lib/hadoop-streaming-2.7.3.jar\
    -D mapred.job.priority="VERY_HIGH"\
    -D mapred.reduce.tasks=200\
    -D mapreduce.job.queuename=root.online.default\
    -D mapred.job.map.capacity=400\
    -D mapred.job.reduce.capacity=100\
    -D mapred.job.name="test"\
    -D mapred.textoutputformat.ignoreseparator="true"\
    -input ${INPUT_PATH} \
    -output ${OUTPUT_PATH} \
    -file ./mapper.py\
    -file ./reducer.py\
    -partitioner "org.apache.hadoop.mapred.lib.HashPartitioner"\
    -mapper "python mapper.py"\
    -reducer "python reducer.py"\
    -inputformat "org.apache.hadoop.mapred.TextInputFormat"\
    -outputformat "org.apache.hadoop.mapred.TextOutputFormat"\

Advance:有條件的合併內容

下面來實現把輸入按某一個值進行合併

輸入形式:
key1 value1 value2
key2 value1 value2
key1 value3 value4
輸出形式:
key1 value1 value2 value3 value4…………
key2 value1 value2 …………

mapper.py
#coding=utf8
import json
import sys
#f = open('part-07198', 'r') #調試用,因爲我的mapreduce任務配置在python2下,調試的時候sys.stdin接收不到輸入,所以直接讀文件
for line in sys.stdin:
    line = line.strip()
    if not line:
        continue
    data = line.split('\t', 2) #只區分key,後面的values不做區分
    if len(data) <= 1:
        continue
    print data

這裏如果怕數據有問題可以寫在try except裏,如果還需要對每一行的數據做什麼處理都放在mapper裏處理,當把數據預處理成可以根據某一項進行合併時就print輸出,丟給reducer

reducer.py
import json
import sys
from operator import itemgetter
from itertools import groupby

def read_mapper_output(file, separator='\t'):
    for line in file:
        yield line.rstrip().split(separator, 2)

def main(separator='\t'):
    #f = open('part-07198', 'r') #調試用
    # input comes from STDIN (standard input)
    data = read_mapper_output(sys.stdin, separator=separator)
    for name, group in groupby(data, itemgetter(0)):
        val = []
        for values in group:
            for v in values[1]:
                val.append(v)
        print "%s\t%s"% (values[0], json.dumps(v))

if __name__ == "__main__":
    main()

這裏有兩個函數非常重要,搞懂了它們你就能搞懂如何寫reducer,再複雜的功能你都能變着花實現
groupby
https://blog.csdn.net/Together_CZ/article/details/73042997

key,  group= groupby(iterator, key=func())

將key函數作用於原循環器的各個元素。根據key函數結果,將擁有相同函數結果的元素分到一個新的循環器。每個新的循環器以函數返回結果爲標籤。
這就好像一羣人的身高作爲循環器。我們可以使 用這樣一個key函數: 如果身高大於180,返回"tall";如果身高底於160,返回"short";中間的返回"middle"。最終,所有身高將分爲三個循環器, 即"tall", “short”, “middle”。

這個函數的意思就是說把原來的迭代器中的值按照某一個key聚合
再來複習一下mapreduce的原理:
hadoop框架會自動的將相同的key分配到同一個reducer上,這個key,默認的就是上一個mapper輸出數據的以\t,或者\001分割後的第一部分
看到這裏大概應該就明白了,只要我們把所需要合併的key在mapper中變換到第一位輸出,這樣就能用groupby直接進行聚合
那麼,groupby的輸出是什麼呢?
groupby的輸出有兩部分,一部分是key,另一部分就是同一key下的所有data,這裏的data同樣含有key這個字段
看一下https://blog.csdn.net/LY_ysys629/article/details/72553273這個實例應該就能對groupby的輸出有直觀感受了。
itemgetter
我覺得這篇博客的理解寫的非常好https://blog.csdn.net/qq_22022063/article/details/79019294

作用:itemgetter 用於獲取對象的哪些位置的數據,參數即爲代表位置的序號值,

也就是說itemgetter的參數等於i,就取data[i]的數據,並且它是一個函數,因此可以直接用作groupby的key傳參。

中文字符的處理

我已經被這個坑了兩次了,記錄一下
(1)文件開頭一定要記得#coding=utf8
(2)如果保存的文件是’\u’開頭的,解析的時候用json.loads能直接解析出中文
(3)如果保存的文件是’\x’開頭的。。。可能在解析的時候要decode(‘utf-8’)吧。。。

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