Как уменьшить по ключу в «Scala» [Not In Spark] - PullRequest
0 голосов
/ 09 февраля 2019

Я пытаюсь уменьшитьByKeys в Scala, есть ли способ уменьшить значения, основанные на ключах в Scala.[я знаю, что мы можем сделать это с помощью метода lowerByKey в spark, но как мы можем сделать то же самое в Scala?]

Входные данные:

val File = Source.fromFile("C:/Users/svk12/git/data/retail_db/order_items/part-00000")
                 .getLines()
                 .toList

 val map = File.map(x => x.split(","))
               .map(x => (x(1),x(4)))

  map.take(10).foreach(println)

После вышеуказанного шага я получаю результат как:

(2,250.0)
(2,129.99)
(4,49.98)
(4,299.95)
(4,150.0)
(4,199.92)
(5,299.98)
(5,299.95)

Ожидаемый результат:

(2,379.99)
(5,499.93)
.......

Ответы [ 5 ]

0 голосов
/ 10 февраля 2019

Вот еще одно решение с использованием foldLeft:

val File : List[String] = ???

File.map(x => x.split(","))
  .map(x => (x(1),x(4).toInt))
  .foldLeft(Map.empty[String,Int]){case (state, (key,value)) => state.updated(key,state.get(key).getOrElse(0)+value)}
  .toSeq
  .sortBy(_._1)
  .take(10)
  .foreach(println)
0 голосов
/ 09 февраля 2019

Начиная с Scala 2.13, вы можете использовать метод groupMapReduce, который (как следует из названия) эквивалентен groupBy с последующим mapValues и шагом reduce:

io.Source.fromFile("file.txt")
  .getLines.to(LazyList)
  .map(_.split(','))
  .groupMapReduce(_(1))(_(4).toDouble)(_ + _)

Этап groupMapReduce:

  • group s разбивает массивы по их 2-му элементу (_(1)) (групповая часть group MapReduce)

  • map s для каждого вхождения массива в каждой группе в его 4-й элемент и приведите его к Double (_(4).toDouble) (часть карты группы )Карта уменьшить)

  • reduce s значений в каждой группе (_ + _) путем их суммирования (уменьшить часть groupMap уменьшить ).

Это однопроходная версия того, что можно перевести как:

seq.groupBy(_(1)).mapValues(_.map(_(4).toDouble).reduce(_ + _))

Также обратите внимание на приведение от Iterator до LazyList чтобы использовать коллекцию, которая обеспечивает groupMapReduce (мы не используем Stream, так как запуск Scala 2.13, LazyList является рекомендуемой заменой Stream s).

0 голосов
/ 09 февраля 2019

Сначала сгруппируйте кортеж, используя ключ, сначала элемент, а затем уменьшите.Следующий код будет работать -

val reducedList = map.groupBy(_._1).map(l => (l._1, l._2.map(_._2).reduce(_+_)))
print(reducedList)
0 голосов
/ 09 февраля 2019

Похоже, вы хотите сумму некоторых значений из файла.Одна проблема состоит в том, что файлы являются строками, поэтому вам необходимо привести String к числовому формату, прежде чем его можно будет суммировать.

Это шаги, которые вы можете использовать.

io.Source.fromFile("so.txt") //open file
  .getLines()                //read line-by-line
  .map(_.split(","))         //each line is Array[String]
  .toSeq                     //to something that can groupBy()
  .groupBy(_(1))             //now is Map[String,Array[String]]
  .mapValues(_.map(_(4).toInt).sum) //now is Map[String,Int]
  .toSeq                     //un-Map it to (String,Int) tuples
  .sorted                    //presentation order
  .take(10)                  //sample
  .foreach(println)          //report

Это, конечно, сгенерирует, если какие-либо данные файла не в нужном формате.

0 голосов
/ 09 февраля 2019

Нет ничего встроенного, но вы можете написать это так:

def reduceByKey[A, B](items: Traversable[(A, B)])(f: (B, B) => B): Map[A, B] = {
  var result = Map.empty[A, B]
  items.foreach {
    case (a, b) =>
      result += (a -> result.get(a).map(b1 => f(b1, b)).getOrElse(b))
  }
  result
}

Есть место для оптимизации (например, используйте изменяемые карты), но общая идея остается прежней.

Другой подход, более декларативный, но менее эффективный (создает несколько промежуточных коллекций; может быть переписан, но с потерей ясности:

def reduceByKey[A, B](items: Traversable[(A, B)])(f: (B, B) => B): Map[A, B] = {
  items
    .groupBy { case (a, _) => a }
    .mapValues(_.map { case (_, b) => b }.reduce(f))
    // mapValues returns a view, view.force changes it back to a realized map
    .view.force
}
...