อะไรคือความแตกต่างระหว่าง map และ flatMap และกรณีการใช้งานที่ดีสำหรับแต่ละอัน?


249

ใครสามารถอธิบายความแตกต่างระหว่างแผนที่กับ flatMap ให้ฉันได้และเป็นกรณีการใช้งานที่ดีสำหรับแต่ละข้อ

"การเรียบผลลัพธ์" หมายความว่าอย่างไร มันดีสำหรับอะไร?


4
เนื่องจากคุณเพิ่มแท็ก Spark ที่ฉันจะคิดว่าคุณกำลังถามเกี่ยวกับRDD.mapและRDD.flatMapในApache Spark โดยทั่วไปแล้วการดำเนินงาน RDD ของ Spark ถูกสร้างแบบจำลองหลังจากการดำเนินการรวบรวม Scala ที่สอดคล้องกัน คำตอบในstackoverflow.com/q/1059776/590203ซึ่งพูดถึงความแตกต่างระหว่างmapและflatMapในสกาล่าอาจเป็นประโยชน์กับคุณ
Josh Rosen

1
ตัวอย่างส่วนใหญ่ในที่นี้ดูเหมือนว่าจะถือว่า flatMap ทำงานกับการรวบรวมเท่านั้นซึ่งไม่ใช่ในกรณีนี้
Boon

คำตอบ:


195

นี่คือตัวอย่างของความแตกต่างเป็น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เปลี่ยน RDD ของความยาว N เป็น RDD อีกความยาว N

ตัวอย่างเช่นมันแมปจากสองบรรทัดเป็นสองความยาวบรรทัด:

rdd.map(_.length).collect

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

แต่flatMap(พูดอย่างหลวม ๆ ) แปลง RDD ที่มีความยาว N เป็นคอลเล็กชันของ 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"]

อินพุตและเอาต์พุต RDDs flatMapจะดังนั้นโดยทั่วไปจะมีขนาดแตกต่างกันสำหรับ

หากเราได้ลองใช้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)

(สังเกตที่นี่ว่าตัวเลือกทำงานค่อนข้างชอบรายการที่มีองค์ประกอบเดียวหรือเป็นศูนย์)


1
การโทรแยกภายในแผนที่จะให้["a b c", "", "d"] => [["a","b","c"],[],["d"]]หรือไม่
2635088

1
ใช่ - (แต่โปรดทราบว่าสัญกรณ์อย่างไม่เป็นทางการของฉันมีไว้เพื่อระบุชุดของบางอย่าง - ในความเป็นจริงการทำแผนที่splitในรายชื่อของ Strings จะสร้างรายชื่อของอาร์เรย์)
DNA

2
ขอบคุณที่เขียนขึ้นนี้เป็นคำอธิบายที่ดีที่สุดที่ฉันได้อ่านเพื่อแยกความแตกต่างระหว่างเดียวกัน
Rajiv

98

โดยทั่วไปเราใช้ตัวอย่างการนับจำนวนคำใน hadoop ฉันจะใช้กรณีการใช้งานเดียวกันและจะใช้mapและflatMapและเราจะเห็นความแตกต่างว่ามันประมวลผลข้อมูลอย่างไร

ด้านล่างเป็นไฟล์ข้อมูลตัวอย่าง

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

แฟ้มดังกล่าวข้างต้นจะถูกแยกโดยใช้และmapflatMap

การใช้ map

>>> 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']

เอาท์พุทจะแตกต่างจากแผนที่


ลองกำหนด 1 เป็นค่าสำหรับแต่ละคีย์เพื่อรับจำนวนคำ

  • fm: RDD สร้างโดยใช้ flatMap
  • wc: RDD สร้างโดยใช้ map
>>> 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)]

โดยflatMapที่ RDD wcจะให้ผลลัพธ์ที่ไม่ต้องการด้านล่าง:

>>> 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]

คุณไม่สามารถได้รับการนับจำนวนคำถ้าใช้แทนmapflatMap

ตามคำนิยามความแตกต่างระหว่างmapและflatMapคือ:

