我编写了一种必须考虑随机数来模拟伯努利分布的方法。我正在使用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
)在我的方法中生成数字,如下所示,但我仍然在每个for循环中得到相同的数字< / p>
val r = new java.util.Random(s.hashCode.toLong)
val rand = r.nextDouble()
我做过一些研究,似乎这与Sparks的确定性有关。
答案 0 :(得分:8)
只需使用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|
+---+------------------+
答案 1 :(得分: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)
}
答案 2 :(得分:3)
根据this post,最好的解决方案不是将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)))
}
答案 3 :(得分:0)
使用Spark Dataset API,也许可以在累加器中使用:
df.withColumn("_n", substring(rand(),3,4).cast("bigint"))