谁能给我解释一下map和flatMap之间的区别,以及它们各自的良好用例是什么?
“flatten the results”是什么意思? 它有什么好处?
谁能给我解释一下map和flatMap之间的区别,以及它们各自的良好用例是什么?
“flatten the results”是什么意思? 它有什么好处?
当前回答
下面是一个不同的例子,作为一个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是相似的,从某种意义上说,它们从输入RDD中获取一行并在其上应用一个函数。它们的不同之处在于map中的函数只返回一个元素,而flatMap中的函数可以返回一个元素列表(0或更多)作为迭代器。
同样,flatMap的输出是扁平的。尽管flatMap中的函数返回一个元素列表,但flatMap返回一个RDD,其中以平面方式(而不是列表)包含列表中的所有元素。
使用测试。以Md为例:
➜ spark-1.6.1 cat test.md
This is the first line;
This is the second line;
This is the last line.
scala> val textFile = sc.textFile("test.md")
scala> textFile.map(line => line.split(" ")).count()
res2: Long = 3
scala> textFile.flatMap(line => line.split(" ")).count()
res3: Long = 15
scala> textFile.map(line => line.split(" ")).collect()
res0: Array[Array[String]] = Array(Array(This, is, the, first, line;), Array(This, is, the, second, line;), Array(This, is, the, last, line.))
scala> textFile.flatMap(line => line.split(" ")).collect()
res1: Array[String] = Array(This, is, the, first, line;, This, is, the, second, line;, This, is, the, last, line.)
如果您使用映射方法,您将得到测试线。md,对于flatMap方法,您将得到字数。
map方法类似于flatMap,它们都返回一个新的RDD。map方法经常使用返回一个新的RDD, flatMap方法经常使用分割词。
这可以归结为你最初的问题:你所说的扁平化是什么意思?
当您使用flatMap时,“多维”集合就变成了“一维”集合。
val array1d = Array ("1,2,3", "4,5,6", "7,8,9")
//array1d is an array of strings
val array2d = array1d.map(x => x.split(","))
//array2d will be : Array( Array(1,2,3), Array(4,5,6), Array(7,8,9) )
val flatArray = array1d.flatMap(x => x.split(","))
//flatArray will be : Array (1,2,3,4,5,6,7,8,9)
当你想使用flatMap时,
你的地图功能的结果是创建多层结构 但所有你想要的是一个简单的-平面-一维结构,通过删除所有的内部分组
map返回相同数量元素的RDD,而flatMap可能不会。
flatMap过滤丢失或不正确数据的示例用例。
map在各种各样的情况下使用,其中输入和输出的元素数量是相同的。
number.csv
1
2
3
-
4
-
5
Map.py添加add.csv中的所有数字。
from operator import *
def f(row):
try:
return float(row)
except Exception:
return 0
rdd = sc.textFile('a.csv').map(f)
print(rdd.count()) # 7
print(rdd.reduce(add)) # 15.0
py使用flatMap在添加之前过滤掉缺失的数据。与以前的版本相比,增加的数字更少。
from operator import *
def f(row):
try:
return [float(row)]
except Exception:
return []
rdd = sc.textFile('a.csv').flatMap(f)
print(rdd.count()) # 5
print(rdd.reduce(add)) # 15.0