map: มันคืน RDD ใหม่โดยใช้ฟังก์ชั่นที่กำหนดให้กับองค์ประกอบของ RDD ฟังก์ชั่นการmapคืนค่าเพียงหนึ่งรายการ

flatMap: คล้ายกับmapมันจะส่งคืน RDD ใหม่โดยใช้ฟังก์ชั่นกับแต่ละองค์ประกอบของ RDD แต่เอาต์พุตจะถูกทำให้แบน


14
ฉันรู้สึกว่าคำตอบนี้ดีกว่าคำตอบที่ยอมรับ
กฤษณะ

15
ทำไมบนโลกนี้คุณจะสร้างภาพหน้าจอที่อ่านไม่ออกเมื่อคุณสามารถคัดลอกวางข้อความออกได้?
nbubis

ดังนั้น flatMap () คือ map () + "flatten" และฉันรู้ว่ามันไม่เข้าท่า แต่มีฟังก์ชั่น "flatten" ชนิดใดบ้างที่เราสามารถใช้หลังจาก map ()?
burakongun

2
รหัสของคุณมีการพิมพ์ผิดที่ทำให้เข้าใจผิด ผลลัพธ์ของ.map(lambda line:line.split(" "))ไม่ใช่อาร์เรย์ของสตริง คุณควรเปลี่ยนdata.collect() เป็นwc.collectและคุณจะเห็นอาร์เรย์ของอาร์เรย์
swdev

1
ใช่ แต่ผลลัพธ์ของคำสั่งยังไม่ถูกต้อง คุณวิ่งwc.collect()ไหม
swdev

18

หากคุณถามถึงความแตกต่างระหว่าง RDD.map และ RDD.flatMap ใน Spark แผนที่จะแปลง RDD ที่มีขนาด N เป็นขนาด N อีกขนาดหนึ่ง เช่น.

myRDD.map(x => x*2)

ตัวอย่างเช่นหาก myRDD ประกอบด้วย Doubles

ในขณะที่ flatMap สามารถเปลี่ยน RDD ให้เป็นอับละอองเกสรตัวหนึ่งที่มีขนาดแตกต่างกันเช่น:

myRDD.flatMap(x =>new Seq(2*x,3*x))

ซึ่งจะส่งคืน RDD ที่มีขนาด 2 * N หรือ

myRDD.flatMap(x =>if x<10 new Seq(2*x,3*x) else new Seq(x) )

17

มันทำให้คำถามแรกเริ่มของคุณเดือด: คุณหมายความว่าอย่างไรเมื่อแบน ?

เมื่อคุณใช้ 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 เมื่อ

  • ผลการทำงานของแผนที่ของคุณในการสร้างโครงสร้างหลายชั้น
  • แต่สิ่งที่คุณต้องการคือโครงสร้างมิติเดียวที่เรียบง่ายโดยลบการจัดกลุ่มภายในทั้งหมดออก

15

ใช้test.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.)

หากคุณใช้mapวิธีการคุณจะได้รับบรรทัดtest.mdสำหรับflatMapวิธีการคุณจะได้รับจำนวนคำ

mapวิธีการที่คล้ายกันคือการflatMapที่พวกเขาจะกลับมาทั้งหมด RDD ใหม่ mapวิธีการมักจะใช้คืน RDD ใหม่flatMapวิธีมักจะใช้คำแยก


9

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

flatMap.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

8

map และ flatMap มีความคล้ายคลึงกันในแง่ที่ว่าพวกเขาใช้เส้นจากอินพุต RDD และใช้ฟังก์ชั่นกับมัน วิธีที่พวกเขาแตกต่างกันคือฟังก์ชั่นในแผนที่ส่งคืนองค์ประกอบเดียวเท่านั้นในขณะที่ฟังก์ชั่นใน flatMap สามารถส่งกลับรายการขององค์ประกอบ (0 หรือมากกว่า) เป็นตัววนซ้ำ

นอกจากนี้เอาต์พุตของ flatMap จะแบน แม้ว่าฟังก์ชั่นใน flatMap จะส่งคืนรายการองค์ประกอบ แต่ flatMap จะส่งคืน RDD ซึ่งมีองค์ประกอบทั้งหมดจากรายการในลักษณะแบบแบน (ไม่ใช่รายการ)


