Was ist der Unterschied zwischen Map und FlatMap und ein guter Anwendungsfall für jeden?


249

Kann mir jemand den Unterschied zwischen Map und FlatMap erklären und was ist ein guter Anwendungsfall für jeden?

Was bedeutet "Ergebnisse reduzieren"? Wozu ist es gut?


4
Da Sie das Spark-Tag hinzugefügt haben, gehe ich davon aus, dass Sie nach RDD.mapund RDD.flatMapin Apache Spark fragen . Im Allgemeinen werden die RDD-Operationen von Spark nach den entsprechenden Scala-Erfassungsoperationen modelliert. Die Antworten in stackoverflow.com/q/1059776/590203 , in denen die Unterscheidung zwischen mapund flatMapin Scala erläutert wird , können für Sie hilfreich sein.
Josh Rosen

1
Die meisten Beispiele hier scheinen davon auszugehen, dass flatMap nur mit der Erfassung arbeitet, was nicht der Fall ist.
Segen

Antworten:


195

Hier ist ein Beispiel für den Unterschied als spark-shellSitzung:

Zunächst einige Daten - zwei Textzeilen:

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

rdd.collect

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

Nun mapwandelt eine RDD der Länge N in einer anderen RDD der Länge N

Beispielsweise werden zwei Zeilen in zwei Zeilenlängen abgebildet:

rdd.map(_.length).collect

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

Aber flatMap(lose gesagt) transformiert eine RDD der Länge N in eine Sammlung von N Sammlungen und glättet diese dann in eine einzelne RDD von Ergebnissen.

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

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

Wir haben mehrere Wörter pro Zeile und mehrere Zeilen, aber am Ende haben wir ein einziges Ausgabearray von Wörtern

Um dies zu veranschaulichen, sieht flatMapping von einer Sammlung von Zeilen zu einer Sammlung von Wörtern folgendermaßen aus:

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

Die Eingangs- und Ausgangs-RDDs haben daher typischerweise unterschiedliche Größen für flatMap.

Wenn wir versucht hätten, mapmit unserer splitFunktion zu arbeiten, hätten wir verschachtelte Strukturen erhalten (eine RDD von Arrays von Wörtern mit Typ RDD[Array[String]]), weil wir genau ein Ergebnis pro Eingabe haben müssen:

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

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

Ein nützlicher Sonderfall ist schließlich die Zuordnung zu einer Funktion, die möglicherweise keine Antwort zurückgibt und daher eine zurückgibt Option. Wir können verwenden flatMap, um die Elemente herauszufiltern, die zurückgeben, Noneund die Werte aus denen zu extrahieren, die a zurückgeben 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)

(Beachten Sie hier, dass sich eine Option eher wie eine Liste verhält, die entweder ein Element oder null Elemente enthält.)


1
Würde es einen Aufruf geben, innerhalb der Karte geteilt zu werden ["a b c", "", "d"] => [["a","b","c"],[],["d"]]?
user2635088

1
Ja - (aber beachten Sie, dass meine informelle Notation nur eine Sammlung irgendeiner Art anzeigen sollte - tatsächlich wird durch Zuordnen splitüber eine Liste von Strings eine Liste von Arrays erstellt)
DNA

2
Vielen Dank für das Aufschreiben, dies ist die beste Erklärung, die ich gelesen habe, um den Unterschied zwischen denselben zu unterscheiden
Rajiv

97

Im Allgemeinen verwenden wir ein Beispiel für die Wortanzahl in hadoop. Ich werde den gleichen Anwendungsfall nehmen und verwenden mapund flatMapund wir werden den Unterschied sehen, wie es die Daten verarbeitet.

Unten finden Sie die Beispieldatendatei.

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

Die obige Datei wird mit mapund analysiert flatMap.

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

Die Eingabe hat 4 Zeilen und die Ausgabegröße beträgt ebenfalls 4, dh N Elemente ==> N Elemente.

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

Die Ausgabe unterscheidet sich von der Karte.


Weisen wir jedem Schlüssel 1 als Wert zu, um die Wortanzahl zu erhalten.

  • fm: RDD erstellt mit flatMap
  • wc: RDD erstellt mit 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)]

Während flatMapauf RDD wcdie folgende unerwünschte Ausgabe liefert:

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

Sie können die Wortanzahl nicht erhalten, wenn mapanstelle von verwendet wird flatMap.

Gemäß der Definition ist der Unterschied zwischen mapund flatMap:

