Qual è la differenza tra map e flatMap e un buon caso d'uso per ciascuno?


249

Qualcuno può spiegarmi la differenza tra map e flatMap e qual è un buon caso d'uso per ciascuno?

Che cosa significa "appiattire i risultati"? Per cosa è buono?


4
Da quando hai aggiunto il tag Spark, suppongo che tu lo stia chiedendo RDD.mape RDD.flatMapin Apache Spark . In generale, le operazioni RDD di Spark sono modellate sulla base delle corrispondenti operazioni di raccolta Scala. Le risposte in stackoverflow.com/q/1059776/590203 , che discutono della distinzione tra mape flatMapin Scala, possono esserti utili.
Josh Rosen,

1
La maggior parte degli esempi qui sembra supporre che flatMap funzioni solo sulla raccolta, il che non è il caso.
Boon,

Risposte:


195

Ecco un esempio della differenza, come spark-shellsessione:

Innanzitutto, alcuni dati - due righe di testo:

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

rdd.collect

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

Ora maptrasforma un RDD di lunghezza N in un altro RDD di lunghezza N.

Ad esempio, mappa da due linee in due lunghezze di linea:

rdd.map(_.length).collect

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

Ma flatMap(parlando in senso lato) trasforma un RDD di lunghezza N in una raccolta di N raccolte, quindi li appiattisce in un singolo RDD di risultati.

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

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

Abbiamo più parole per riga e più righe, ma finiamo con un singolo array di parole di output

Giusto per illustrarlo, flatMapping da una raccolta di linee a una raccolta di parole assomiglia a:

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

I RDD di input e output saranno pertanto in genere di dimensioni diverse per flatMap.

Se avessimo provato a utilizzare la mapnostra splitfunzione, saremmo finiti con strutture nidificate (un RDD di matrici di parole, con tipo RDD[Array[String]]) perché dovremmo avere esattamente un risultato per input:

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

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

Infine, un utile caso speciale è il mapping con una funzione che potrebbe non restituire una risposta, e quindi restituisce un Option. Possiamo usare flatMapper filtrare gli elementi che restituiscono Noneed estrarre i valori da quelli che restituiscono a 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)

(notando qui che un'opzione si comporta piuttosto come un elenco che ha un elemento o zero elementi)


1
Darebbe una divisione nella mappa ["a b c", "", "d"] => [["a","b","c"],[],["d"]]?
user2635088

1
Sì - (ma nota che la mia notazione informale intendeva solo indicare una raccolta di qualche tipo - infatti mappare splitsu un elenco di stringhe produrrà un elenco di array)
DNA

2
Grazie per averlo scritto, questa è la migliore spiegazione che ho letto per distinguere la differenza tra le stesse
Rajiv,

97

Generalmente usiamo l'esempio del conteggio delle parole in hadoop. Prenderò lo stesso caso d'uso mape userò e flatMapvedremo la differenza come sta elaborando i dati.

Di seguito è riportato il file di dati di esempio.

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

Il file sopra sarà analizzato usando mape flatMap.

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

L'input ha 4 righe e anche la dimensione dell'output è 4, ovvero N elementi ==> N elementi.

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

L'output è diverso dalla mappa.


Assegniamo 1 come valore per ogni chiave per ottenere il conteggio delle parole.

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

Considerando che flatMapsu RDD wcdarà il risultato indesiderato di seguito:

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

Non è possibile ottenere il conteggio delle parole se mapviene utilizzato al posto di flatMap.

Secondo la definizione, la differenza tra mape flatMapè:

map: Restituisce un nuovo RDD applicando una determinata funzione a ciascun elemento del RDD. La funzione in maprestituisce solo un elemento.

flatMap: Simile a map, restituisce un nuovo RDD applicando una funzione a ciascun elemento del RDD, ma l'output è appiattito.


14
Sento che questa risposta è migliore della risposta accettata.
Krishna,

15
Perché mai crei screenshot illeggibili, quando potresti semplicemente copiare e incollare il testo di output?
nbubis,

Quindi flatMap () è map () + "flatten" e so che non ha molto senso ma c'è qualche tipo di funzione "flatten" che possiamo usare dopo map ()?
Burakongun,

2
Il tuo codice ha un errore di battitura fuorviante. Il risultato .map(lambda line:line.split(" "))non è una matrice di stringhe. Dovresti passare data.collect() a wc.collecte vedrai una serie di array.
swdev,

1
sì, ma il risultato del comando è ancora sbagliato. hai corso wc.collect()?
swdev,

18

Se stai chiedendo la differenza tra RDD.map e RDD.flatMap in Spark, map trasforma un RDD di dimensione N in un altro di dimensione N. per esempio.

myRDD.map(x => x*2)

ad esempio, se myRDD è composto da Doppio.

Mentre flatMap può trasformare l'RDD in un altro di dimensioni diverse: ad es .:

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

che restituirà un RDD di dimensioni 2 * N o

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

17

Si riduce alla tua domanda iniziale: cosa intendi per appiattimento ?

Quando si utilizza flatMap, una raccolta "multidimensionale" diventa una raccolta "monodimensionale" .

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)

Vuoi usare una mappa piatta quando,

  • la tua funzione mappa si traduce nella creazione di strutture a più livelli
  • ma tutto ciò che vuoi è una struttura semplice - piatta - monodimensionale, rimuovendo TUTTI i raggruppamenti interni

