计算中位数减少

时间:2012-04-11 15:53:08

标签: hadoop statistics mapreduce apache-pig median

有人可以举例说明地图中的中位数/分位数的计算吗?

我对Datafu的中位数的理解是'n'映射器对它进行排序 数据并将数据发送到负责排序的“1”减速器 来自n个映射器的所有数据并找到中位数(中间值) 我的理解是否正确?,

如果是这样,这种方法是否适用 大量的数据,因为我可以清楚地看到一个单一的减速机 努力完成最后的任务。 谢谢

4 个答案:

答案 0 :(得分:13)

试图找到一个系列中的中位数(中间数)将要求1个reducer传递整个数字范围,以确定哪个是'中间'值。

根据输入集中值的范围和唯一性,您可以引入组合器来输出每个值的频率 - 减少发送到单个reducer的地图输出的数量。然后,您的减速器可以使用排序值/频率对来识别中位数。

另一种可以缩放的方法(再次,如果您知道值的范围和粗略分布)是使用自定义分区器按范围桶分配键(0-99转到减速器0,1-100-199到减速器2) , 等等)。然而,这将需要一些辅助工作来检查减速器输出并执行最终中位数计算(例如,知道每个减速器中的键数,您可以计算哪个减速器输出将包含中位数,以及在哪个偏移处)

答案 1 :(得分:7)

你真的需要完全中位数和分位数吗?

很多时候,你最好只获得近似值,并使用它们,特别是如果你使用它,例如数据分区。

事实上,你可以使用近似分位数来加速找到精确的分位数(实际上在O(n/p)时间内),这里是策略的大致轮廓:

  1. 为每个分区设置一个映射器,计算所需的分位数,并将它们输出到新的数据集。这个数据集应该是几个较小的量级(除非你要求太多的分位数!)
  2. 在此数据集中,再次计算分位数 ,类似于“中位数中位数”。这些是您最初的估计。
  3. 根据这些分位数(甚至以这种方式获得的其他分区)重新分配数据。目标是最终,真正的分位数保证在一个分区中,并且每个分区中最多应该有一个所需的分位数
  4. 在每个分区中,执行QuickSelect(在O(n)中)以查找真正的分位数。
  5. 每个步骤都是线性时间。最昂贵的步骤是第3部分,因为它需要重新分配整个数据集,因此它会生成O(n)网络流量。 您可以通过为第一次迭代选择“备用”分位数来优化过程。说,你想找到全球中位数。您无法轻松地在线性过程中找到它,但是当它被分成k个分区时,您可能将其缩小到数据集的1 / kth。因此,不是让每个节点报告其中值,而是让每个节点另外报告(k-1)/(2k)和(k + 1)/(2k)处的对象。这应该允许您缩小真实中位数必须显着位置的值的范围。因此,在下一步中,您可以将每个节点将所需范围内的对象发送到单个主节点,并仅选择此范围内的中位数。

答案 2 :(得分:2)

O((n log n)/ p)对其进行排序,然后按O(1)得到中位数。

是的......你可以获得O(n / p),但是你不能在Hadoop中使用开箱即用的排序功能。我只是排序并得到中心项目,除非你可以证明2-20小时的开发时间来编码并行的第k个最大算法。

答案 3 :(得分:0)

在许多实际场景中,数据集中值的基数相对较小。在这种情况下,使用两个MapReduce作业可以有效地解决问题:

  1. 计算数据集中的值的频率(基本上是字数统计作业)
  2. 身份映射器+减少器,其基于<价值 - 频率>对
  3. 作业1.将大幅减少数据量,并且可以完全并行执行。作业2的缩减器只需处理nn = cardinality of your value set)个项而不是所有值,就像天真的方法一样。

    下面是作业2的示例缩减器。它是可以在Hadoop流中直接使用的python脚本。假设数据集中的值为ints,但double s

    可以轻松采用
    import sys
    
    item_to_index_range = []
    total_count = 0
    
    # Store in memory a mapping of a value to the range of indexes it has in a sorted list of all values
    for line in sys.stdin:
        item, count = line.strip().split("\t", 1)
        new_total_count = total_count + int(count)
        item_to_index_range.append((item, (total_count + 1,   new_total_count + 1)))
        total_count = new_total_count
    
    # Calculate index(es) of middle items
    middle_items_indexes = [(total_count / 2) + 1]
    if total_count % 2 == 0:
        middle_items_indexes += [total_count / 2]
    
    # Retrieve middle item(s) 
    middle_items = []
    for i in middle_items_indexes:
        for item, index_range in item_to_index_range:
            if i in range(*index_range):
                middle_items.append(item)
                continue
    
    print sum(middle_items) / float(len(middle_items))
    

    此答案建立在最初来自answer Chris White的建议之上。答案建议使用组合器作为计算值的频率的平均值。但是,在MapReduce中,不能保证始终执行组合器。这有一些副作用:

    • reducer首先必须计算最终<价值 - 频率>然后计算中位数。
    • 在最糟糕的情况下,合并器永远不会执行,减速器仍然需要处理所有单个值