map: Es wird eine neue RDD zurückgegeben, indem auf jedes Element der RDD eine bestimmte Funktion angewendet wird. Die Funktion in mapgibt nur einen Artikel zurück.

flatMap: Ähnlich wie mapwird eine neue RDD zurückgegeben, indem auf jedes Element der RDD eine Funktion angewendet wird, die Ausgabe wird jedoch reduziert.


14
Ich denke, diese Antwort ist besser als die akzeptierte Antwort.
Krishna

15
Warum um alles in der Welt würden Sie unleserliche Screenshots erstellen, wenn Sie den Ausgabetext einfach kopieren und einfügen könnten?
Nbubis

FlatMap () ist also map () + "flatten" und ich weiß, dass es nicht viel Sinn macht, aber gibt es irgendeine Art von "flatten" -Funktion, die wir nach map () verwenden können?
Burakongun

2
Ihr Code hat einen irreführenden Tippfehler. Das Ergebnis von .map(lambda line:line.split(" "))ist kein Array von Zeichenfolgen. Sie sollten ändern data.collect() zu wc.collectund Sie werden ein Array von Arrays sehen.
swdev

1
Ja, aber das Ergebnis des Befehls ist immer noch falsch. hast du laufen wc.collect()?
swdev

18

Wenn Sie in Spark nach dem Unterschied zwischen RDD.map und RDD.flatMap fragen, transformiert map eine RDD der Größe N in eine andere der Größe N. z.B.

myRDD.map(x => x*2)

Zum Beispiel, wenn myRDD aus Doubles besteht.

Während flatMap das RDD in ein anderes mit einer anderen Größe umwandeln kann: z.

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

Dies gibt eine RDD der Größe 2 * N oder zurück

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

17

Es läuft auf Ihre erste Frage hinaus: Was meinen Sie mit Abflachen ?

Wenn Sie flatMap verwenden, wird eine "mehrdimensionale" Sammlung zu einer "eindimensionalen" Sammlung.

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)

Sie möchten eine flatMap verwenden, wenn,

  • Ihre Kartenfunktion führt zur Erstellung mehrschichtiger Strukturen
  • Aber alles, was Sie wollen, ist eine einfache - flache - eindimensionale Struktur, indem Sie ALLE internen Gruppierungen entfernen

15

Verwenden Sie test.mdals Beispiel:

➜  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.)

Wenn Sie mapmethod verwenden, erhalten Sie die Zeilen test.md, für flatMapmethod erhalten Sie die Anzahl der Wörter.

Die mapMethode ist ähnlich flatMap, sie geben alle eine neue RDD zurück. mapMethode häufig zur Verwendung einer neuen RDD zurückgeben, flatMapMethode häufig zur Verwendung von geteilten Wörtern.


9

mapGibt RDD mit der gleichen Anzahl von Elementen zurück, obwohl dies flatMapmöglicherweise nicht der Fall ist .

Ein Anwendungsbeispiel für dasflatMap Herausfiltern fehlender oder falscher Daten.

Ein Anwendungsbeispiel für diemap Verwendung in einer Vielzahl von Fällen, in denen die Anzahl der Eingabe- und Ausgabeelemente gleich ist.

number.csv

1
2
3
-
4
-
5

map.py fügt alle Zahlen in add.csv hinzu.

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 wird verwendet flatMap, um fehlende Daten vor dem Hinzufügen herauszufiltern. Im Vergleich zur vorherigen Version werden weniger Zahlen hinzugefügt.

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 und flatMap sind insofern ähnlich, als sie eine Linie von der Eingabe-RDD nehmen und eine Funktion darauf anwenden. Sie unterscheiden sich darin, dass die Funktion in map nur ein Element zurückgibt, während die Funktion in flatMap eine Liste von Elementen (0 oder mehr) als Iterator zurückgeben kann.

Außerdem wird die Ausgabe der flatMap abgeflacht. Obwohl die Funktion in flatMap eine Liste von Elementen zurückgibt, gibt die flatMap eine RDD zurück, die alle Elemente aus der Liste auf flache Weise enthält (keine Liste).


7

Alle Beispiele sind gut ... Hier ist eine schöne visuelle Illustration ... Quelle mit freundlicher Genehmigung: DataFlair Training of Spark

Karte: Eine Karte ist eine Transformationsoperation in Apache Spark. Es gilt für jedes Element von RDD und gibt das Ergebnis als neues RDD zurück. In der Map kann der Operationsentwickler seine eigene benutzerdefinierte Geschäftslogik definieren. Die gleiche Logik wird auf alle Elemente von RDD angewendet.

