Эффективное манипулирование подмножествами ключей RDD в искрых

У меня есть RDD пар (ключ, значение) формы

RDD[(
  scala.collection.immutable.Vector[(Byte, Byte)],   
  scala.collection.immutable.Vector[Int]
)]

где key - Vector[(Byte, Byte)], а value - Vector[Int].

Например, содержимое RDD может быть таким, как показано ниже.

(Vector((3,3), (5,5)), Vector(1, 2)),
(Vector((1,1), (2,2), (3,3),(4,4), (5,5)), Vector(1, 3, 4, 2)), 
(Vector((1,1), (2,3)), Vector(1, 4, 2)), 
(Vector((1,1), (2,2), (5,5)), Vector(3, 5)),

Я хотел бы сделать манипуляцию на этом RDD, чтобы в результирующем RDD для каждой пары (ключ, значение) выполнялось следующее условие.

Когда ключ "k1" этого RDD является подмножеством ключа "k2" этого RDD, значения k1 должны обновляться, чтобы также содержать значения k2, в то время как значения k2 останутся неизменными.

В приведенном выше примере RDD станет,

(Vector((3,3), (5,5)), Vector(1, 2, 3, 4)), 
(Vector((1,1), (2,2), (3,3), (4,4), (5,5)), Vector(1, 3, 4, 2))
(Vector((1,1), (2,3)), Vector(1, 4, 2))
(Vector((1,1), (2,2), (5,5)), Vector(1, 2, 3, 4, 5))

Я задал аналогичный вопрос здесь. Предоставленное решение приведено ниже (слегка изменено в соответствии с моей проблемой). Это работает, но очень неэффективно для больших наборов данных.

val resultPre = rddIn
  .flatMap { case (colMapkeys, rowIds) => 
    colMapkeys.subsets.tail.map(_ -> rowIds)
  }
  .reduceByKey(_ ++ _)
  .join(rddIn map identity[(Seq[(Byte, Byte)], Vector[Int])])
  .map{ case (key, (v, _)) => (key, v) }


implicit class SubSetsOps[T](val elems: Seq[T]) extends AnyVal {
  def subsets: Vector[Seq[T]] = elems match {
    case Seq() => Vector(elems)
    case elem +: rest => {
      val recur = rest.subsets
      recur ++ recur.map(elem +: _)
    }
  }
}

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

Как я могу справиться с этим эффективно?

Ответ 1

Я думаю, что ваша проблема принципиально сложна. У вас есть 2 способа сделать это:

  • Сгенерировать все подмножества ключей, объединить списки значений и собрать финальный список для любого данного подмножества, а затем присоединиться к существующим подмножествам. (это то, что вы делаете).

  • Сравните каждую запись с каждой записью, посмотрите, является ли один ключ подмножеством другого, а затем слияние всех подмножеств, сгенерированных таким образом с помощью ключа. Это не приводит к промежуточным перестановкам поддельных ключей.

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

Другие оптимизации, которые вы можете попробовать, - сделать данные немного проще в обращении. Например, вы можете смело сопоставить свои внутренние координаты с целыми числами (они просто байты). Скажем (5,5) - 5 * 1000 + 5 = 5005. Поскольку сравнение целых чисел проще и быстрее, чем сравнение кортежей.

В зависимости от того, насколько вы понимаете домен ключей. Если это пространство достаточно мало, вы можете попробовать представить свои ключи в виде растровых изображений или некоторых таких. Эти изменения не будут существенно изменять количество ключей, которые у вас есть, но могут значительно облегчить сравнение и другие операции.