python with  hdfs

hdfs 可以在 linux 本地操作

bin/hdfs dfs -ls /foo

但是这种只能在 命令行 操作。

通常我们需要在程序中实现远程操作,python 是可以的。需要用到一个模块 snakebite,目前仅支持 python2

snakebite 有两种方式远程操作 hdfs,一种是通过命令行,这里不做介绍,另一种是通过 python 脚本实现。

仅需两步:1. 连接 hdfs;2. 执行 各种命令,只是要注意,每条操作都返回一个 Iterator,所以需要写在 for 循环中才能生效。

python 示例

from snakebite.client import Client

client = Client('192.168.10.10', 9000)
for x in client.ls(['/']):
print x # client.mkdir(['/foo2'], create_parent=True) # 没有 for 循环是不行的
for i in client.mkdir(['/foo/f1', '/input4'], create_parent=True): # 创建多个目录,而且 create_parent=True 确保每个目录可以是多层的
print(i)
### 这个 for 循环相当于 挨个 执行创建目录的操作,如果只循环了一次,就只建一个目录 client.delete(['/foo', '/input4'], recurse=True).next() # 删除文件或目录,而且 recurse=True 确保删除多层目录
### 这里只 next 了一下,只删除了 一个目录,input4 并没有被删除 client.copyToLocal(['/usr/input/yanshw/README.txt'], 'e:').next() # 下载文件,下载到 e 盘当前目录,而不是搭建hadoop的服务器【文件是跑在 win 上的】 print client.text(['/usr/input/yanshw/README.txt']).next() # 显示文件,打印出了文件

9000 是 core-site.xml 中 namenode 指定的端口号

这里涉及到一个权限问题:我的 hadoop 集群是在 linux root 搭建的,我的脚本是在 windows 上跑的,windows 主机名 叫 HP,在 操作目录时,会提示 HP 没有权限

当时我为了 测试 以上 API 是否有效,我把 hdfs 根目录的权限改成了 777

bin/hdfs dfs -chmod -R 777 /

然后我发现可以新增和删除目录了,新增的目录 owner 就是 HP

所以,大家在 搭建 集群时就可以考虑后续远程操作的问题了。

python with mapreduce

用 python 写 mapreduce 完成 词频统计

数据文件 test.txt

python|thread|process
python|xlrd|pyinotiy
python|print|c++
c++|java|php
node.js|javascript|go

上传至 hdfs

bin/hadoop fs -put /usr/lib/hadoop-2.6.5/tmp/test.txt /usr/yanshw

数据必须上传至 hdfs 才能使用

1)mapper 文件

import sys

for line in sys.stdin:
words = line.strip().split('|')
for word in words:
print word

从 stdin 逐行读入,拆分,print,这里的 print 相当于 stdout;

mapper 的 stdout 作为 reducer 的 stdin;

hadoop 内部 用  hadoop steaming 实现标准输入输出,使得数据在 map 和 reduce 之间流动

测试 mapper

执行本地测试,确保程序正确

cat tmp/test.txt | python mapper.py

2)reducer 文件

在 reduce 之前 是需要 sort 的,hadoop 内默认是 带 sort 的

sort 其实是个 分区的过程;

hadoop 自动把 不同的 key 发送给不同的 reducer;

虽然 reduce 之前会 分区,sort,发送给不同的 reducer,但 编写 reducer 时,不能认为是 一个分区 的数据,还得按照多个分区进行编写,只是 数据是经过 sort 的

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()

逐行读取 mapper 的 输出,就是一个个的单词;

reducer 逐个累加词的 个数,如果下个词与当前词不同,就把当前词的个数 print,然后统计下个词的个数,依次直至完毕;

测试 reducer

cat tmp/test.txt | python mapper.py |sort|python reducer.py

把 mapper 的输出 进行排序后再 送给 reducer;这也是 hadoop 的默认方式

3)运行 mapreduce

需要用 hadoop-streaming 方式来执行 python

命令行方式

