使用python写一个mapreduce程序,来统计一个文件中的单词出现的个数
1、创建示例文件 words
python|thread|process
python|xlrd|pyinotiy
python|print|c++
c++|java|php
node.js|javascript|go
将文件上传至hsdf上 执行命令 hadoop fs -put words /user/hive/warehouse/test.db/zds
你也许会质疑,这么简单的工作何须使用mapreduce呢,没错,如果一个文件里只有这么一丁点数据,确实没有必要使用mapreduce来处理,但这毕竟只是一个实例,假设这个文件有几个T那么大,你还能用单机的程序进行处理么,不论是空间还是时间,都是无法承受的,
2、编写mapper文件
新建文件 mapper.py 内容为
# coding=utf-8
import sys
for line in sys.stdin:
words = line.strip().split('|')
for word in words:
print word
这里的关键是使用 HadoopStreaming 来让数据在map 和 reduce之间传递数据,使用sys.stdin获得输入数据,而print 等同于 sys.stdout ,作为标准输出
这段代码的作用是一行一行的读取数据,然后用竖线分割,在逐个输出
2.2 测试mapper
执行命令 cat words | python mapper.py ,words 是本地文件,我现在本地进行测试,确保程序是没有明显bug的,免得在hadoop集群上运行程序时出错,毕竟那是要占用集群资源的,有问题要提前发现
3、编写reducer.py
# coding=utf-8
import sys
def get_count():
first_flag = 0
count = 0
for line in sys.stdin:
word = line.strip()
if first_flag == 0:
first_flag = 1
old_key = word
if old_key != word:
print "{word}\t{count}".format(word=old_key, count=count)
# 复位
old_key = word
count = 0
count += 1
if first_flag == 1:
print "{word}\t{count}".format(word=old_key, count=count)
if __name__ == '__main__':
get_count()
需要明确一点,hadoop框架会自动的将相同的key分配到同一个reducer上,这个key,默认的就是上一个mapper输出数据的以\t,或者\001分割后的第一部分,我写的mapper中,直接输出了word,那么,这些单词就成为key
程序中有一个old_key变量,不同的key的数据会被分到同一个reducer上,因此,程序需要识别出不同的key,来进行计数 ,此外还要注意不要忘记最后一个key的处理
执行命令 cat words | python mapper.py |sort|python reducer.py
输出结果为
c++ 2
go 1
java 1
javascript 1
node.js 1
php 1
print 1
process 1
pyinotiy 1
python 3
thread 1
xlrd 1
至此,程序已经可以正常运行,但是reducer的程序非常不符合python的精神,苦涩难懂,我要对它进行一番修改
# coding=utf-8
import sys
from operator import itemgetter
from itertools import groupby
def read_mapper_output(files, separator='\t'):
for line in files:
yield line.strip().split(separator, 1)
def main():
data = read_mapper_output(sys.stdin)
for key, data in groupby(data, itemgetter(0)):
count = 0
for value in data:
count += 1
print "{word}\t{count}".format(word=key, count=count)
if __name__ == '__main__':
main()
这样修改后,看起来简洁了许多,不过如果你没有很深的python基础功底,可能更难看得懂
4、 在hadoop集群上运行代码
编写run.sh脚本
#!/bin/bash/
(1) hadoop fs -rm -r -f /user/hive/warehouse/test_tmp.db/zds/wordcount
(2) hadoop jar /opt/lib/hadoop-mapreduce/hadoop-streaming.jar \
(3) -libjars /opt/lib/hive/lib/hive-exec-1.1.0-cdh5.4.8.jar \
(4) -jobconf mapreduce.reduce.shuffle.memory.limit.percent=0.1 \
(5) -jobconf mapreduce.reduce.shuffle.input.buffer.percent=0.3 \
(6) -jobconf mapreduce.map.memory.mb=512 \
(7) -jobconf mapreduce.reduce.memory.mb=512 \
(8) -jobconf mapred.map.capacity=100 \
(9) -jobconf mapred.reduce.capacity=100 \
(10) -jobconf mapred.job.name=test_word_count \
(11) -file mapper.py \
(12) -file reducer.py \
(13) -mapper "python mapper.py" \
(14) -reducer "python reducer.py" \
(15) -input /user/hive/warehouse/test_tmp.db/zds/words \
(16) -output /user/hive/warehouse/test_tmp.db/zds/wordcount \
先看16,规定了输出,而1 呢,则是删除文件,不然第二次运行的时候,由于文件存在会终止运行
2 是指定 hadoop-streaming.jar,具体的位置,要根据你自己机器上的位置而定
3 是指定 libjars ,具体的也要根据你机器上的位置而定
4 到10 指定了运行时的各项参数
11 ,12 指定了要分发到集群上的文件
13,14 指定了mapper和reduce 要运行的程序
15 指定了输入文件
执行结束后,将文件从hadoop上下载下来
hadoop fs -getmerge /user/hive/warehouse/test_tmp/zds/wordcount wordcount
cat wordcount 内容为
xlrd 1
print 1
python 3
javascript 1
process 1
c++ 2
pyinotiy 1
java 1
php 1
go 1
thread 1
node.js 1