Quelle est la différence entre map et flatMap et un bon cas d'utilisation pour chacun?

249

Quelqu'un peut-il m'expliquer la différence entre map et flatMap et quel est un bon cas d'utilisation pour chacun?

Que signifie «aplatir les résultats»? À quoi ça sert?

Eran Witkon
la source
4
Depuis que vous avez ajouté la balise Spark, je suppose que vous posez des questions sur RDD.mapet RDD.flatMapdans Apache Spark . En général, les opérations RDD de Spark sont modélisées d'après leurs opérations de collecte Scala correspondantes. Les réponses dans stackoverflow.com/q/1059776/590203 , qui discutent de la distinction entre mapet flatMapdans Scala, peuvent vous être utiles.
Josh Rosen
1
La plupart des exemples ici semblent supposer que flatMap fonctionne uniquement sur la collecte, ce qui n'est pas le cas.
Boon

Réponses:

195

Voici un exemple de différence, en spark-shellsession:

Tout d'abord, quelques données - deux lignes de texte:

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

rdd.collect

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

Maintenant, maptransforme un RDD de longueur N en un autre RDD de longueur N.

Par exemple, il mappe de deux lignes en deux longueurs de ligne:

rdd.map(_.length).collect

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

Mais flatMap(en gros) transforme un RDD de longueur N en une collection de N collections, puis les aplatit en un seul RDD de résultats.

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

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

Nous avons plusieurs mots par ligne et plusieurs lignes, mais nous nous retrouvons avec un seul tableau de sortie de mots

Juste pour illustrer cela, flatMapping d'une collection de lignes à une collection de mots ressemble à:

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

Les RDD d'entrée et de sortie seront donc généralement de tailles différentes pour flatMap.

Si nous avions essayé d'utiliser mapavec notre splitfonction, nous nous serions retrouvés avec des structures imbriquées (un RDD de tableaux de mots, avec type RDD[Array[String]]) car nous devons avoir exactement un résultat par entrée:

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

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

Enfin, un cas spécial utile est le mappage avec une fonction qui peut ne pas retourner de réponse, et renvoie donc un Option. Nous pouvons utiliser flatMappour filtrer les éléments qui retournent Noneet extraire les valeurs de ceux qui retournent 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)

(notant ici qu'une option se comporte plutôt comme une liste qui a soit un élément, soit zéro élément)

ADN
la source
1
Est-ce que les appels séparés sur la carte donneraient ["a b c", "", "d"] => [["a","b","c"],[],["d"]]?
user2635088
1
Oui - (mais notez que ma notation informelle était juste destinée à indiquer une collection quelconque - en fait, le mappage splitsur une liste de chaînes produira une liste de tableaux)
DNA
2
Merci de l'avoir écrit, c'est la meilleure explication que j'ai lue pour distinguer la différence entre les mêmes
Rajiv
97

Généralement, nous utilisons l'exemple de comptage de mots dans hadoop. Je prendrai le même cas d'utilisation mapet utiliserai et flatMapnous verrons la différence dans la façon dont il traite les données.

Voici un exemple de fichier de données.

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

Le fichier ci-dessus sera analysé à l'aide de mapet flatMap.

En utilisant 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'entrée a 4 lignes et la taille de sortie est également 4, c'est-à-dire N éléments ==> N éléments.

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

La sortie est différente de la carte.


Attribuons 1 comme valeur à chaque clé pour obtenir le nombre de mots.

  • fm: RDD créé en utilisant flatMap
  • wc: RDD créé en utilisant 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)]

Alors que flatMapsur RDD wcdonnera la sortie indésirable ci-dessous:

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

Vous ne pouvez pas obtenir le nombre de mots si mapest utilisé à la place de flatMap.

Selon la définition, la différence entre mapet flatMapest:

map: Il renvoie un nouveau RDD en appliquant une fonction donnée à chaque élément du RDD. La fonction mapne renvoie qu'un seul élément.