hadoop jar share/hadoop/tools/lib/hadoop-streaming-2.6.5.jar 
-files /usr/lib/hadoop-2.6.5/mapper.py,/usr/lib/hadoop-2.6.5/reducer.py
-mapper 'python mapper.py'
-reducer 'python reducer.py'
-input /usr/input/yanshw
-output /usr/output/yanshuw2

解释如下

hadoop:bin 下的 hadoop,这里设置了环境变量,所以直接 hadoop 就可以了

jar share/hadoop/tools/lib/hadoop-streaming-2.6.5.jar:运行 java streaming 程序

-files:输入 mapper 和 reducer 的绝对路径,hadoop 会把 这俩文件上传到 datanode,用于执行

  // 也可以写 两个 -file mapper  -file reducer

  // 注意两个文件之间不能有空格

-mapper:后面跟命令

-reducer:后面跟命令

  // 这两个命令是执行 python 文件,如果 python 文件中带 shebang,直接写 mapper.py 即可;如果没有,需要写 'python  mapper.py',引号不能省略,无需带路径

-input:hdfs 的输入路径

-output:hdfs 的输出路径,这个路径不能事先存在

完整命令

hadoop jar /usr/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming-2.0.0-mr1-cdh4.1.2.jar \
-input /ngrams \
-output /output-streaming \
-mapper mapper.py \
-combiner reducer.py \
-reducer reducer.py \
-jobconf stream.num.map.output.key.fields=3 \
-jobconf stream.num.reduce.output.key.fields=3 \
-jobconf mapred.reduce.tasks=10 \
-file mapper.py \
-file reducer.py

shell 脚本方式

编写 run.sh 来执行 mapreduce

#!/bin/bash

hadoop fs -rm -r -f /usr/output/yanshw102

hadoop jar share/hadoop/tools/lib/hadoop-streaming-2.6.5.jar \
-file /usr/lib/hadoop-2.6.5/mapper2.py \
-file /usr/lib/hadoop-2.6.5/reducer2.py \
-mapper "python mapper2.py" \
-reducer "python reducer2.py" \
-input /usr/yanshw/test.txt \
-output /usr/output/yanshw102 \

第 1 行 是删除 已经存在的 路径,这个路径用于存放 output,不能事先存在

然后直接执行

run2.sh

执行结束后,下载 output

bin/hdfs dfs -getmerge /usr/output/yanshw102  ttt

直接打印

[root@hadoop10 hadoop-2.6.5]# cat ttt
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

一张图解释 上文 的 mapreduce

mapreduce 实例

背景描述:两个文件,file1.txt 记录学生点击新闻的信息,file2.txt 记录活跃学生的信息;

目标:现在有某一天的这俩个文件,统计这一天 使用  ios/android 的 活跃学生 的 总点击次数

file1.txt

20170001 xiaoming android 331 学费
20170002 xiaohong ios 332 食堂
20170003 xiaohua android 333 考研
20170001 xiaoming android 222 评优
20170001 xiaoming android 225 学费
20170003 xiaohua android 111 期末考试
20170002 xiaohong ios 117 空调安装

file2.txt

20170001 xiaoming 20 android
20170002 xiaohong 19 ios
20170001 xiaoqiang 20 android

难点是同时利用两个文件,如何设计 key,能够很好的进行分区

方案1:把 ios/andriod 设为 key,统计每种手机各个学生的访问量,取出活跃学生的;可以实现,但是这个 分区 之分 2 个区,效率很低

方案2:把 学号 作为 key,统计每个学生的 不同手机的点击次数... 貌似不行

方案3:把 ios/andriod 和 学号 作为 key,然后统计每个 key 中 学号是 活跃学生的 点击次数;其实 直接从 需求看 也能确定这个 key

mapper

ios/andriod 和 学号 作为 key

import sys

def main():
for line in sys.stdin:
data = line.strip().split(" ")
if len(data) == 5: # file1
student_id = data[0]
os_name = data[2]
key = student_id + '_' + os_name
print "\t".join([key,"file1"])
elif len(data) == 4: # file2
student_id = data[0]
os_name = data[-1]
key = student_id + '_' + os_name
print "\t".join([key,"file2"]) if __name__ == "__main__":
main()

