【问题标题】:MapReduce - Reducer giving wrong output dateMapReduce - 减速器给出错误的输出日期
【发布时间】:2014-07-16 19:41:42
【问题描述】:

我编写了一个 MapReduce 程序来解析 CSV 中的值。

数据集如下——

PRAVEEN,40020,宝贝,026A2,12/04/2015

PRAVEEN,40020,TOY,0383,1/04/2014

PRAVEEN,2727272,BOOK,03383,03/14/2013

PRAVEEN,22636,BIKE,7373737,12/24/2012

我的 Map 函数正在读取 CSV 中的第一个值(即用户名)作为 KEY 和最后一个值,即日期作为 VALUE

我的 Reduce 功能也很简单,我必须从 VALUES 列表中为特定 KEY ie UserName 选择最新日期作为 VALUE

代码如下-

  package com.test.mapreduce;
  import java.io.IOException;
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;

  import org.apache.hadoop.conf.Configuration;
  import org.apache.hadoop.conf.Configured;
  import org.apache.hadoop.fs.Path;
  import org.apache.hadoop.io.IntWritable;
  import org.apache.hadoop.io.LongWritable;
  import org.apache.hadoop.io.Text;
  import org.apache.hadoop.mapred.FileInputFormat;
  import org.apache.hadoop.mapred.FileOutputFormat;
  import org.apache.hadoop.mapred.JobClient;
  import org.apache.hadoop.mapred.JobConf;
  import org.apache.hadoop.mapred.KeyValueTextInputFormat;
  import org.apache.hadoop.mapred.MapReduceBase;
  import org.apache.hadoop.mapred.Mapper;
  import org.apache.hadoop.mapred.OutputCollector;
  import org.apache.hadoop.mapred.Reducer;
  import org.apache.hadoop.mapred.Reporter;
  import org.apache.hadoop.mapred.TextInputFormat;
  import org.apache.hadoop.mapred.TextOutputFormat;
  import org.apache.hadoop.util.Tool;
  import org.apache.hadoop.util.ToolRunner;





 public class RetailCustomerAnalysis_2 extends Configured implements Tool {
             public static class MapClass extends MapReduceBase
             implements Mapper<LongWritable, Text, Text, Text> {

      private Text key1 = new Text();
      private Text value1 = new Text();
      private int noofFields = 5;



 public void map(LongWritable key, Text value,
                 OutputCollector<Text, Text> output,
                 Reporter reporter) throws IOException {

        String line = value.toString().replaceAll("\\s+","");
        String[] split = line.split(",");


        if(split.length!=noofFields){
        return;
        }

        else {
            key1.set(split[0].toString().trim()); 
            value1.set(split[4].toString().trim());
            System.out.println(split[4].toString().trim());
            output.collect(key1, value1);
     }
    }
  }

 public static class Reduce extends MapReduceBase
 implements Reducer<Text, Text, Text, Text> {

 public void reduce(Text key, Iterator<Text> values,
                    OutputCollector<Text, Text> output,
                    Reporter reporter) throws IOException {

     SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
     Date date = new Date();

     List<Text> dateList = new ArrayList<Text>();

     for(Iterator<Text> it = values; it.hasNext();) {
         // add the values in the arrayList
         dateList.add((Text) it.next());
     }


     if(dateList.size()==1){ //If the mapper output has only one date , then select that date 
                             // as the VALUE
     try  {
            date = formatter.parse(dateList.get(0).toString());
          } catch (ParseException e) {
            e.printStackTrace();
        }
     } //If part ends 

     else {
             try {
               date = formatter.parse(dateList.get(0).toString()); 
                      //select the first date from list
             } catch (ParseException e1) {
               e1.printStackTrace();
             }

             for(int i=0 ; i <dateList.size();++i){
                   try {
                   //compare the selected date with the rest of the dates in the list.
                   if((formatter.parse(dateList.get(i).toString())).compareTo(date)>0){
                       date=formatter.parse(dateList.get(i).toString());
                       // getting the max date from the list
                        }
                   }
                   catch (ParseException e) {
                  e.printStackTrace();
                }
             } //for loops ends
     }  // else part ends    

     Text value = new Text(date.toString());
       output.collect(key, value);
      }
  }



 public int run(String[] args) throws Exception {
 Configuration conf = getConf();

 JobConf job = new JobConf(conf, RetailCustomerAnalysis_2.class);

 Path in = new Path(args[0]);
 Path out = new Path(args[1]);
 FileInputFormat.setInputPaths(job, in);
 FileOutputFormat.setOutputPath(job, out);

 job.setJobName("RetailCustomerAnalysis_2");
 job.setMapperClass(MapClass.class);
 job.setReducerClass(Reduce.class);

 job.setInputFormat(TextInputFormat.class);
 job.setOutputFormat(TextOutputFormat.class);
 job.setOutputKeyClass(Text.class);
 job.setOutputValueClass(Text.class);
 job.set("key.value.separator.in.input.line", ",");

 JobClient.runJob(job);
 return 0;
  }

public static void main(String[] args) throws Exception { 
 int res = ToolRunner.run(new Configuration(), new RetailCustomerAnalysis_2(), args);

 System.exit(res);
 }

 }