15

Usa test.mdcome esempio:

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

Se usi il mapmetodo, otterrai le righe di test.md, per il flatMapmetodo, otterrai il numero di parole.

Il mapmetodo è simile a flatMap, tutti restituiscono un nuovo RDD. mapil metodo spesso da usare restituisce un nuovo RDD, il flatMapmetodo spesso usa parole divise.


9

maprestituisce RDD di uguale numero di elementi mentre flatMappotrebbe non farlo.

Un esempio di caso d'uso perflatMap filtrare i dati mancanti o errati.

Un esempio di utilizzo per l'map uso in un'ampia varietà di casi in cui il numero di elementi di input e output è lo stesso.

number.csv

1
2
3
-
4
-
5

map.py aggiunge tutti i numeri in 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 utilizza flatMapper filtrare i dati mancanti prima dell'aggiunta. Vengono aggiunti meno numeri rispetto alla versione precedente.

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 e flatMap sono simili, nel senso che prendono una linea dall'input RDD e applicano una funzione su di esso. Il modo in cui differiscono è che la funzione in map restituisce solo un elemento, mentre la funzione in flatMap può restituire un elenco di elementi (0 o più) come iteratore.

Inoltre, l'output di flatMap è appiattito. Sebbene la funzione in flatMap restituisca un elenco di elementi, flatMap restituisce un RDD che ha tutti gli elementi dell'elenco in modo semplice (non un elenco).


7

tutti gli esempi sono buoni ... Ecco una bella illustrazione visiva ... fonte di cortesia: formazione DataFlair di spark

Mappa: una mappa è un'operazione di trasformazione in Apache Spark. Si applica a ciascun elemento di RDD e restituisce il risultato come nuovo RDD. Nella mappa, lo sviluppatore dell'operazione può definire la propria logica aziendale personalizzata. La stessa logica verrà applicata a tutti gli elementi di RDD.

La mapfunzione Spark RDD accetta un elemento mentre l'input lo elabora in base al codice personalizzato (specificato dallo sviluppatore) e restituisce un elemento alla volta. La mappa trasforma un RDD di lunghezza N in un altro RDD di lunghezza N. Gli RDD di input e output avranno in genere lo stesso numero di record.

inserisci qui la descrizione dell'immagine

Esempio di maputilizzo di 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 flatMapè un'operazione di trasformazione. Si applica a ciascun elemento di RDD e restituisce il risultato come nuovo RDD. È simile a Mappa, ma FlatMap consente di restituire 0, 1 o più elementi dalla funzione mappa. Nell'operazione FlatMap, uno sviluppatore può definire la propria logica aziendale personalizzata. La stessa logica verrà applicata a tutti gli elementi del RDD.

Che cosa significa "appiattire i risultati"?

Una funzione FlatMap accetta un elemento mentre l'input lo elabora secondo il codice personalizzato (specificato dallo sviluppatore) e restituisce 0 o più elementi alla volta. flatMap() trasforma un RDD di lunghezza N in un altro RDD di lunghezza M.

inserisci qui la descrizione dell'immagine

Esempio di flatMaputilizzo di 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

La differenza può essere vista dal seguente codice pyspark di esempio:

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 e Map trasformano entrambi la raccolta.

Differenza:

map (func)
Restituisce un nuovo set di dati distribuito formato passando ogni elemento della sorgente attraverso una funzione func.

flatMap (func)
Simile alla mappa, ma ogni elemento di input può essere mappato su 0 o più elementi di output (quindi func dovrebbe restituire un Seq anziché un singolo elemento).

La funzione di trasformazione:
mappa : un elemento in -> un elemento in uscita.
flatMap : un elemento in -> 0 o più elementi in uscita (una raccolta).


3

RDD.map restituisce tutti gli elementi in un singolo array

RDD.flatMap restituisce elementi in array di array

supponiamo di avere testo nel file text.txt come

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

Utilizzando la mappa

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

produzione:

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

Utilizzando flatMap

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

produzione:

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

2

Per tutti coloro che hanno voluto PySpark in relazione:

Trasformazione di esempio: flatMap

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

['Ciao, cosa stai facendo']

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

Traceback (ultima chiamata più recente): File "", riga 1, in AttributeError: l'oggetto 'list' non ha attributi 'split'

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

Spero che sia d'aiuto :)


2

map: Restituisce un nuovo RDDapplicando una funzione a ciascun elemento di RDD. La funzione in .map può restituire solo un elemento.

flatMap: Simile a mappare, restituisce una nuova RDDda applicando una funzione ad ogni elemento della RDD, ma l'uscita è appiattita.

Inoltre, la funzione in flatMappuò restituire un elenco di elementi (0 o più)

Per esempio:

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

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

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

Output: l'avviso o / p è appiattito in un unico elenco [1, 2, 1, 2, 3, 1, 2, 3, 4]

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



-1

Differenza nell'output di map e flatMap:

1.flatMap

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

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

Produzione:

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

Produzione:

3 6 6 3 8

-1
  • map (func) Restituisce un nuovo set di dati distribuito formato passando ogni elemento della sorgente attraverso una funzione func dichiarata. map () è a termine singolo

whiles

  • flatMap (func) Simile alla mappa, ma ogni elemento di input può essere mappato su 0 o più elementi di output, quindi func dovrebbe restituire una sequenza anziché un singolo elemento.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.