flatMap: Similaire à map, il renvoie un nouveau RDD en appliquant une fonction à chaque élément du RDD, mais la sortie est aplatie.

yoga
la source
14
je pense que cette réponse est meilleure que la réponse acceptée.
Krishna
15
Pourquoi diable voudriez-vous créer des captures d'écran illisibles, alors que vous pourriez simplement copier coller le texte de sortie?
nbubis
Donc flatMap () est map () + "flatten" et je sais que cela n'a pas beaucoup de sens, mais existe-t-il une sorte de fonction "flatten" que nous pouvons utiliser après map ()?
burakongun
2
Votre code comporte une faute de frappe trompeuse. Le résultat de .map(lambda line:line.split(" "))n'est pas un tableau de chaînes. Vous devez passer data.collect() à wc.collectet vous verrez un tableau de tableaux.
swdev
1
oui, mais le résultat de la commande est toujours faux. as-tu couru wc.collect()?
swdev
18

Si vous demandez la différence entre RDD.map et RDD.flatMap dans Spark, map transforme un RDD de taille N en un autre de taille N. par exemple.

myRDD.map(x => x*2)

par exemple, si myRDD est composé de doubles.

FlatMap peut transformer le RDD en un anthère de taille différente: par exemple:

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

qui renverra un RDD de taille 2 * N ou

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

Cela se résume à votre question initiale: que voulez-vous dire par aplatissement ?

Lorsque vous utilisez flatMap, une collection "multidimensionnelle" devient une collection "unidimensionnelle" .

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)

Vous souhaitez utiliser un flatMap lorsque,

  • votre fonction de carte se traduit par la création de structures multicouches
  • mais tout ce que vous voulez, c'est une structure simple - plate - unidimensionnelle, en supprimant TOUS les regroupements internes
ramu
la source
15

Utilisez test.mdcomme exemple:

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

Si vous utilisez la mapméthode, vous obtiendrez les lignes de test.md, pour la flatMapméthode, vous obtiendrez le nombre de mots.

La mapméthode est similaire à flatMap, ils renvoient tous un nouveau RDD. mapsouvent utiliser pour retourner un nouveau RDD, flatMapsouvent utiliser des mots séparés.

pangpang
la source
9

maprenvoie RDD d'un nombre égal d'éléments alors flatMapqu'il ne le peut pas.

Un exemple d'utilisation pourflatMap filtrer les données manquantes ou incorrectes.

Un exemple de cas d'map utilisation pour une utilisation dans une grande variété de cas où le nombre d'éléments d'entrée et de sortie est le même.

number.csv

1
2
3
-
4
-
5

map.py ajoute tous les nombres dans 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 utilise flatMappour filtrer les données manquantes avant l'ajout. Moins de numéros sont ajoutés par rapport à la version précédente.

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
wannik
la source
8

map et flatMap sont similaires, dans le sens où ils prennent une ligne du RDD d'entrée et y appliquent une fonction. La façon dont ils diffèrent est que la fonction dans map ne renvoie qu'un seul élément, tandis que la fonction dans flatMap peut renvoyer une liste d'éléments (0 ou plus) en tant qu'itérateur.

De plus, la sortie du flatMap est aplatie. Bien que la fonction dans flatMap renvoie une liste d'éléments, le flatMap renvoie un RDD qui contient tous les éléments de la liste de manière plate (pas une liste).

Bhasker
la source
7

tous les exemples sont bons .... Voici une belle illustration visuelle ... source courtoisie: Formation DataFlair de spark

Carte: une carte est une opération de transformation dans Apache Spark. Il s'applique à chaque élément de RDD et renvoie le résultat en tant que nouveau RDD. Dans la carte, le développeur d'opérations peut définir sa propre logique métier personnalisée. La même logique sera appliquée à tous les éléments de RDD.

