В чем разница между map и flatMap и хорошим вариантом использования для каждого?


249

Может кто-нибудь объяснить мне разницу между map и flatMap и каков хороший вариант использования для каждого?

Что значит «сгладить результаты»? Для чего это?


4
Поскольку вы добавили тег Spark, я предполагаю, что вы спрашиваете об этом RDD.mapи RDD.flatMapв Apache Spark . В целом, операции RDD в Spark моделируются после соответствующих операций сбора Scala. Ответы в stackoverflow.com/q/1059776/590203 , в которых обсуждается различие между mapи flatMapв Scala, могут быть вам полезны.
Джош Розен

1
В большинстве приведенных здесь примеров предполагается, что flatMap работает только с коллекцией, а это не так.
Бун

Ответы:


195

Вот пример разницы, как spark-shellсеанс:

Во-первых, некоторые данные - две строки текста:

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

rdd.collect

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

Теперь mapпреобразует СДР длины N в другой СДР длины N.

Например, он отображается из двух строк в две длины строки:

rdd.map(_.length).collect

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

Но flatMap(грубо говоря) преобразует СДР длины N в набор из N коллекций, а затем объединяет их в одну СДР результатов.

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

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

У нас есть несколько слов в строке и несколько строк, но в итоге мы получаем один выходной массив слов

Просто чтобы проиллюстрировать это, flatMapping из набора строк в набор слов выглядит так:

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

Поэтому входные и выходные СДР обычно имеют разные размеры flatMap.

Если бы мы попытались использовать mapнашу splitфункцию, мы бы получили вложенные структуры (СДР из массивов слов с типом RDD[Array[String]]), потому что мы должны иметь ровно один результат на вход:

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

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

Наконец, одним полезным частным случаем является сопоставление с функцией, которая может не возвращать ответ, и поэтому возвращает Option. Мы можем использовать flatMapдля фильтрации элементов, которые возвращают Noneи извлекать значения из тех, которые возвращают 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)

(отмечая здесь, что Option ведет себя скорее как список, который имеет либо один элемент, либо ноль элементов)


1
Будет ли коллинг внутри карты давать ["a b c", "", "d"] => [["a","b","c"],[],["d"]]?
user2635088

1
Да - (но учтите, что моя неформальная запись была просто предназначена для обозначения некоторой коллекции - на самом деле отображение splitсписка строк приведет к созданию списка массивов)
DNA

2
Спасибо за то, что написали это, это лучшее объяснение, которое я прочитал, чтобы отличить одно и то же
Раджив

98

Обычно мы используем пример подсчета слов в hadoop. Я возьму тот же вариант использования и буду использовать, mapи flatMapмы увидим разницу в том, как он обрабатывает данные.

Ниже приведен пример файла данных.

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

Приведенный выше файл будет проанализирован с помощью mapи 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']

Вход имеет 4 строки, а размер вывода также равен 4, то есть N элементов ==> N элементов.

С помощью 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']

Вывод отличается от карты.


Давайте назначим 1 в качестве значения для каждого ключа, чтобы получить количество слов.

  • fm: СДР, созданный с помощью flatMap
  • wc: СДР создан с использованием 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)]

Принимая во внимание, что flatMapна RDD wcвыдаст следующие нежелательные выходные данные:

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

Вы не можете получить количество слов, если mapиспользуется вместо flatMap.

Согласно определению, разница между mapи flatMapсоставляет:

map: Возвращает новый СДР, применяя данную функцию к каждому элементу СДР. Функция в mapвозвращает только один элемент.

flatMapАналогично map, он возвращает новый RDD, применяя функцию к каждому элементу RDD, но вывод выравнивается.


14
Я чувствую, что этот ответ лучше, чем принятый ответ.
Кришна

15
С какой стати вы создавали неразборчивые скриншоты, когда вы могли просто скопировать и вставить выходной текст?
nbubis

Так что flatMap () - это map () + "flatten", и я знаю, что в этом нет особого смысла, но есть ли какая-нибудь функция "flatten", которую мы можем использовать после map ()?
burakongun

2
Ваш код содержит неверную опечатку. Результатом .map(lambda line:line.split(" "))является не массив строк. Вы должны изменить data.collect() на wc.collectи вы увидите массив массивов.
swdev

1
да, но результат команды все еще неправильный. ты бежал wc.collect()?
swdev

18

Если вы спрашиваете разницу между RDD.map и RDD.flatMap в Spark, map преобразует RDD размера N в другой размер N. например.

myRDD.map(x => x*2)

например, если myRDD состоит из Doubles.

В то время как flatMap может преобразовать RDD в другой, другой размер: например:

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

который вернет СДР размером 2 * N или

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

17

Это сводится к вашему первоначальному вопросу: что вы подразумеваете под сглаживанием ?

Когда вы используете flatMap, «многомерная» коллекция становится «одномерной» .

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)

Вы хотите использовать flatMap, когда,

  • Ваша функция карты приводит к созданию многослойных структур
  • но все, что вам нужно, это простая - плоская - одномерная структура, удалив ВСЕ внутренние группировки

15

Используйте test.mdв качестве примера:

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

Если вы используете mapметод, вы получите строки test.md, для flatMapметода вы получите количество слов.

