谁能给我解释一下map和flatMap之间的区别,以及它们各自的良好用例是什么?

“flatten the results”是什么意思? 它有什么好处?


当前回答

通常我们在hadoop中使用字数计算示例。我将使用相同的用例,将使用map和flatMap,我们将看到它如何处理数据的区别。

下面是示例数据文件。

hadoop is fast
hive is sql on hdfs
spark is superfast
spark is awesome

上面的文件将使用map和flatMap进行解析。

使用地图

>>> wc = data.map(lambda line:line.split(" "));
>>> wc.collect()
[u'hadoop is fast', u'hive is sql on hdfs', u'spark is superfast', u'spark is awesome']

输入有4行,输出大小也是4,即N个元素==> N个元素。

使用flatMap

>>> fm = data.flatMap(lambda line:line.split(" "));
>>> fm.collect()
[u'hadoop', u'is', u'fast', u'hive', u'is', u'sql', u'on', u'hdfs', u'spark', u'is', u'superfast', u'spark', u'is', u'awesome']

输出与map不同。


让我们为每个键赋值1以获得单词计数。

fm:使用flatMap创建的RDD wc:使用map创建RDD

>>> fm.map(lambda word : (word,1)).collect()
[(u'hadoop', 1), (u'is', 1), (u'fast', 1), (u'hive', 1), (u'is', 1), (u'sql', 1), (u'on', 1), (u'hdfs', 1), (u'spark', 1), (u'is', 1), (u'superfast', 1), (u'spark', 1), (u'is', 1), (u'awesome', 1)]

然而,RDD wc上的flatMap将给出以下不希望看到的输出:

>>> wc.flatMap(lambda word : (word,1)).collect()
[[u'hadoop', u'is', u'fast'], 1, [u'hive', u'is', u'sql', u'on', u'hdfs'], 1, [u'spark', u'is', u'superfast'], 1, [u'spark', u'is', u'awesome'], 1]

如果使用map而不是flatMap,则无法获得单词计数。

根据定义,map和flatMap的区别是:

map:它通过对每个元素应用给定的函数来返回一个新的RDD RDD。函数在map中只返回一个项。 flatMap:与map类似,它通过应用函数返回一个新的RDD 到RDD的每个元素,但输出是平坦的。

其他回答

地图:

是一种高阶方法,它接受一个函数作为输入,并将其应用于源RDD中的每个元素。

http://commandstech.com/difference-between-map-and-flatmap-in-spark-what-is-map-and-flatmap-with-examples/

flatMap:

接受输入函数的高阶方法和转换操作。

所有的例子都很好....这是一个很好的视觉插图…资料来源:spark的DataFlair培训

Map: Map是Apache Spark中的转换操作。它应用于RDD的每个元素,并将结果作为新的RDD返回。在Map中,操作开发人员可以定义自己的自定义业务逻辑。同样的逻辑将应用于RDD的所有元素。

Spark RDD map函数以一个元素作为输入,根据自定义代码(由开发人员指定)处理它,每次返回一个元素。Map将一个长度为N的RDD转换为另一个长度为N的RDD。输入和输出RDD通常具有相同数量的记录。

使用scala的map示例:

val x = spark.sparkContext.parallelize(List("spark", "map", "example",  "sample", "example"), 3)
val y = x.map(x => (x, 1))
y.collect
// res0: Array[(String, Int)] = 
//    Array((spark,1), (map,1), (example,1), (sample,1), (example,1))

// rdd y can be re writen with shorter syntax in scala as 
val y = x.map((_, 1))
y.collect
// res1: Array[(String, Int)] = 
//    Array((spark,1), (map,1), (example,1), (sample,1), (example,1))

// Another example of making tuple with string and it's length
val y = x.map(x => (x, x.length))
y.collect
// res3: Array[(String, Int)] = 
//    Array((spark,5), (map,3), (example,7), (sample,6), (example,7))

FlatMap:

flatMap是一个转换操作。它应用于RDD的每个元素,并将结果作为新的RDD返回。它类似于Map,但是FlatMap允许从Map函数返回0,1或更多元素。在FlatMap操作中,开发人员可以定义自己的自定义业务逻辑。同样的逻辑将应用于RDD的所有元素。

“flatten the results”是什么意思?

FlatMap函数接受一个元素作为输入,根据自定义代码(由开发人员指定)处理它,并一次返回0个或多个元素。flatMap()将一个长度为N的RDD转换为另一个长度为M的RDD。

使用scala的flatMap示例:

val x = spark.sparkContext.parallelize(List("spark flatmap example",  "sample example"), 2)

// map operation will return Array of Arrays in following case : check type of res0
val y = x.map(x => x.split(" ")) // split(" ") returns an array of words
y.collect
// res0: Array[Array[String]] = 
//  Array(Array(spark, flatmap, example), Array(sample, example))

