Spark-Генерация Случайных Чисел
Я написал метод, который должен учитывать случайное число для моделирования распределения Бернулли. Я использую random.nextDouble
для генерации числа от 0 до 1, а затем принятия решения на основе этого значения с учетом моего параметра вероятности.
моя проблема в том, что Spark генерирует одни и те же случайные числа в каждой итерации моей функции отображения цикла for. Я использую DataFrame
API-интерфейс. Мой код следует этому формату:
val myClass = new MyClass()
val M = 3
val myAppSeed = 91234
val rand = new scala.util.Random(myAppSeed)
for (m <- 1 to M) {
val newDF = sqlContext.createDataFrame(myDF
.map{row => RowFactory
.create(row.getString(0),
myClass.myMethod(row.getString(2), rand.nextDouble())
}, myDF.schema)
}
здесь класс:
class myClass extends Serializable {
val q = qProb
def myMethod(s: String, rand: Double) = {
if (rand <= q) // do something
else // do something else
}
}
мне нужно новое случайное число каждый раз myMethod
называется. Я также попытался создать число внутри моего метода с помощью java.util.Random
(scala.util.Random
v10 не распространяется Serializable
), как показано ниже, но я все еще получаю те же цифры в пределах каждого цикла
val r = new java.util.Random(s.hashCode.toLong)
val rand = r.nextDouble()
я провел некоторые исследования, и, похоже, это связано с детерминированной природой Спаркс.
3 ответов
причина, по которой повторяется одна и та же последовательность, заключается в том, что генератор случайных чисел создается и инициализируется семенем до секционирования данных. Затем каждый раздел начинается с одного и того же значения. Возможно, это не самый эффективный способ сделать это, но должно работать следующее:
val myClass = new MyClass()
val M = 3
for (m <- 1 to M) {
val newDF = sqlContext.createDataFrame(myDF
.map{
val rand = scala.util.Random
row => RowFactory
.create(row.getString(0),
myClass.myMethod(row.getString(2), rand.nextDouble())
}, myDF.schema)
}
просто используйте функцию SQL rand
:
import org.apache.spark.sql.functions._
//df: org.apache.spark.sql.DataFrame = [key: int]
df.select($"key", rand() as "rand").show
+---+-------------------+
|key| rand|
+---+-------------------+
| 1| 0.8635073400704648|
| 2| 0.6870153659986652|
| 3|0.18998048357873532|
+---+-------------------+
df.select($"key", rand() as "rand").show
+---+------------------+
|key| rand|
+---+------------------+
| 1|0.3422484248879837|
| 2|0.2301384925817671|
| 3|0.6959421970071372|
+---+------------------+
По данным этот пост, лучшее решение-не ставить new scala.util.Random
внутри карты, ни снаружи (т. е. в коде драйвера), но в промежуточном mapPartitionsWithIndex
:
import scala.util.Random
val myAppSeed = 91234
val newRDD = myRDD.mapPartitionsWithIndex { (indx, iter) =>
val rand = new scala.util.Random(indx+myAppSeed)
iter.map(x => (x, Array.fill(10)(rand.nextDouble)))
}