什么是Map / Reduce?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是Map / Reduce?相关的知识,希望对你有一定的参考价值。

我听说过很多关于map / reduce的内容,特别是在谷歌大规模并行计算系统的背景下。究竟是什么?

答案

从谷歌的MapReduce研究出版物页面的摘要:

MapReduce是一种编程模型,是处理和生成大型数据集的相关实现。用户指定处理键/值对以生成一组中间键/值对的映射函数,以及合并与相同中间键关联的所有中间值的reduce函数。

MapReduce的优点是可以在多个处理节点(多个服务器)上并行执行处理,因此它是一个可以很好地扩展的系统。

因为它基于functional programming模型,所以mapreduce步骤都没有任何副作用(map过程的每个子部分的状态和结果不依赖于另一个),因此映射和减少的数据集可以各自在多个处理节点上分开。

Joel的Can Your Programming Language Do This?文章讨论了如何理解函数式编程对于Google提出的MapReduce是至关重要的,MapReduce为其搜索引擎提供支持。如果您不熟悉函数式编程以及它如何允许可伸缩代码,那么这是一个非常好的阅读。

另见:Wikipedia: MapReduce

相关问题:Please explain mapreduce simply

另一答案

MapReduce Explained

它解释得比我能做得更好。有帮助吗?

另一答案

Map是一个函数,它将另一个函数应用于列表中的所有项,以生成另一个列表,其中包含所有返回值。 (另一种说法是“将f应用到x”是“调用f,传递给它x”。所以有时说“应用”而不是“调用”听起来更好。)

这就是map可能用C#编写的(它叫做Select并且在标准库中):

public static IEnumerable<R> Select<T, R>(this IEnumerable<T> list, Func<T, R> func)
{
    foreach (T item in list)
        yield return func(item);
}

因为你是一个Java家伙,Joel Spolsky喜欢告诉GROSSLY UNFAIR LIES关于Java是多么糟糕(实际上,他不是在说谎,这很糟糕,但我试图赢得你),这是我非常粗略的尝试一个Java版本(我没有Java编译器,我依旧记得Java版本1.1!):

// represents a function that takes one arg and returns a result
public interface IFunctor
{
    object invoke(object arg);
}

public static object[] map(object[] list, IFunctor func)
{
    object[] returnValues = new object[list.length];

    for (int n = 0; n < list.length; n++)
        returnValues[n] = func.invoke(list[n]);

    return returnValues;
}

我相信这可以通过一百万种方式得到改善。但这是基本的想法。

Reduce是一个将列表中的所有项目转换为单个值的函数。要做到这一点,需要给它另一个函数func,它将两个项转换为单个值。它可以通过给func提供前两个项目。然后是第三项的结果。然后是第四个项目的结果,依此类推,直到所有项目都消失了,我们只剩下一个值。

在C#中,reduce被称为Aggregate,并且再次出现在标准库中。我将直接跳到Java版本:

// represents a function that takes two args and returns a result
public interface IBinaryFunctor
{
    object invoke(object arg1, object arg2);
}

public static object reduce(object[] list, IBinaryFunctor func)
{
    if (list.length == 0)
        return null; // or throw something?

    if (list.length == 1)
        return list[0]; // just return the only item

    object returnValue = func.invoke(list[0], list[1]);

    for (int n = 1; n < list.length; n++)
        returnValue = func.invoke(returnValue, list[n]);

    return returnValue;
}

这些Java版本需要添加泛型,但我不知道如何在Java中执行此操作。但是你应该能够通过匿名内部类来提供函子:

string[] names = getLotsOfNames();