reducer

按 key 排序后 喂给 reducer,不同的 key 给到不同的 reducer

在写 reducer 方法时,既定输入是 排序后的 多个 key 的序列

import sys
import json dic_result = {'android': 0, 'ios': 0}
pre_key = "" def post_deal(pre_key):
# 相同key的数据的处理
global dic # pre_key: student_id + '_' + os_name
os_name = pre_key.strip().split("_")[1]
if 'click' in dic and dic['click'] == 'yes' and 'active' in dic and dic['active'] == 'yes': # 点击 的 活跃用户 +1
dic_result[os_name] += 1 def deal(data):
# 对每一行数据的处理
# data:20170001_android file1
global dic if data[1].strip() == "file1":
dic['click'] = 'yes' # file1 代表点击
if data[1].strip() == "file2":
dic['active'] = 'yes' # file2 代表活跃 def pre_deal():
# 预处理,用于存储同一个key的一组value
global dic
dic = dict() def main():
# sys.stdin = ['20170001_android file1',
# '20170001_android file1',
# '20170001_android file1',
# '20170001_android file2',
# '20170001_android file2',
# '20170002_ios file1',
# '20170002_ios file1',
# '20170002_ios file2',
# '20170003_android file1',
# '20170003_android file1'
# ] # sort 之后的
for line in sys.stdin:
data = line.strip().split("\t") key = data[0]
global pre_key if key != pre_key:
# 如果换 key,先给 上一个 key 加1,然后 初始化新 key
if pre_key != "":
post_deal(pre_key) # 计数 # 初始化 for 下一个 key
pre_deal()
pre_key = key deal(data) # 来一个学生我先标记,然后计数
post_deal(pre_key) if pre_key != "":
post_deal(pre_key) print json.dumps(dic_result) if __name__ == "__main__":
main()

本地测试

[root@hadoop10 hadoop-2.6.5]# cat tmp/example1/file1.txt tmp/example1/file2.txt | python tmp/example1/mapper.py | sort| python tmp/example1/reducer.py
{"android": 3, "ios": 2}

hadoop 上 运行 mapreduce

这里有些问题,解决方法 见 下面 进阶和 最后一个链接

具体的解决思路我后面会专门写一篇博客

进阶配置

-jobconf stream.num.map.output.key.fields=2 这行代码用于指定排序的字段,数字2指定map函数输出数据的第几列用于排序,就是例子中的sales字段。  【从 1 开始】

-jobconf num.key.fields.for.partition=1这行代码指定partition字段,数字1指定map函数输出数据的第一列用于分区。

-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner这行代码是调用hadoop streaming包中的分区类,实现分区功能

参考资料:

https://www.jianshu.com/p/70bd81b2956f

《Hadoop with Python》  我的电子书

http://www.zhangdongshengtech.com/article-detials/236   十分清晰地解释了 python 怎么玩 mr

https://www.iteye.com/blog/blackproof-2154523    内容比较广,设计一些不太常用的操作

https://www.cnblogs.com/airnew/p/9574309.html  自定义排序 分区 等的实现

