从java地图中跳过标题减少代码

时间:2015-12-21 16:41:59

标签: java hadoop mapreduce

我正在尝试获取csv文件的摘要,文件的第一行是标题。有没有办法将每个列的值及其标题名称作为Java代码中的键值对。

例如:输入文件就像

A,B,C,d

1,2,3,4

-5,6,7,8-

我希望mapper的输出为(A,1),(B,2),(C,3),(D,4),(A,5),....

注意:我尝试使用覆盖Mapper类中的run函数来跳过第一行。但据我所知,每次输入分割都会调用run函数,因此不适合我的需要。对此的任何帮助都将非常感激。

这是我的mapper看起来的方式:

public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        String line = value.toString();
        String[] splits = line.split(",",-1);
        int length = splits.length;
    //  count = 0;

        for (int i = 0; i < length; i++) {
            columnName.set(header[i]);      
            context.write(columnName, new Text(splits[i]+""));
        }

    }

    public void run(Context context) throws IOException, InterruptedException
    {        
        setup(context); 
        try 
        {

            if (context.nextKeyValue())
            { 

                Text columnHeader = context.getCurrentValue();
                header =  columnHeader.toString().split(",");

            }    
            while (context.nextKeyValue()) 
            {
                map(context.getCurrentKey(), context.getCurrentValue(), context);
            }
        } 
        finally 
        {
            cleanup(context);
        }      
    }

2 个答案:

答案 0 :(得分:1)

我假设列标题是字母表,列值是数字。

实现此目标的方法之一是使用DistributedCache。 以下是步骤:

  1. 创建包含列标题的文件。
  2. 在驱动程序代码中,通过调用Job::addCacheFile()
  3. 将此文件添加到分布式缓存中
  4. 在映射器的setup()方法中,从分布式缓存中访问此文件。将文件内容解析并存储在columnHeader列表中。
  5. map()方法中,检查每条记录中的值是否与标题匹配(存储在columnnHeader列表中)。如果是,则忽略该记录(因为记录只包含标题)。如果不是,则将值与列标题一起发出。
  6. 这就是Mapper和Driver代码的样子:

    <强>驱动程序:

    public static void main(String[] args) throws Exception {
    
        Configuration conf = new Configuration();
    
        Job job = Job.getInstance(conf, "HeaderParser");
        job.setJarByClass(WordCount.class);
        job.setMapperClass(HeaderParserMapper.class);
    
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);
    
        job.addCacheFile(new URI("/in/header.txt#header.txt"));
        FileInputFormat.addInputPath(job, new Path("/in/in7.txt"));
        FileOutputFormat.setOutputPath(job, new Path("/out/"));
    
        System.exit(job.waitForCompletion(true) ? 0:1);
    }
    

    驱动程序逻辑:

    • 复制&#34; header.txt&#34; (仅包含一行:A,B,C,D)到HDFS
    • 在驱动程序中,添加&#34; header.txt&#34;通过执行以下语句来分布式缓存:

      job.addCacheFile(new URI("/in/header.txt#header.txt"));
      

    <强>映射器:

    public static class HeaderParserMapper
            extends Mapper<LongWritable, Text , Text, NullWritable>{
    
        String[] headerList;
        String header;
    
        @Override
        protected void setup(Mapper.Context context) throws IOException, InterruptedException {
            BufferedReader bufferedReader = new BufferedReader(new FileReader("header.txt"));
            header = bufferedReader.readLine();
            headerList = header.split(",");
        }
    
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
    
            String line = value.toString();
            String[] values = line.split(",");
    
            if(headerList.length == values.length && !header.equals(line)) {
                for(int i = 0; i < values.length; i++)
                    context.write(new Text(headerList[i] + "," + values[i]), NullWritable.get());
            }
        }
    }
    

    Mapper Logic:

    • 覆盖setup()方法。
    • 阅读&#34; header.txt&#34; (在setup()方法中放置在驱动程序中的分布式缓存中)。
    • map()方法中,检查该行是否与标题匹配。如果是,则忽略该行。否则,输出标题和值为(h1,v1),(h2,v2),(h3,v3)和(h4,v4)。

    我按以下输入运行了这个程序:

    A,B,C,D
    1,2,3,4
    5,6,7,8
    

    我得到了以下输出(其中值与各自的标题匹配):

    A,1
    A,5
    B,2
    B,6
    C,3
    C,7
    D,4
    D,8
    

答案 1 :(得分:0)

@Manjunath Ballur接受的答案是一个很好的技巧。但是,Map Reduce必须与简单性结合使用。不建议您检查每一行的标题。

一种可行的方法是编写一个自定义InputFormat来为您完成这项工作