如何在mapreduce中做平均值

时间:2014-04-29 17:39:03

标签: hadoop mapreduce

问题:我们想要获取存储在文本文件中的工资的平均值。假设该文件包含firstname,lastname和salary。让我们说我们希望为所有规模在美国的公司做到这一点。一个新文件将在新的一天开始,即4月29日输入的所有工资都在一个名为April29.txt的文件中,所有在4月30日输入的工资都在标题为April30.text的文件中,依此类推。您可以想象每天的行号都不同。

目标:使用mapreduce计算每个文件的平均工资。

现在我到处都看到做平均值的总体建议是这样的: map一次读取一行并输出“key”,value 因为只有一个键 - “键”所有输出都转到ONE reducer,我们使用for循环来计算平均值。

这种方法很棒,只是文件越大,计算时间就越差。有没有办法改善这种情况?我没有找到解决这种情况的例子,但如果你知道一些,请分享一个链接。提前谢谢。

2 个答案:

答案 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);
}

因此我们可以使用这种清理方法来优化我们的平均代码。

首先,您需要一个存储两件事的自定义可写,countsum。我们称之为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();

找到平均值