python with hadoop的更多相关文章

  1. 让python在hadoop上跑起来

    duang~好久没有更新博客啦,原因很简单,实习啦-好吧,我过来这边上班表示觉得自己简直弱爆了.第一周,配置环境:第二周,将数据可视化,包括学习了excel2013的一些高大上的技能,例如数据透视表和 ...

  2. 使用Python实现Hadoop MapReduce程序

    转自:使用Python实现Hadoop MapReduce程序 英文原文:Writing an Hadoop MapReduce Program in Python 根据上面两篇文章,下面是我在自己的 ...

  3. python 运行 hadoop 2.0 mapreduce 程序

    要点:#!/usr/bin/python 因为要发送到各个节点,所以py文件必须是可执行的. 1) 统计(所有日志)独立ip数目,即不同ip的总数 ####################本地测试## ...

  4. Python实现Hadoop MapReduce程序

    1.概述 Hadoop Streaming提供了一个便于进行MapReduce编程的工具包,使用它可以基于一些可执行命令.脚本语言或其他编程语言来实现Mapper和 Reducer,从而充分利用Had ...

  5. [python]使用python实现Hadoop MapReduce程序:计算一组数据的均值和方差

    这是参照<机器学习实战>中第15章“大数据与MapReduce”的内容,因为作者写作时hadoop版本和现在的版本相差很大,所以在Hadoop上运行python写的MapReduce程序时 ...

  6. python 实现Hadoop的partitioner和二次排序

    我们知道,一个典型的Map-Reduce过程包 括:Input->Map->Partition->Reduce->Output. Partition负责把Map任务输出的中间结 ...

  7. Hadoop(三)通过C#/python实现Hadoop MapReduce

    MapReduce Hadoop中将数据切分成块存在HDFS不同的DataNode中,如果想汇总,按照常规想法就是,移动数据到统计程序:先把数据读取到一个程序中,再进行汇总. 但是HDFS存的数据量非 ...

  8. python 遍历hadoop, 跟指定列表对比 包含列表中值的取出。

    import sys import tstree fname = 'high_freq_site.list' tree = tstree.TernarySearchTrie() tree.loadDa ...

  9. Hadoop Streaming例子(python)

    以前总是用java写一些MapReduce程序现举一个例子使用Python通过Hadoop Streaming来实现Mapreduce. 任务描述: HDFS上有两个目录/a和/b,里面数据均有3列, ...

随机推荐

  1. luoguP2285 [HNOI2004]打鼹鼠 x

    P2285 [HNOI2004]打鼹鼠 题目描述 鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的.根据这个特点阿牛编写了一个打鼹鼠的游戏:在一个n*n的网格中,在某 ...

  2. HGOI20190812 省常中互测5

    Task 1 辩论 有N 个参加辩论的候选人,每个人对这两个议题都有明确的态度,支持或反对.作为组织者,小D 认真研究了每个候选人,并给每个人评估了一个非负的活跃度,他想让活跃度之和尽可能大.选出的候 ...

  3. BZOJ 2655 calc (组合计数、DP、多项式、拉格朗日插值)

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=2655 题解 据说有一种神仙容斥做法,但我不会. 以及貌似网上大多数人的dp和我的做法都不 ...

  4. sleep() 、join()、yield()有什么区别

    1sleep()方法 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行).此操作受到系统计时器和调度程序精准和准确性的影响,让其他线程有机会继续执行,但是它不释放对象锁.也就是如果有synchron ...

  5. LinkedList类源码浅析(一)

    1.先来看一看LinkedList类的字段和构造方法 size记录链表的长度,first永远指向链表的第一个元素,last永远指向链表的最后一个元素 提供两个构造方法,一个无参的构造方法,一个接受一个 ...

  6. 在mac中,npm安装或者卸载失败,提示没有权限

    在终端输入 sudo chown -R $USER /usr/local 输入开机密码

  7. C++入门经典-例3.21-goto语句实现循环

    1:代码如下: // 3.21.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> usin ...

  8. Java期末课程学习汇总。

    本学期面向对象与Java程序设计课程已经结束了,给自己学习来个总结. 本学期过的非常快,不得不说这一学期学到的东西很少,感觉自己的进步很小. 而且感觉自己总少了点什么,在写这篇总结前,我认真想了,很多 ...

  9. ffmpeg修复时间戳

    ffmpeg -re -i e:/media/baifa.mp4 -filter_complex -hls_wrap -hls_time d:/demo/hls/cctv13/playlist.m3u ...

  10. C++如何限制对象在堆上或栈上生成

    1,限制类的对象只能生成在栈上 将 operator new 各种原型设为私有 #include <iostream> class OnlyOnStack { public: OnlyOn ...