从Java映射的头文件中减少精简代码

问题描述 投票:1回答:2

我正在尝试获取一个csv文件的摘要,文件的第一行是标题。有没有一种方法可以使每列的值都以其标题名称作为来自Java代码的键值对。

例如:输入文件就像

A,B,C,D

1,2,3,4

5,6,7,8

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

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

这是我的映射器的外观:

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);
        }      
    }
java hadoop mapreduce
2个回答
1
投票

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

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

  1. 创建包含列标题的文件。
  2. 在驱动程序代码中,通过调用Job::addCacheFile()将此文件添加到分布式缓存中>
  3. 在映射器的setup()方法中,从分布式缓存访问此文件。解析文件的内容并将其存储在columnHeader列表中。
  4. map()方法中,检查每个记录中的值是否与标题(存储在columnnHeader列表中)匹配。如果是,则忽略该记录(因为该记录仅包含标题)。如果否,则将值与列标题一起发出。
  5. 这是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);
}

驱动器逻辑:

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

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

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

映射器逻辑:

  • 覆盖setup()方法。
  • setup()方法中读取“ header.txt”(已放入驱动程序的分布式缓存中)。
  • 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

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

一种方法是编写一个可以为您完成此工作的自定义InputFormat


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.