Python MRjob:减速器可以执行2次操作吗?

Python MRjob:减速器可以执行2次操作吗?,python,mapreduce,mrjob,Python,Mapreduce,Mrjob,我试图给出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对值中的所有整数求和。 我怎样才能让它返回,并除以每个实例的总和 谢谢 您上面所做的应该也可以,但这是假设一个键的所有

我试图给出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对值中的所有整数求和。 我怎样才能让它返回,并除以每个实例的总和


谢谢

您上面所做的应该也可以,但这是假设一个键的所有数据都可以放入内存中。如果是这样,那么在Reducer中,您可以在内存中保存所有值,然后计算总数,然后计算每个键值对的边际值。这通常被称为“条纹”方法

但是,现在大多数情况下,这可能是真的,并且数据可能无法放入内存中。在这种情况下,您必须找到一种方法,在实际键值对之前发送值以计算总数,这样,当它们可以用于计算边际值并立即发出值时

这是“反转顺序”设计模式的候选。当您需要计算相对频率时,它很有用。基本思想是在映射器的一端,为每个中间数据发出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”,键将在同一个减速器中结束。此外,它们将按如下所示的方式进行排序-

emit (a, *), 5
emit (a, *), 6
emit (a, *), 7
emit (a, e), 6
emit (a, r), 5
emit (a, w), 7
在reducer中,当您遍历键-值对时,如果键的第二部分中有一个*的话,您将不得不简单地累积来自键的值。然后可以使用累积值计算所有其他键值对的边际值

total = 0
for(value : values){
    if (key.second == *)
        total += value
    else
        emit (key.first , key.second, value, value/total)
}
这种设计模式通常被称为使用成对方法的反转顺序。 有关此设计模式和其他设计模式的更多信息,我建议阅读本书中关于MapReduce设计模式的章节-。
通过示例对其进行了很好的解释。

Pai的解决方案在技术上是正确的,但在实践中,这会给您带来很多冲突,因为设置分区可能是一件非常痛苦的事情(请参阅)

通过使用mrjob.step,然后创建两个异径管,可以更轻松地完成此任务,如本例所示:

按照您描述的方式进行:

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()

这会进行标准的字数计数,并主要在组合器中聚合,然后使用“None”作为公共键,因此每个字都会在同一个键下间接发送到reducer。在reducer中,您可以获得总字数并计算相对频率。

您可以简单地计算和,就像您所做的那样,还可以将对保留在内存中,以发出您想要的概率,如下所示:

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);
reduce(键,列表):
整数和=0;
for(值中的值){
sum=sum+value.frequency;//假设可以在每个值中提取两个字段:value.word和value.frequency
}
字符串outputValue=“[”;
对于(值中的值){//再次迭代这些值
outputValue=outputValue+“[”+value.word+“,“+value.frequency+”,“+value.frequency/sum+”,”
}
outputValue=outputValue.replaceLast(“,”,“]);
发射(键、输出值);

当然,这只是一个伪代码,因为我不习惯python,但我希望转换应该非常简单。

感谢您提供的有用示例。我不明白的是,考虑到不能保证调用组合器(例如,可以调用零次,而只调用减速机),这是如何工作的?