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?
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)
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
.
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.
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 dansmap
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.
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) )
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.
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,
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
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).
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]]
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.
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.
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)
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)
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).
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/
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 :)