Text outputKey = new Text();
Text outputValue = new Text();
//calculate tf-idf of every word in every document)
public static class CalReducer extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
// Note: key is a word, values are in the form of
// (filename=frequency)
Map<String, Integer> tfs = new HashMap<>();
for (Text value: values) {
String[] valueParts = value.split("=");
tfs.put(valueParts[0], Integer.parseInt(valueParts[1])); //do the necessary checks here
}
int numDocs = context.getInt("noOfDocuments"); //set this in the Driver, if you know it already, or set a counter in the mapper to get it here using getCounter()
double IDF = Math.log10((double)numDocs/tfs.keySet().size());
// for every filename=frequency in the value, compute tf-idf of this
// word in filename and output (word@filename, tfidf)
for (String file : tfs.keySet()) {
outputKey.set(key.toString()+"@"+file);
outputValue.set(new String(tfs.get(file)*IDF)); //you could also set the outputValue to be a DoubleWritable
context.write(outputKey, outputValue);
}
}
}
如果您将 tf 定义为 frequency / maxFrequency,您可以在第一个循环中找到 maxFrequency 并相应地更改 outputValue。
如果要尝试单循环解决方案,则需要获取IDF,因此需要获取输入的编号values。
你可以在 Java 8 中使用:
long DF = values.spliterator().getExactSizeIfKnown();
double IDF = Math.log10((double)numDocs/DF);
如this post 中的建议,或遵循同一帖子中不使用循环的其他建议(否则,您可以遵循上一个答案)。
在这种情况下,您的代码将是(我没有尝试过):
Text outputKey = new Text();
Text outputValue = new Text();
//calculate tf-idf of every word in every document)
public static class CalReducer extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
int numDocs = context.getInt("noOfDocuments"); //set this in the Driver, if you know it already, or set a counter in the mapper to get it here using getCounter()
long DF = values.spliterator().getExactSizeIfKnown();
double IDF = Math.log10((double)numDocs/DF);
// Note: key is a word, values are in the form of
// (filename=frequency)
for (Text value: values) {
String[] valueParts = value.split("=");
outputKey.set(key.toString()+"@"+valueParts[0]);
outputValue.set(new String(Integer.parseInt(valueParts[1]) * IDF);
context.write(outputKey, outputValue);
}
}
}
这也将节省一些内存,因为您不需要额外的地图(如果可以的话)。
编辑:上面的代码假定您已经知道文件名的每个单词的总频率,即相同的文件名不会多次出现在值中,但您可能想要检查它是否成立。否则,第二个解决方案不起作用,因为您必须在第一个循环中计算每个文件的总频率和。