La mapfonction Spark RDD prend un élément en entrée le traite selon le code personnalisé (spécifié par le développeur) et retourne un élément à la fois. La carte transforme un RDD de longueur N en un autre RDD de longueur N. Les RDD d'entrée et de sortie auront généralement le même nombre d'enregistrements.

entrez la description de l'image ici

Exemple d' maputilisation de 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 flatMapest une opération de transformation. Il s'applique à chaque élément de RDD et renvoie le résultat comme nouveau RDD. Il est similaire à Map, mais FlatMap permet de retourner 0, 1 ou plusieurs éléments de la fonction map. Dans l'opération FlatMap, un développeur peut définir sa propre logique métier personnalisée. La même logique sera appliquée à tous les éléments du RDD.

Que signifie «aplatir les résultats»?

Une fonction FlatMap prend un élément en entrée, le traite selon le code personnalisé (spécifié par le développeur) et renvoie 0 ou plusieurs éléments à la fois. flatMap() transforme un RDD de longueur N en un autre RDD de longueur M.

entrez la description de l'image ici

Exemple d' flatMaputilisation de 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)
Ram Ghadiyaram
la source
5

La différence peut être vue à partir de l'exemple de code pyspark ci-dessous:

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]]
awadhesh pathak
la source
3

Flatmap et Map transforment tous les deux la collection.

Différence:

map (func)
Retourne un nouvel ensemble de données distribué formé en passant chaque élément de la source à travers une fonction func.

flatMap (func)
Similaire à map, mais chaque élément d'entrée peut être mappé à 0 ou plusieurs éléments de sortie (donc func doit retourner un Seq plutôt qu'un seul élément).

La fonction de transformation:
map : un élément en entrée -> un élément en sortie.
flatMap : un élément entrant -> 0 ou plusieurs éléments sortants (une collection).

Ajit K'sagar
la source
3

RDD.map renvoie tous les éléments dans un seul tableau

RDD.flatMap renvoie des éléments dans des tableaux de tableau

supposons que nous ayons du texte dans le fichier text.txt comme

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

Utilisation de la carte

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

production:

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

Utilisation de flatMap

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

production:

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

Pour tous ceux qui ont voulu PySpark liés:

Exemple de transformation: flatMap

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

['salut que fais tu']

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

Traceback (dernier appel le plus récent): Fichier "", ligne 1, dans AttributeError: l'objet 'list' n'a pas d'attribut 'split'

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

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


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

[['bonjour', 'quoi', 'êtes', 'vous', 'faire'], ['ceci', 'est', 'rak']]

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

['bonjour', 'quoi', 'êtes', 'vous', 'faire', 'ceci', 'est', 'rak']

J'espère que ça aide :)

Rakshith N Gowda
la source
2

map: Il retourne un nouveau RDDen appliquant une fonction à chaque élément du RDD. La fonction dans .map ne peut renvoyer qu'un seul élément.

flatMap: Similaire à la carte, il renvoie un nouveau RDDen appliquant une fonction à chaque élément du RDD, mais la sortie est aplatie.

De plus, la fonction in flatMappeut renvoyer une liste d'éléments (0 ou plus)

Par exemple:

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

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

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

Sortie: l'avis o / p est aplati dans une seule liste [1, 2, 1, 2, 3, 1, 2, 3, 4]

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

Pushkar Deshpande
la source
-1

Différence de sortie de map et flatMap:

1.flatMap

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

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

Production:

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

Production:

3 6 6 3 8
Ashutosh S
la source
-1
  • map (func) Retourne un nouvel ensemble de données distribué formé en passant chaque élément de la source à travers une fonction func declare.so map () est un terme unique

alors

  • flatMap (func) Similaire à la carte, mais chaque élément d'entrée peut être mappé à 0 ou plusieurs éléments de sortie, donc func doit retourner une séquence plutôt qu'un seul élément.
Kondas Lamar Jnr
la source