我试图产生mapper生成的每个键值对的概率。
所以,让我们说mapper产生:
a, (r, 5)
a, (e, 6)
a, (w, 7)
我需要添加5 + 6 + 7 = 18,然后找到概率5 / 18,6 / 18,7 / 18
所以减速器的最终输出如下:
a, [[r, 5, 0.278], [e, 6, 0.33], [w, 7, 0.389]]
到目前为止,我只能让reducer从值中求和所有整数。 如何让它返回并将每个实例除以总和?
谢谢!
答案 0 :(得分:5)
Pai的解决方案在技术上是正确的,但在实践中这会给你带来很多冲突,因为设置分区可能是一个很大的痛苦(见https://groups.google.com/forum/#!topic/mrjob/aV7bNn0sJ2k)。
您可以使用mrjob.step更轻松地完成此任务,然后创建两个reducer,例如在此示例中:https://github.com/Yelp/mrjob/blob/master/mrjob/examples/mr_next_word_stats.py
要按照你所描述的方式去做:
from mrjob.job import MRJob
import re
from mrjob.step import MRStep
from collections import defaultdict
wordRe = re.compile(r"[\w]+")
class MRComplaintFrequencyCount(MRJob):
def mapper(self, _, line):
self.increment_counter('group','num_mapper_calls',1)
#Issue is third column in csv
issue = line.split(",")[3]
for word in wordRe.findall(issue):
#Send all map outputs to same reducer
yield word.lower(), 1
def reducer(self, key, values):
self.increment_counter('group','num_reducer_calls',1)
wordCounts = defaultdict(int)
total = 0
for value in values:
word, count = value
total+=count
wordCounts[word]+=count
for k,v in wordCounts.iteritems():
# word, frequency, relative frequency
yield k, (v, float(v)/total)
def combiner(self, key, values):
self.increment_counter('group','num_combiner_calls',1)
yield None, (key, sum(values))
if __name__ == '__main__':
MRComplaintFrequencyCount.run()
这是一个标准的字数统计和聚合主要在组合器中,然后使用“无”作为公共密钥,因此每个字间接地在相同的密钥下发送到reducer。在减速器中,您可以获得总字数并计算相对频率。
答案 1 :(得分:4)
您上面所做的工作也应该有效,但这是假设单个密钥的所有数据都适合内存。如果是,则在Reducer中,您可以保留内存中的所有值,然后计算总计,然后计算每个键值对的边际值。这通常被称为“条纹”方法。
但是,大多数情况下,现在可能都是这样,数据可能不适合内存。在这种情况下,您必须找到一种方法来发送值以在实际键值对之前计算总数,以便在它们可以用于计算边际值并立即发出值时。
这是“反转顺序”设计模式的候选者。当您需要计算相对频率时,它很有用。基本思路是在Mapper的最后为每个中间数据发出2个键值对,其中一个键值对将具有所有值的相同公共键。这将用于计算总数。
示例:
For a, (r, 5) :
---------------
emit (a, r), 5
emit (a, *), 5
For a, (e, 6) :
---------------
emit (a, e), 6
emit (a, *), 6
For a, (w, 7) :
---------------
emit (a, w), 7
emit (a, *), 7
完成此操作后,您需要一个分区程序,它将仅使用键中的第一个值对每个中间键值对进行分区。在上面的示例中使用“a”。
您还需要一个密钥排序顺序,始终将密钥的第二部分中的*放在首位。
这样,所有中间键在键的第一部分都有“a”,最终会在同一个reducer中。此外,它们将以如下所示的方式排序 -
emit (a, *), 5
emit (a, *), 6
emit (a, *), 7
emit (a, e), 6
emit (a, r), 5
emit (a, w), 7
在迭代器中,当您遍历键值对时,如果键的第二部分中有*,则必须简单地累积键中的值。然后,您可以使用累计值来计算所有其他键值对的边际值。
total = 0
for(value : values){
if (key.second == *)
total += value
else
emit (key.first , key.second, value, value/total)
}
此设计模式通常称为使用配对方法的反转顺序。 有关此设计模式和其他设计模式的更多信息,我建议您阅读本书中有关MapReduce设计模式的章节 - http://lintool.github.com/MapReduceAlgorithms/。 它用例子很好地解释了。
答案 2 :(得分:1)
您可以像对象一样简单地计算总和,并将对保留在内存中,以发出您想要的概率,如下所示:
reduce (key, list<values>):
int sum = 0;
for (value in values) {
sum = sum + value.frequency; //assuming you can extract two fields in each value: value.word and value.frequency
}
String outputValue = "[";
for (value in values) { //iterate over the values once more
outputValue = outputValue + "["+ value.word + ", " +value.frequency + ", "+ value.frequency/sum +"],"
}
outputValue = outputValue.replaceLast(",","]");
emit (key, outputValue);
当然,这只是一个伪代码,因为我不习惯python,但我希望过渡应该很容易。