@ Manjunath Ballur接受的答案很不错。但是,Map Reduce必须与简单性结合使用。建议您不要检查每一行的标题。
我正在尝试获取一个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);
}
}
我假设列标题是字母,列值是数字。
实现此目的的方法之一是使用DistributedCache
。步骤如下:
Job::addCacheFile()
将此文件添加到分布式缓存中>setup()
方法中,从分布式缓存访问此文件。解析文件的内容并将其存储在columnHeader
列表中。map()
方法中,检查每个记录中的值是否与标题(存储在columnnHeader
列表中)匹配。如果是,则忽略该记录(因为该记录仅包含标题)。如果否,则将值与列标题一起发出。 这是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”添加到分布式缓存:
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
@ Manjunath Ballur接受的答案很不错。但是,Map Reduce必须与简单性结合使用。建议您不要检查每一行的标题。