mapМетод подобен flatMap, они все возвращаются новый РДД. mapметод часто использовать, чтобы вернуть новый RDD, flatMapметод часто использовать разделенные слова.


9

mapвозвращает СДР с равным количеством элементов, а flatMapможет и нет.

Пример варианта использования дляflatMap фильтрации отсутствующих или неверных данных.

Примерmap варианта использования для использования в самых разнообразных случаях, когда количество элементов ввода и вывода одинаково.

number.csv

1
2
3
-
4
-
5

map.py добавляет все числа в 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 использует flatMapдля фильтрации отсутствующих данных перед добавлением. Меньше номеров добавлено по сравнению с предыдущей версией.

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 и flatMap похожи в том смысле, что они берут строку из входного RDD и применяют к ней функцию. Они отличаются тем, что функция на карте возвращает только один элемент, а функция в flatMap может возвращать список элементов (0 или более) в качестве итератора.

Кроме того, вывод flatMap сглаживается. Хотя функция в flatMap возвращает список элементов, flatMap возвращает RDD, в котором все элементы из списка отображаются плоским способом (не списком).


7

все примеры хороши .... Вот хорошая наглядная иллюстрация ... источник любезно: DataFlair обучение искры

Карта: карта - это операция преобразования в Apache Spark. Он применяется к каждому элементу СДР и возвращает результат как новый СДР. На карте разработчик операции может определить свою собственную бизнес-логику. Та же логика будет применяться ко всем элементам СДР.

mapФункция Spark RDD принимает один элемент в качестве входного процесса, обрабатывает его в соответствии с пользовательским кодом (указанным разработчиком) и возвращает один элемент за раз. Карта преобразует СДР длиной N в другой СДР длины N. Как правило, входные и выходные СДР имеют одинаковое количество записей.

введите описание изображения здесь

Пример mapиспользования 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:

А flatMapявляется операцией преобразования. Он применяется к каждому элементу СДР и возвращает результат как новый RDD. Он похож на Map, но FlatMap позволяет возвращать 0, 1 или более элементов из функции карты. В операции FlatMap разработчик может определить свою собственную бизнес-логику. Та же логика будет применяться ко всем элементам СДР.

Что значит «сгладить результаты»?

Функция FlatMap принимает один элемент в качестве входного процесса, обрабатывает его в соответствии с пользовательским кодом (указанным разработчиком) и возвращает 0 или более элементов одновременно. flatMap() преобразует СДР длины N в другой СДР длины M.

введите описание изображения здесь

Пример flatMapиспользования 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

Разницу можно увидеть из приведенного ниже примера кода 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]]

3

Flatmap и Map преобразуют коллекцию.

Разница:

map (func)
Возвращает новый распределенный набор данных, сформированный путем передачи каждого элемента источника через функцию func.

flatMap (func)
Аналогично map, но каждый входной элемент может быть сопоставлен с 0 или более выходными элементами (поэтому func должен возвращать Seq, а не один элемент).

Функция преобразования:
map : один элемент в -> один элемент в.
flatMap : один элемент в -> 0 или более элементов (коллекция).


3

RDD.map возвращает все элементы в одном массиве

RDD.flatMap возвращает элементы в массивах массива

давайте предположим, что у нас есть текст в файле text.txt как

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

Используя карту

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

вывод:

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

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

вывод:

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

2

Для всех тех, кто хотел PySpark связанных:

Пример преобразования: flatMap

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

['Привет, чем занимаешься']

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

Traceback (последний вызов был последним): файл "", строка 1, в AttributeError: объект list не имеет атрибута split

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

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


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

[['привет', 'что', 'есть', 'ты', 'делаешь'], ['this', 'is', 'rak']]

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

['привет', 'что', 'есть', 'ты', 'делаешь', 'это', 'есть', 'rak']

Надеюсь, поможет :)


2

map: Возвращает новое RDD, применяя функцию к каждому элементу RDD. Функция в .map может вернуть только один элемент.

flatMap: Подобно карте, он возвращает новое RDD, применяя функцию к каждому элементу СДР, но вывод выравнивается.

Также функция in flatMapможет возвращать список элементов (0 или более)

Например:

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

Вывод: [[1, 2], [1, 2, 3], [1, 2, 3, 4]]

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

Вывод: уведомление о / п сведено в единый список [1, 2, 1, 2, 3, 1, 2, 3, 4]

Источник: https://www.linkedin.com/pulse/difference-between-map-flatmap-transformations-spark-pyspark-pandey/


0

карта :

это метод более высокого порядка, который принимает функцию в качестве входных данных и применяет ее к каждому элементу в исходном СДР.

http://commandstech.com/difference-between-map-and-flatmap-in-spark-what-is-map-and-flatmap-with-examples/

flatMap:

метод более высокого порядка и операция преобразования, которая принимает входную функцию.


-1

Разница в выводе карты и flatMap:

1.flatMap

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

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

Вывод:

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

Вывод:

3 6 6 3 8

-1
  • map (func) Возвращает новый распределенный набор данных, сформированный путем передачи каждого элемента источника через функцию func Объявленный.

в то время как

  • flatMap (func) Аналогично map, но каждый входной элемент может быть сопоставлен с 0 или более выходными элементами, поэтому func должен возвращать последовательность, а не один элемент.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.