但是我从列表中得到随机日期作为结果。谁能帮忙。

【问题讨论】:

    标签: java csv hadoop mapreduce


    【解决方案1】:

    代码大部分是正确的。减速器的实现必须稍作修改。下面的代码截图创建了问题

    for(Iterator<Text> it = values; it.hasNext();) {
       // add the values in the arrayList
       dateList.add((Text) it.next());
    }
    

    在上面的代码中,sn -p 每次迭代都使用相同的值对象,只是改变了它们的内容。

    例如,假设 Mapreduce 使用以下输入运行

    PRAVEEN,4002013454,宝贝,026A12,12/04/2015

    PRAVEEN,4002013454,TOY,020383,1/04/2014

    PRAVEEN,2727272727272,BOOK,03383,03/14/2013

    PRAVEEN,2263637373,BIKE,7373737,12/24/2012

    在reduce方法中,'dateList'对象元素在for循环完成后具有值(12/24/2012、12/24/2012、12/24/2012、12/24/2012)。这会导致剩余代码执行不正确,最终输出错误。

    改为如下更改代码

    public static class Reduce extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
    
        public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {
    
            SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
            Date date = new Date();
            //-----Modified section START-----------
            List<String> dateList = new ArrayList<String>();
    
            for(Iterator<Text> it = values; it.hasNext();) {
                // add the values in the arrayList
                dateList.add(((Text)it.next()).toString());
            }
            //----Modified section END--------------
            if(dateList.size()==1){ //If the mapper output has only one date , then select that date 
                // as the VALUE
                try  {
                    date = formatter.parse(dateList.get(0).toString());
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            } //If part ends 
            else {
                String str = dateList.get(0).toString();
                try {
    
                    date = formatter.parse(dateList.get(0).toString());
                    //select the first date from list
                } catch (ParseException e1) {
                    e1.printStackTrace();
                }
    
                for(int i=0 ; i <dateList.size();++i){
                    try {
                        //compare the selected date with the rest of the dates in the list.
                        if((formatter.parse(dateList.get(i).toString())).compareTo(date)>0){
                            date=formatter.parse(dateList.get(i).toString());
                            // getting the max date from the list
                        }
                    }
                    catch (ParseException e) {
                        e.printStackTrace();
                    }
                } //for loops ends
            }  // else part ends    
    
            Text value = new Text(date.toString());
            output.collect(key, value);
        }
    }
    

    请参考Hadoop Reducer Values in Memory?了解更多关于map、reduce方法中对象引用的详细信息。

    【讨论】:

    • 您好,非常感谢,它解决了我的问题,但仍然无法理解它是如何工作的,因为您几乎没有更改错误 sn-p 中的代码
    • @Rahul,问题在于对象引用。在将“文本”对象添加到 dateList 时的代码中,所有文本对象都指向堆中的单个内存位置。 (这是因为 MapReduce 框架重用了对象并且只改变了状态)。在最后一次迭代中,Text 对象值设置为 2012 年 12 月 24 日。这意味着 dateList 中的所有元素的值为 12/24/2012。
    • 感谢您的解释。 :) 但是由于错误的 sn-p 我从列表中获得了第一个值作为“结果”日期。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    • 2016-09-10
    • 2013-05-30
    • 1970-01-01
    • 2023-02-10
    相关资源
    最近更新 更多