Die Spark-RDD- mapFunktion verwendet ein Element als Eingabeprozess gemäß dem vom Entwickler angegebenen benutzerdefinierten Code und gibt jeweils ein Element zurück. Map transformiert eine RDD der Länge N in eine andere RDD der Länge N. Die Eingabe- und Ausgabe-RDDs haben normalerweise die gleiche Anzahl von Datensätzen.

Geben Sie hier die Bildbeschreibung ein

Beispiel für die mapVerwendung von Scala:

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 flatMapist eine Transformationsoperation. Es gilt für jedes Element von RDD und gibt das Ergebnis als neu zurück RDD. Es ähnelt Map, aber FlatMap ermöglicht die Rückgabe von 0, 1 oder mehr Elementen aus der Map-Funktion. In der FlatMap-Operation kann ein Entwickler seine eigene benutzerdefinierte Geschäftslogik definieren. Die gleiche Logik wird auf alle Elemente der RDD angewendet.

Was bedeutet "Ergebnisse reduzieren"?

Eine FlatMap-Funktion verwendet ein Element als Eingabeprozess gemäß dem vom Entwickler angegebenen benutzerdefinierten Code und gibt jeweils 0 oder mehr Elemente zurück. flatMap() transformiert eine RDD der Länge N in eine andere RDD der Länge M.

Geben Sie hier die Bildbeschreibung ein

Beispiel für die flatMapVerwendung von Scala:

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

Der Unterschied ist aus dem folgenden Beispiel-Pyspark-Code ersichtlich:

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 und Map transformieren beide die Sammlung.

Unterschied:

map (func)
Gibt einen neuen verteilten Datensatz zurück, der durch Übergeben jedes Elements der Quelle durch eine Funktionsfunktion gebildet wird.

flatMap (func)
Ähnlich wie map, aber jedes Eingabeelement kann 0 oder mehr Ausgabeelementen zugeordnet werden (daher sollte func eine Seq anstelle eines einzelnen Elements zurückgeben).

Die Transformationsfunktion:
map : Ein Element rein -> ein Element raus.
flatMap : Ein Element in -> 0 oder mehr Elemente out (eine Sammlung).


3

RDD.map Gibt alle Elemente in einem Array zurück

RDD.flatMap Gibt Elemente in Arrays des Arrays zurück

Nehmen wir an, wir haben Text in der Datei text.txt als

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

Karte verwenden

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

Ausgabe:

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 verwenden

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

Ausgabe:

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

2

Für alle, die PySpark im Zusammenhang haben wollten:

Beispieltransformation: flatMap

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

['Hallo, was machst du']

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

Traceback (letzter Aufruf zuletzt): Datei "", Zeile 1, in AttributeError: Das Objekt 'list' hat kein Attribut 'split'.

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

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


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

[['Hallo', 'Was', 'Sind', 'Du', 'Tun'], ['Dies', 'Ist', 'Rak']]

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

['Hallo', 'Was', 'Sind', 'Du', 'Tun', 'Dies', 'Ist', 'Rak']

Ich hoffe es hilft :)


2

map: Es wird eine neue zurückgegeben, RDDindem auf jedes Element der eine Funktion angewendet wird RDD. Die Funktion in .map kann nur ein Element zurückgeben.

flatMap: Ähnlich wie bei der Karte, gibt es eine neue RDDdurch eine Funktion zu jedem Element der RDD Anwendung, aber die Ausgangs abgeflacht ist.

Außerdem kann function in flatMapeine Liste von Elementen zurückgeben (0 oder mehr).

Beispielsweise:

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

Ausgabe: [[1, 2], [1, 2, 3], [1, 2, 3, 4]]

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

Ausgabe: Hinweis o / p wird in einer einzelnen Liste abgeflacht [1, 2, 1, 2, 3, 1, 2, 3, 4]

Quelle: https://www.linkedin.com/pulse/difference-between-map-flatmap-transformations-spark-pyspark-pandey/



-1

Unterschied in der Ausgabe von Map und FlatMap:

1.flatMap

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

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

Ausgabe:

 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()

Ausgabe:

3 6 6 3 8

-1
  • map (func) Gibt ein neues verteiltes Dataset zurück, das durch Übergeben jedes Elements der Quelle durch eine deklarierte Funktion func gebildet wird. also map () ist ein einzelner Begriff

während

  • flatMap (func) Ähnlich wie map, aber jedes Eingabeelement kann 0 oder mehr Ausgabeelementen zugeordnet werden, sodass func eine Sequenz anstelle eines einzelnen Elements zurückgeben sollte.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.