问题:我们想要获取存储在文本文件中的工资的平均值。假设该文件包含firstname,lastname和salary。让我们说我们希望为所有规模在美国的公司做到这一点。一个新文件将在新的一天开始,即4月29日输入的所有工资都在一个名为April29.txt的文件中,所有在4月30日输入的工资都在标题为April30.text的文件中,依此类推。您可以想象每天的行号都不同。
目标:使用mapreduce计算每个文件的平均工资。
现在我到处都看到做平均值的总体建议是这样的: map一次读取一行并输出“key”,value 因为只有一个键 - “键”所有输出都转到ONE reducer,我们使用for循环来计算平均值。
这种方法很棒,只是文件越大,计算时间就越差。有没有办法改善这种情况?我没有找到解决这种情况的例子,但如果你知道一些,请分享一个链接。提前谢谢。
答案 0 :(得分:3)
这绝对可以更有效地完成。
现在,我们知道Mapper
有一个map
方法可以覆盖。但是,它也有一个cleanup
。查看映射器的来源,您会看到:
public void run(Context context) throws IOException, InterruptedException {
setup(context);
while (context.nextKeyValue()) {
map(context.getCurrentKey(), context.getCurrentValue(), context);
}
cleanup(context);
}
因此我们可以使用这种清理方法来优化我们的平均代码。
首先,您需要一个存储两件事的自定义可写,count
和sum
。我们称之为AverageWritable
。然后,我们将在mapper中执行类似的操作:
AverageWritable avg = new AverageWritable();
public void map(LongWritable key, Text value, Context ctx) {
long salary = [ ... code to get salary... ]
avg.addCount(1);
avg.addSum(salary);
}
public void cleanup(Context ctx) {
ctx.write(CONSTANT_KEY, avg);
}
reducer和combiner代码应该很容易从这里弄清楚。
答案 1 :(得分:0)
我很好奇,因为我们可以使用hadoop提供的计数器。 假设我们构建两个计数器,如
public enum CountCounters { 计数器 }
public enum SumCounters { 计数器 }
在mapper的map方法中,我们可以访问计数器并递增它。
context.getCounter(CountCounters.Counter).increment(1); context.getCounter(SumCounters.Counter).increment();
最后我们将
job.getCounters()findCounter(CountCounters.Counter).getValue(); 。job.getCounters()findCounter(SumCounters.Counter).getValue();
找到平均值