7

ตัวอย่างทั้งหมดเป็นสิ่งที่ดี .... นี่คือภาพประกอบภาพที่ดี ... มารยาทแหล่งที่มา: การฝึกอบรม DataFlair ของประกายไฟ

แผนที่: แผนที่เป็นการดำเนินการเปลี่ยนแปลงใน Apache Spark มันใช้กับแต่ละองค์ประกอบของ RDD และส่งกลับผลลัพธ์เป็น RDD ใหม่ ในแผนที่ผู้พัฒนาการดำเนินการสามารถกำหนดตรรกะทางธุรกิจที่กำหนดเองของเขาเอง ตรรกะเดียวกันจะถูกนำไปใช้กับองค์ประกอบทั้งหมดของ RDD

mapฟังก์ชั่นSpark RDD ใช้เวลาหนึ่งองค์ประกอบในการดำเนินการอินพุตตามรหัสที่กำหนดเอง (ระบุโดยนักพัฒนา) และส่งกลับองค์ประกอบหนึ่งครั้ง แผนที่แปลง RDD ที่มีความยาว N เป็น RDD ที่มีความยาว N อื่นโดยทั่วไปอินพุตและเอาต์พุต RDDs จะมีจำนวนระเบียนเท่ากัน

ป้อนคำอธิบายรูปภาพที่นี่

ตัวอย่างการ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:

A flatMapคือการแปลงสภาพ ซึ่งจะใช้กับองค์ประกอบของ RDD RDDแต่ละคนและจะส่งกลับผลตามที่ใหม่ มันคล้ายกับแผนที่ แต่ FlatMap อนุญาตให้ส่งคืนองค์ประกอบ 0, 1 หรือมากกว่าจากฟังก์ชั่นแผนที่ ในการดำเนินการ FlatMap ผู้พัฒนาสามารถกำหนดตรรกะทางธุรกิจที่กำหนดเองของเขาเอง ตรรกะเดียวกันจะถูกนำไปใช้กับองค์ประกอบทั้งหมดของ RDD

"การเรียบผลลัพธ์" หมายความว่าอย่างไร

ฟังก์ชั่น FlatMap ใช้เวลาหนึ่งองค์ประกอบเป็นกระบวนการป้อนข้อมูลตามรหัสที่กำหนดเอง (ระบุโดยนักพัฒนา) และส่งกลับองค์ประกอบ 0 หรือมากกว่าในเวลา flatMap() แปลง RDD ของความยาว N เป็น RDD อีกความยาว M

ป้อนคำอธิบายรูปภาพที่นี่

ตัวอย่างการ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)

5

ความแตกต่างสามารถเห็นได้จากรหัส pyspark ตัวอย่างด้านล่าง:

rdd = sc.parallelize([2, 3, 4])
rdd.flatMap(lambda x: range(1, x)).collect()
Output:
[1, 1, 2, 1, 2, 3]


rdd.map(lambda x: range(1, x)).collect()
Output:
[[1], [1, 2], [1, 2, 3]]

3

Flatmap และ Map ทั้งแปลงคอลเลกชัน

ความแตกต่าง:

map (func)
ส่งคืนชุดข้อมูลแบบกระจายใหม่ที่เกิดขึ้นโดยส่งแต่ละองค์ประกอบของแหล่งข้อมูลผ่านฟังก์ชั่น func

flatMap (func)
คล้ายกับ map แต่แต่ละไอเท็มอินพุตสามารถแม็พกับ 0 หรือมากกว่าไอเท็มเอาต์พุต (ดังนั้น func ควรส่งคืน Seq แทนที่จะเป็นไอเท็มเดียว)

ฟังก์ชั่นการแปลง:
แผนที่ : องค์ประกอบหนึ่งใน -> หนึ่งองค์ประกอบออก
flatMap : องค์ประกอบหนึ่งใน -> 0 หรือมากกว่าองค์ประกอบ (คอลเลกชัน)


3

RDD.map ส่งคืนองค์ประกอบทั้งหมดในอาร์เรย์เดียว

RDD.flatMap ส่งคืนองค์ประกอบในอาร์เรย์ของอาร์เรย์