string commaSeparatedNames = (string)reduce(names, 
   new IBinaryFunctor {
       public object invoke(object arg1, object arg2)
           { return ((string)arg1) + ", " + ((string)arg2); }
   }

希望仿制药可以摆脱演员阵容。 C#中的类型安全等价物是:

string commaSeparatedNames = names.Aggregate((a, b) => a + ", " + b);

为什么这个“酷”?将更大的计算分解成更小的部分的简单方法,因此它们可以以不同的方式重新组合在一起,总是很酷。 Google应用这种想法的方式是并行化,因为map和reduce都可以通过多台计算机共享。

但关键要求并不是您的语言可以将函数视为值。任何OO语言都可以做到这一点。并行化的实际要求是,传递给map和reduce的小func函数不得使用或更新任何状态。它们必须返回一个仅依赖于传递给它们的参数的值。否则,当您尝试并行运行整个过程时,结果将完全搞砸。

另一答案

在对非常长的waffley或非常短暂的模糊博客文章感到非常沮丧之后,我最终发现了这个very good rigorous concise paper

然后我继续前进,通过翻译成Scala使其更简洁,我提供了最简单的情况,用户只需指定应用程序的mapreduce部分。在Hadoop / Spark中,严格来说,采用了更复杂的编程模型,要求用户明确指定此处列出的另外4个函数:http://en.wikipedia.org/wiki/MapReduce#Dataflow

import scalaz.syntax.id._

trait MapReduceModel {
  type MultiSet[T] = Iterable[T]

  // `map` must be a pure function
  def mapPhase[K1, K2, V1, V2](map: ((K1, V1)) => MultiSet[(K2, V2)])
                              (data: MultiSet[(K1, V1)]): MultiSet[(K2, V2)] = 
    data.flatMap(map)

  def shufflePhase[K2, V2](mappedData: MultiSet[(K2, V2)]): Map[K2, MultiSet[V2]] =
    mappedData.groupBy(_._1).mapValues(_.map(_._2))

  // `reduce` must be a monoid
  def reducePhase[K2, V2, V3](reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)])
                             (shuffledData: Map[K2, MultiSet[V2]]): MultiSet[V3] =
    shuffledData.flatMap(reduce).map(_._2)

  def mapReduce[K1, K2, V1, V2, V3](data: MultiSet[(K1, V1)])
                                   (map: ((K1, V1)) => MultiSet[(K2, V2)])
                                   (reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)]): MultiSet[V3] =
    mapPhase(map)(data) |> shufflePhase |> reducePhase(reduce)
}

// Kinda how MapReduce works in Hadoop and Spark except `.par` would ensure 1 element gets a process/thread on a cluster
// Furthermore, the splitting here won't enforce any kind of balance and is quite unnecessary anyway as one would expect
// it to already be splitted on HDFS - i.e. the filename would constitute K1
// The shuffle phase will also be parallelized, and use the same partition as the map phase.  
abstract class ParMapReduce(mapParNum: Int, reduceParNum: Int) extends MapReduceModel {
  def split[T](splitNum: Int)(data: MultiSet[T]): Set[MultiSet[T]]

  override def mapPhase[K1, K2, V1, V2](map: ((K1, V1)) => MultiSet[(K2, V2)])
                                       (data: MultiSet[(K1, V1)]): MultiSet[(K2, V2)] = {
    val groupedByKey = data.groupBy(_._1).map(_._2)
    groupedByKey.flatMap(split(mapParNum / groupedByKey.size + 1))
    .par.flatMap(_.map(map)).flatten.toList
  }

  override def reducePhase[K2, V2, V3](reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)])
                             (shuffledData: Map[K2, MultiSet[V2]]): MultiSet[V3] =
    shuffledData.map(g => split(reduceParNum / shuffledData.size + 1)(g._2).map((g._1, _)))
    .par.flatMap(_.map(reduce))
    .flatten.map(_._2).toList
}
另一答案

MapReduce和/或SQL:

http://www.data-miners.com/blog/2008/01/mapreduce-and-sql-aggregations.html

http://www.data-miners.com/blog/

批评MapReduce

http://www.databasecolumn.com/2008/01/mapreduce-a-major-step-back.html

http://www.databasecolumn.com/2008/01/mapreduce-continued.html

另一答案

Map是可以应用于数组的本机JS方法。它会创建一个新数组,因为某些函数映射到原始数组中的每个元素。因此,如果您映射了一个函数(element){return element * 2;},它将返回一个新数组,其中每个元素都加倍。原始数组将不加修改。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Reduce是一种原生JS方法,也可以应用于数组。它将函数应用于数组,并具有称为累加器的初始输出值。它循环遍历数组中的每个元素,应用一个函数,并将它们减少为单个值(从累加器开始)。它很有用,因为你可以拥有你想要的任何输出,你只需要从那种类型的累加器开始。因此,如果我想将某些内容减少为一个对象,我将从一个累加器{}开始。以上是关于什么是Map / Reduce?的主要内容,如果未能解决你的问题,请参考以下文章

几个关于js数组方法reduce的经典片段

JavaScript 代码片段

几个关于js数组方法reduce的经典片段

1lambda是什么?及filter,map,reduce

什么是 Map/Reduce?

什么是Map-Reduce