hadoop streaming reduce端join的python两种实现方式

实现student和course数据表的join操作,以学生编号(sno)为连接字段

  • 测试数据

    • student.txt文件
    
    #以一个空格分隔
    
    
    #学生编号   姓名
    
    
    #sno    sname
    
    01 lily
    02 tom
    03 jack
    04 rose
    • course.txt文件

      
      #以一个空格分隔
      
      
      #学生编号    课程名  课程成绩
      
      
      #sno cname grade 
      
      01 English 80
      01 Math 90
      02 English 82
      02 Math 95
    • 最终reduce端连接结果

    sno  sname   cname   grade
    01   lily    English   80
    01   lily    Math      90
    02   tom     English   82
    02   tom     Math      95
  • 思路1

    按照不同的文件输出时对数据添加标记,然后通过配置map阶段的排序字段,保证学生信息出现在课程成绩信息的最上面,reduce中,发现是学生信息时,对后面接收的课程信息进行join并输出。步骤如下:

    1:map端有多个输入文件,以os.environ[‘map_input_file’]获取输入的文件信息,如果是student.txt文件的输入,则在输出中增加一个’0’,是course.txt的输入,在输出中增加一个’1’.

    2: 以sno为key进行partation,确保将同一个学生的信息和课程信息分配到同一个reduce中。同时以”sno,标识” 作为map阶段输出的排序依据(以\t分隔的前两列),确保在reduce中同一个学生的学生信息在课程信息的上面(sno,0在所有sno,1的行前面)。

    3: 在reduce中,遇到0标记的行保存sno和sname信息,遇到1标记的行将sno和sname和当前行的课程信息一起输出。

    • 示例代码

      mapper和reducer代码

          #! /usr/bin/env python
          # coding:utf-8
          import os
          import sys
      
          def read_input(file,sepr=' '):
              for line in file:
                  line = line.split(sepr)
                  yield line
      
          def mapper():
              filepath = os.environ['map_input_file']
              filename = os.path.split(filepath)[1]
              lines = read_input(sys.stdin)
              for data in lines:
                  if data[0].strip() == "":
                      continue
      
                  if "student.txt" == filename:
                      if len(data) != 2:
                          continue
                      else:
                          print "%s\t%s\t%s" % (data[0],"0",data[1].strip())
                  else:
                      if len(data) != 4:
                          continue
                      else:
                          print "%s\t%s\t%s\t%s" %(data[0],"1",data[2].strip(),data[3].strip())
      
          def reducer():
              sno = None
              sname = None
      
              line = read_input(sys.stdin,'\t')
              for data in line:
                  if (len(data) !=3 and len(data) !=4 ) or data[0].split() == "":
                      continue
                  if data[0] != sno:
                      sno = data[0]
                      if data[1] == "0":
                          sname = data[2].strip('\n')
                  else:
                      cname = data[2]
                      cnum = data[3].strip('\n')
                      print "%s\t%s\t%s\t%s" % (sno,sname,cname,cnum)
      
      
      
          if __name__ == "__main__":
              d = {"mapper":mapper,"reducer":reducer}
              if sys.argv[1] in d:
                  d[sys.argv[1]]()

      work.bash

      
      #! /bin/bash
      
      
      #定义map和reduce的目录
      
      export WORK_PATH=/var/tmp/reduce_join
      
      #执行stream的jar包地址
      
      stream=$HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar
      
      #数据输入目录
      
      input=/lcy/reduce_join/input
      
      #输出结果目录
      
      output=/lcy/reduce_join/output
      
      
      #删除mr输出目录
      
      if $HADOOP_HOME/bin/hdfs dfs -test -d $output
      then
          $HADOOP_HOME/bin/hdfs dfs -rm -r $output
      fi
      
      
      #执行mapreduce程序
      
      $HADOOP_HOME/bin/hadoop jar $stream \
      -D mapreduce.job.reduces=2 \        #设置reduce个数
      -D num.key.fields.for.partition=1   \   #设置第一个字段为分区字段(默认使用\t作为map输出分隔符)
      -D stream.num.map.output.key.fields=2 \  #设置map的输出以前两个字段作为排序依据,这个配置很重要,否则reduce的输入格式不正确
      -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \  #和partition参数一起使用
      -input $input/* \
      -output $output \
      -mapper "python mr.py mapper" \
      -reducer "python mr.py reducer" \
      -file $WORK_PATH/mr.py
  • 思路2

    在reduce中,将学生信息和课程成绩信息保存到内存中,等遍历完一个学生后,对两个数据进行join操作。这种方式可以让学生信息在课程信息的后面(因为经过partation和shuffle后,同一个sno的学生信息和课程成绩信息会是连续的,要么学生信息在上面,要么学生信息在下面)步骤如下:

    1:同思路1的第一步

    2:以sno为key进行partation,确保将同一个学生的信息和课程信息分配到同一个reduce中。其中map端的排序可以不用设置了。

    3:reduce中,遇到0标记的行保存sno和sname信息,遇到1标记的行,将cname和grade组成list并追加到arr中。遇到新的sno时遍历arr,并和sno,sname一起输出。

    • 测试代码

    mapper和reducer

    
    #! /usr/bin/env python
    
    
    # coding:utf-8
    
    import os
    import sys
    
    def read_input(file,sepr=' '):
        for line in file:
            line = line.split(sepr)
            yield line
    
    def mapper():
        filepath = os.environ['map_input_file']
        filename = os.path.split(filepath)[1]
        lines = read_input(sys.stdin)
        for data in lines:
            if data[0].strip() == "":
                continue
    
            if "student.txt" == filename:
                if len(data) != 2:
                    continue
                else:
                    print "%s\t%s\t%s" % (data[0],"0",data[1].strip())
            else:
                if len(data) != 3:
                    continue
                else:
                    print "%s\t%s\t%s\t%s" %(data[0],"1",data[1].strip(),data[2].strip())
    
    def reducer():
        sno = None
        sname = None
        tpl = []
        line = read_input(sys.stdin,'\t')
        for data in line:
            if (len(data) !=3 and len(data) !=4 ) or data[0].split() == "":
                continue
    
            if data[0] != sno:
                #遇到新的sno时,进行连接输出
                if sno != None and len(tpl) > 0:
                    for row in tpl:
                        print '%s\t%s\t%s\t%s' % (sno,sname,row[0],row[1])
                    tpl = []   #清空list,否则会变成笛卡尔积
    
                sno = data[0]
                if data[1] == "0":
                    sname = data[2].strip('\n')
                else:
                    tpl.append([data[2].strip(),data[3].strip()])
            else:
                if data[1] == "0":
                    sname = data[2].strip('\n')
                else:
                    tpl.append([data[2].strip(),data[3].strip()])
    
        if sno != None and len(tpl) > 0:
            for row in tpl:
                print '%s\t%s\t%s\t%s' % (sno,sname,row[0],row[1])
    
    
    
    if __name__ == "__main__":
        d = {"mapper":mapper,"reducer":reducer}
        if sys.argv[1] in d:
            d[sys.argv[1]]()

    work.bash

    
    #! /bin/bash
    
    
    #定义map和reduce的目录
    
    export WORK_PATH=/var/tmp/reduce_join2
    
    #执行stream的jar包地址
    
    stream=$HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar
    
    #数据输入目录
    
    input=/lcy/reduce_join2/input
    
    #输出结果目录
    
    output=/lcy/reduce_join2/output
    
    
    #删除mr输出目录
    
    if $HADOOP_HOME/bin/hdfs dfs -test -d $output
    then
        $HADOOP_HOME/bin/hdfs dfs -rm -r $output
    fi
    
    
    #执行mapreduce程序
    
    $HADOOP_HOME/bin/hadoop jar $stream \
    -D mapreduce.job.reduces=2 \
    -D num.key.fields.for.partition=1   \
    -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
    -input $input/* \
    -output $output \
    -mapper "python mr.py mapper" \
    -reducer "python mr.py reducer" \
    -file $WORK_PATH/mr.py
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值