web-dev-qa-db-fra.com

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

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

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

227
Eran Witkon

Voici un exemple de différence, sous la forme d'une session spark-Shell:

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, map transforme un RDD de longueur N en un autre RDD de longueur N.

Par exemple, il mappe deux lignes en deux longueurs:

rdd.map(_.length).collect

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

Mais flatMap (en termes simples) 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 mots en sortie

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

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

Les RDD d’entrée et de sortie auront donc typiquement des tailles différentes pour flatMap.

Si nous avions essayé d'utiliser map avec notre fonction split, nous nous serions retrouvés avec des structures imbriquées (un RDD de tableaux de mots, avec le 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 particulier utile est le mappage avec une fonction qui peut ne pas renvoyer de réponse et renvoie donc un Option. Nous pouvons utiliser flatMap pour filtrer les éléments qui retournent None et extraire les valeurs de ceux qui renvoient un 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)

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

180
DNA

Généralement, nous utilisons l'exemple de nombre de mots dans hadoop. Je prendrai le même cas d'utilisation et utiliserai map et flatMap et nous verrons la différence de traitement des données.

Voici l'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é avec map et flatMap.

Utiliser 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 la sortie 4 également, c’est-à-dire N éléments ==> N éléments.

Utiliser 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 touche pour obtenir le nombre de mots.

  • fm: RDD créé à l'aide de flatMap
  • wc: RDD créé à l'aide de 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 map sur RDD wc donnera la sortie non désirée 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 map est utilisé à la place de flatMap.

Selon la définition, la différence entre map et flatMap est la suivante:

map: Il retourne un nouveau RDD en appliquant une fonction donnée à chaque élément du RDD. Fonction dans map renvoie un seul élément.

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

85
yoga

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.

Alors que flatMap peut transformer le RDD en un autre de taille différente:

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

qui retournera un RDD de taille 2 * N ou

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

Utilisez test.md comme 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 méthode map, vous obtiendrez les lignes de test.md, pour la méthode flatMap, vous obtiendrez le nombre de mots.

La méthode map est similaire à flatMap, elles renvoient toutes un nouveau RDD. map la méthode souvent utilisée retourne un nouveau RDD, flatMap la méthode souvent à l'aide de mots divisés.

15
pangpang

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

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

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 voulez utiliser un flatMap quand,

  • votre fonction de carte crée des structures multi-couches
  • mais tout ce que vous voulez, c'est une structure simple - plate - à une dimension, en supprimant TOUS les regroupements internes
12
ramu

map renvoie RDD d'un nombre égal d'éléments alors que flatMap ne le peut pas.

Exemple d'utilisation de flatMap Filtrer les données manquantes ou incorrectes.

Exemple d'utilisation de map Utilisation dans un grand nombre de cas où le nombre d'éléments d'entrée et de sortie est identique.

nombre.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 flatMap pour filtrer les données manquantes avant leur addition. Moins de chiffres 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
9
wannik

map et flatMap sont similaires, dans le sens où ils prennent une ligne du RDD en entrée et y appliquent une fonction. La différence entre ces deux méthodes est que la fonction dans map ne renvoie qu'un élément, alors que function dans flatMap peut renvoyer une liste d'éléments (0 ou plus) sous forme d'itérateur.

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

8
Bhasker

La différence peut être vue ci-dessous avec l'exemple de code 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]]
5
awadhesh pathak

tous les exemples sont bons .... Voici une belle illustration visuelle ... source de courtoisie: DataFlair formation of 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 mappe, le développeur d'opération peut définir sa propre logique métier personnalisée. La même logique sera appliquée à tous les éléments du RDD.

La fonction Spark RDD map prend un élément en entrée, la traite conformément au code personnalisé (spécifié par le développeur) et renvoie un élément à la fois. Map 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.

enter image description here

Exemple de map utilisant 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:

Un flatMap est une opération de transformation. Il s'applique à chaque élément de RDD et renvoie le résultat sous la forme de new RDD. Il ressemble à Map, mais FlatMap permet de renvoyer 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, la traite conformément au code personnalisé (spécifié par le développeur) et renvoie 0 élément ou plus à la fois. flatMap () transforme un RDD de longueur N en un autre RDD de longueur M.

enter image description here

Exemple de flatMap utilisant 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)
4
Ram Ghadiyaram

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

RDD.flatMap retourne des éléments dans les tableaux de tableau

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

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

Utiliser la carte

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

sortie:

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

Utiliser flatMap

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

sortie:

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

Flatmap et Map transforment 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 en entrée peut être mappé à 0 ou plusieurs éléments en sortie (donc, func devrait renvoyer un Seq plutôt qu'un seul élément).

La fonction de transformation:
map: Un élément dans -> un élément dans.
flatMap: Un élément sur -> 0 ou plusieurs éléments sur (une collection).

3
Ajit K'sagar

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

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

De plus, la fonction dans flatMap peut 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: la note o/p est aplatie dans une liste unique [1, 2, 1, 2, 3, 1, 2, 3, 4]

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

2
Pushkar Deshpande

Pour tous ceux qui ont voulu relier PySpark:

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 "divisé"

>>> rline=sc.parallelize(b)
>>> type(rline)
>>> def fwords(x):
...     return x.split()


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

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

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

['bonjour', 'quoi', 'es', 'toi', 'faire', 'ceci', 'est', 'rak']

J'espère que ça aide :)

2