Яка різниця між картою та flatMap та корисним випадком використання для кожного?


249

Чи може хтось пояснити мені різницю між картою та flatMap та чим корисним є використання для кожного?

Що означає "вирівнювання результатів"? Для чого це добре?


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

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перетворює RDD довжиною N в інший RDD довжини N.

Наприклад, він відображає з двох рядків на дві довжини рядків:

rdd.map(_.length).collect

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

Але flatMap(слабко кажучи) перетворює RDD довжиною N в колекцію N колекцій, а потім згладжує їх в єдину RDD результатів.

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

Отже, вхідні та вихідні RDD зазвичай будуть різного розміру для flatMap .

Якби ми намагалися використовувати mapсвою splitфункцію, ми б закінчилися вкладеними структурами (RDD масивів слів з типом 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)

(тут зазначаючи, що Опція поводиться скоріше як список, який містить або один елемент, або нульові елементи)


1
Чи дасть дзвінок розділення в межах карти ["a b c", "", "d"] => [["a","b","c"],[],["d"]]?
користувач2635088

1
Так - (але зауважте, що моє неофіційне позначення було призначене лише для того, щоб вказати якусь колекцію - адже відображення splitсписку рядків створить список масивів)
ДНК

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: RDD створено за допомогою flatMap
  • wc: RDD створено за допомогою 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)]

Тоді як flatMapRDD 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: Він повертає новий RDD, застосовуючи задану функцію до кожного елемента RDD. Функція 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, карта перетворює RDD розміром N в інший розміром N. напр.

myRDD.map(x => x*2)

наприклад, якщо myRDD складається з парних пар.

У той час як flatMap може перетворити RDD в пильовик одного розміру: напр .:

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

який поверне RDD розміром 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 повертає RDD з рівною кількістю елементів при цьому flatMap може не бути.

Приклад використання випадку дляflatMap фільтрації відсутніх або неправильних даних.

Приклад використання випадку дляmap використання в найрізноманітніших випадках, коли кількість елементів введення та виведення однакові.

номер.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 повертає RDD, який містить усі елементи зі списку рівним способом (а не список).


7

всі приклади хороші .... Ось приємна візуальна ілюстрація ... ввічливість джерела: Навчання DataFlair іскри

Карта: Карта - це операція перетворення в Apache Spark. Він застосовується до кожного елемента RDD і повертає результат як новий RDD. На карті розробник операцій може визначити власну логіку бізнесу. Така ж логіка буде застосована до всіх елементів RDD.

mapФункція Іскра ІРД приймає один елемент як процес введення його відповідно до користувальницького коду (вказаний розробником) і повертає один елемент за один раз. Карта перетворює RDD довжиною N в інший RDD довжини N. Вхідні та вихідні RDD зазвичай мають однакову кількість записів.

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

Приклад mapвикористання шкали:

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- операція перетворення. Він застосовується до кожного елемента RDD і повертає результат як новий RDD. Це схоже на Map, але FlatMap дозволяє повернути 0, 1 або більше елементів з функції карти. В операції FlatMap розробник може визначити власну власну логіку бізнесу. Така ж логіка буде застосована до всіх елементів RDD.

Що означає "вирівнювання результатів"?

Функція FlatMap приймає один елемент як процес введення його відповідно до користувальницького коду (вказаного розробником) і одночасно повертає 0 або більше елементів. flatMap() перетворює RDD довжини N в інший RDD довжини M.

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

Приклад flatMapвикористання шкали:

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)
Подібно до карти, але кожен елемент введення може бути відображений до 0 або більше вихідних елементів (тому функція повинна повертати 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: "список" об'єкта не має атрибута "розділити"

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

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


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

[['привіт', 'що', 'є', 'ти', 'робиш]], [' це ',' є ',' рейк ']]

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

['привіт', 'що', 'є', 'ти', 'робиш', 'це', 'є', 'рейк']

Сподіваюся, це допомагає :)


2

map: Він повертає нове RDD, застосовуючи функцію до кожного елемента RDD. Функція в .map може повертати лише один елемент.

flatMap: Подібно до карти, він повертає нове RDD, застосовуючи функцію до кожного елемента RDD, але вихід вирівнюється.

Також функція в 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()

Вихід: повідомлення o / p вирівнюється в одному списку [1, 2, 1, 2, 3, 1, 2, 3, 4]

Джерело: https://www.linkedin.com/pulse/difference-between-map-flatmap-transformations-spark-pyspark-pandey/


0

карта:

- метод вищого порядку, який приймає функцію як вхідну інформацію і застосовує її до кожного елемента вихідної RDD.

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

flatMap:

метод вищого порядку та операція перетворення, яка приймає функцію введення.


-1

Різниця у виведенні карти та плоскої карти:

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 оголошено.so map () - це один термін

вили

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