我正在使用嵌套循环和外部jar比较scala / spark中的2个数据帧。
for (nrow <- dfm.rdd.collect) {
var mid = nrow.mkString(",").split(",")(0)
var mfname = nrow.mkString(",").split(",")(1)
var mlname = nrow.mkString(",").split(",")(2)
var mlssn = nrow.mkString(",").split(",")(3)
for (drow <- dfn.rdd.collect) {
var nid = drow.mkString(",").split(",")(0)
var nfname = drow.mkString(",").split(",")(1)
var nlname = drow.mkString(",").split(",")(2)
var nlssn = drow.mkString(",").split(",")(3)
val fNameArray = Array(mfname,nfname)
val lNameArray = Array (mlname,nlname)
val ssnArray = Array (mlssn,nlssn)
val fnamescore = Main.resultSet(fNameArray)
val lnamescore = Main.resultSet(lNameArray)
val ssnscore = Main.resultSet(ssnArray)
val overallscore = (fnamescore +lnamescore +ssnscore) /3
if(overallscore >= .95) {
println("MeditechID:".concat(mid)
.concat(" MeditechFname:").concat(mfname)
.concat(" MeditechLname:").concat(mlname)
.concat(" MeditechSSN:").concat(mlssn)
.concat(" NextGenID:").concat(nid)
.concat(" NextGenFname:").concat(nfname)
.concat(" NextGenLname:").concat(nlname)
.concat(" NextGenSSN:").concat(nlssn)
.concat(" FnameScore:").concat(fnamescore.toString)
.concat(" LNameScore:").concat(lnamescore.toString)
.concat(" SSNScore:").concat(ssnscore.toString)
.concat(" OverallScore:").concat(overallscore.toString))
}
}
}
我希望做的是向外部循环添加一些并行性,以便我可以创建5个线程池并从外部循环的集合中提取5条记录,并将它们与内部循环的集合进行比较,而不是将它们进行比较依次执行此操作。因此,结果是我可以指定线程数,在任何给定时间针对内循环中的集合,从外循环的收集处理中获取5条记录。我将如何去做?
答案 0 :(得分:3)
让我们从分析您的工作开始。您将dfm
的数据收集到驱动程序。然后,对于每个元素,您都从dfn
收集数据,对其进行转换并为每对元素计算得分。
这在很多方面都有问题。首先,即使不考虑并行计算,对dfn
元素的转换也要进行与元素dfm
相同的次数。同样,您为dfn
的每一行收集dfm
的数据。在驱动程序和执行程序之间有很多网络通信。
如果要使用spark并行化计算,则需要使用API(RDD,SQL或数据集)。您似乎想使用RDD执行笛卡尔积(这是O(N * M),所以要小心,可能要花一些时间。)
让我们先转换笛卡尔乘积之前或之后的数据,以避免对每个元素执行多次以上的操作。另外,为清楚起见,让我们定义一个包含您的数据的案例类和一个将数据框转换为该案例类的RDD的函数。
case class X(id : String, fname : String, lname : String, lssn : String)
def toRDDofX(df : DataFrame) = {
df.rdd.map(row => {
// using pattern matching to convert the array to the case class X
row.mkString(",").split(",") match {
case Array(a, b, c, d) => X(a, b, c, d)
}
})
}
然后,我使用filter
仅保留分数大于.95
的元组,但是您可以使用map
,foreach
...取决于您的意图要做。
val rddn = toRDDofX(dfn)
val rddm = toRDDofX(dfm)
rddn.cartesian(rddm).filter{ case (xn, xm) => {
val fNameArray = Array(xm.fname,xn.fname)
val lNameArray = Array(xm.lname,xn.lname)
val ssnArray = Array(xm.lssn,xn.lssn)
val fnamescore = Main.resultSet(fNameArray)
val lnamescore = Main.resultSet(lNameArray)
val ssnscore = Main.resultSet(ssnArray)
val overallscore = (fnamescore +lnamescore +ssnscore) /3
// and then, let's say we filter by score
overallscore > .95
}}
答案 1 :(得分:1)
这不是迭代spark数据帧的正确方法。主要关注的是dfm.rdd.collect
。如果数据帧任意大,则最终将导致异常。这是由于collect
函数实际上将所有数据带入了主节点。
另一种方法是使用rdd的foreach或map构造。
dfm.rdd.foreach(x => {
// your logic
}
现在,您尝试在此处迭代第二个数据帧。恐怕不可能。优雅的方法是加入dfm
和dfn
并遍历结果数据集以计算函数。