สมมติว่าเรามีข้อความในไฟล์ text.txt เป็น

Spark is an expressive framework
This text is to understand map and faltMap functions of Spark RDD

ใช้แผนที่

val text=sc.textFile("text.txt").map(_.split(" ")).collect

เอาท์พุท:

text: **Array[Array[String]]** = Array(Array(Spark, is, an, expressive, framework), Array(This, text, is, to, understand, map, and, faltMap, functions, of, Spark, RDD))

ใช้ flatMap

val text=sc.textFile("text.txt").flatMap(_.split(" ")).collect

เอาท์พุท:

 text: **Array[String]** = Array(Spark, is, an, expressive, framework, This, text, is, to, understand, map, and, faltMap, functions, of, Spark, RDD)

2

สำหรับผู้ที่ต้องการ PySpark ที่เกี่ยวข้อง:

ตัวอย่างการแปลง: flatMap

>>> a="hello what are you doing"
>>> a.split()

['สวัสดีคุณกำลังทำอะไรอยู่']

>>> b=["hello what are you doing","this is rak"]
>>> b.split()

Traceback (การโทรล่าสุดครั้งล่าสุด): ไฟล์ "", บรรทัด 1, ใน AttributeError: 'รายการ' วัตถุไม่มีแอตทริบิวต์ 'แยก'

>>> rline=sc.parallelize(b)
>>> type(rline)

>>> def fwords(x):
...     return x.split()


>>> rword=rline.map(fwords)
>>> rword.collect()

[['hello', 'what', 'are', 'you', 'doing'], ['this', 'is', 'rak']]

>>> rwordflat=rline.flatMap(fwords)
>>> rwordflat.collect()

['hello', 'what', 'are', 'you', 'doing', 'this', 'is', 'rak']

หวังว่าจะช่วย :)


2

map: มันกลับใหม่โดยใช้ฟังก์ชั่นให้องค์ประกอบของแต่ละRDD RDDฟังก์ชันใน. map สามารถส่งคืนได้เพียงหนึ่งรายการเท่านั้น

flatMap: คล้ายกับ map มันส่งคืนค่าใหม่RDDโดยใช้ฟังก์ชั่นกับแต่ละองค์ประกอบของ RDD แต่เอาต์พุตจะถูกทำให้แบน

นอกจากนี้ฟังก์ชั่นในflatMapสามารถส่งคืนรายการองค์ประกอบ (0 หรือมากกว่า)

ตัวอย่างเช่น:

sc.parallelize([3,4,5]).map(lambda x: range(1,x)).collect()

ผลลัพธ์: [[1, 2], [1, 2, 3], [1, 2, 3, 4]]

sc.parallelize([3,4,5]).flatMap(lambda x: range(1,x)).collect()

เอาท์พุท: สังเกต o / p แบนในรายการเดียว [1, 2, 1, 2, 3, 1, 2, 3, 4]

ที่มา: https://www.linkedin.com/pulse/difference-between-map-flatmap-transformations-spark-pyspark-pandey/


0

แผนที่:

เป็นวิธีการเรียงลำดับที่สูงกว่าที่ใช้ฟังก์ชันเป็นอินพุตและใช้กับแต่ละองค์ประกอบใน RDD ต้นทาง

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

flatMap:

วิธีการเรียงลำดับที่สูงขึ้นและการดำเนินการแปลงที่ใช้ฟังก์ชั่นอินพุต


-1

ความแตกต่างในผลลัพธ์ของแผนที่และ flatMap:

1flatMap

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. map:

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

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

เอาท์พุท:

3 6 6 3 8

-1
  • map (func) ส่งคืนชุดข้อมูลแบบกระจายใหม่ที่เกิดขึ้นโดยการส่งแต่ละองค์ประกอบของแหล่งข้อมูลผ่านฟังก์ชั่น func ที่ประกาศไว้ดังนั้น map () เป็นคำเดียว

whiles

  • flatMap (func) คล้ายกับ map แต่แต่ละไอเท็มอินพุตสามารถแม็พกับ 0 หรือมากกว่าไอเท็มเอาต์พุตดังนั้น func ควรส่งคืน Sequence แทนไอเท็มเดียว
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.