// flatMap operation will return Array of words in following case : Check type of res1
val y = x.flatMap(x => x.split(" "))
y.collect
//res1: Array[String] = 
//  Array(spark, flatmap, example, sample, example)

// RDD y can be re written with shorter syntax in scala as 
val y = x.flatMap(_.split(" "))
y.collect
//res2: Array[String] = 
//  Array(spark, flatmap, example, sample, example)

下面是一个不同的例子,作为一个spark-shell会话:

首先是一些数据——两行文本:

val rdd = sc.parallelize(Seq("Roses are red", "Violets are blue"))  // lines

rdd.collect

    res0: Array[String] = Array("Roses are red", "Violets are blue")

现在,map将一个长度为N的RDD转换为另一个长度为N的RDD。

例如,它将两行映射为两行长度:

rdd.map(_.length).collect

    res1: Array[Int] = Array(13, 16)

但是flatMap(松散地说)将长度为N的RDD转换为N个集合的集合,然后将这些集合平展为单个结果RDD。

rdd.flatMap(_.split(" ")).collect

    res2: Array[String] = Array("Roses", "are", "red", "Violets", "are", "blue")

我们每行有多个单词,而且每行有多行,但我们最终得到一个单词输出数组

为了说明这一点,从一个行集合到一个单词集合的flatMapping如下:

["aa bb cc", "", "dd"] => [["aa","bb","cc"],[],["dd"]] => ["aa","bb","cc","dd"]

因此,对于flatMap,输入和输出rdd通常具有不同的大小。

如果我们试图使用map与我们的split函数,我们将以嵌套结构结束(RDD的单词数组,类型为RDD[Array[String]]),因为我们必须对每个输入只有一个结果:

rdd.map(_.split(" ")).collect

    res3: Array[Array[String]] = Array(
                                     Array(Roses, are, red), 
                                     Array(Violets, are, blue)
                                 )

最后,一个有用的特殊情况是映射到一个可能不返回答案的函数,因此返回一个Option。我们可以使用flatMap过滤出返回None的元素,并从返回Some的元素中提取值:

val rdd = sc.parallelize(Seq(1,2,3,4))

def myfn(x: Int): Option[Int] = if (x <= 2) Some(x * 10) else None

rdd.flatMap(myfn).collect

    res3: Array[Int] = Array(10,20)

(注意这里Option的行为很像一个只有一个元素或者没有元素的列表)

通常我们在hadoop中使用字数计算示例。我将使用相同的用例,将使用map和flatMap,我们将看到它如何处理数据的区别。

下面是示例数据文件。

hadoop is fast
hive is sql on hdfs
spark is superfast
spark is awesome

上面的文件将使用map和flatMap进行解析。

使用地图

>>> wc = data.map(lambda line:line.split(" "));
>>> wc.collect()
[u'hadoop is fast', u'hive is sql on hdfs', u'spark is superfast', u'spark is awesome']

输入有4行,输出大小也是4,即N个元素==> N个元素。

使用flatMap

>>> fm = data.flatMap(lambda line:line.split(" "));
>>> fm.collect()
[u'hadoop', u'is', u'fast', u'hive', u'is', u'sql', u'on', u'hdfs', u'spark', u'is', u'superfast', u'spark', u'is', u'awesome']

输出与map不同。


让我们为每个键赋值1以获得单词计数。

fm:使用flatMap创建的RDD wc:使用map创建RDD

>>> fm.map(lambda word : (word,1)).collect()
[(u'hadoop', 1), (u'is', 1), (u'fast', 1), (u'hive', 1), (u'is', 1), (u'sql', 1), (u'on', 1), (u'hdfs', 1), (u'spark', 1), (u'is', 1), (u'superfast', 1), (u'spark', 1), (u'is', 1), (u'awesome', 1)]

然而,RDD wc上的flatMap将给出以下不希望看到的输出:

>>> wc.flatMap(lambda word : (word,1)).collect()
[[u'hadoop', u'is', u'fast'], 1, [u'hive', u'is', u'sql', u'on', u'hdfs'], 1, [u'spark', u'is', u'superfast'], 1, [u'spark', u'is', u'awesome'], 1]

如果使用map而不是flatMap,则无法获得单词计数。

根据定义,map和flatMap的区别是:

map:它通过对每个元素应用给定的函数来返回一个新的RDD RDD。函数在map中只返回一个项。 flatMap:与map类似,它通过应用函数返回一个新的RDD 到RDD的每个元素,但输出是平坦的。

map和flatMap输出的差异:

1. flatmap

val a = sc.parallelize(1 to 10, 5)

a.flatMap(1 to _).collect()

输出:

 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

2.地图:

val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)

val b = a.map(_.length).collect()

输出:

3 6 6 3 8