如何在scala / spark中优化数据框到Map [String,List [String]]的转换?

时间:2017-12-06 15:03:38

标签: scala spark-dataframe

我有一个包含大量信号的DataFrame,我想将其转换为Map [String,List [String]]

我已运行Code,但我遇到的问题是执行它需要很长时间。对于一百个信号,它需要大约13分钟。

这是我在开头得到的inputDataFrame(例子):

+----------+-----+
|SignalName|Value|
+----------+-----+
|        S1|   V1|
|        S2|   V1|
|        S1|   V2|
|        S2|   V2|
|        S3|   V1|
|        S1|   V3|
|        S1|   V1|
+----------+-----+

然后我想过滤重复项

var reducedDF = inputDataFrame.select("SignalName","Value").dropDuplicates()

reduedDF.show的输出:

+----------+-----+
|SignalName|Value|
+----------+-----+
|        S1|   V1|
|        S1|   V2|
|        S1|   V3|
|        S2|   V1|
|        S2|   V2|
|        S3|   V1|
+----------+-----+

下一步是获取SignalNames的RDD而不重复。我使用了zipWithIndex(),因为后来我想读取RDD的每个值。 我可以使用以下代码执行此操作:

var RDDOfSignalNames = reducedDF.select("SignalName").rdd.map(r => r(0).asInstanceOf[String])  
RDDOfSignalNames = RDDOfSignalNames.distinct() 
val RDDwithIndex = RDDOfSignalNames.zipWithIndex() 
val indexKey = RDDwithIndex.map { case (k, v) => (v, k) }

现在最后一步是将每个SignalName作为Type List [String]列表获取每个可能的值并将其添加到Map:

var dataTmp: DataFrame = null
var signalname = Seq[String]("")
var map = scala.collection.mutable.Map[String, List[String]]()

for (i <- 0 to (RDDOfSignalNames.count()).toInt - 1) {

  signalname = indexKey.lookup(i)

  dataTmp = reducedDF.filter(data.col("Signalname").contains(signalname(0)))          

  map += (signalname(0) -> dataTmp.rdd.map(r => r(0).asInstanceOf[String]).collect().toList) 
  println(i+"/"+(RDDOfSignalNames.count().toInt - 1).toString())

}

在最后,地图看起来像这样:

scala.collection.mutable.Map[String,List[String]] = Map(S1 -> List(V1, V2, V3), S3 -> List(V1), S2 -> List(V1, V2))

问题是线图+ = ... 106信号需要大约13分钟!有更有效的方法吗?

1 个答案:

答案 0 :(得分:1)

首先,不建议在 scala 中使用var。您应该始终尝试使用不可变变量。所以改变以下行

var reducedDF = inputDataFrame.select("SignalName","Value").dropDuplicates()

val reducedDF = inputDataFrame.select("SignalName","Value").distinct()

是首选。

您无需经历这样的复杂情况即可获得所需的输出。您可以通过以下方式获得所需的输出

import org.apache.spark.sql.functions.collect_list
reducedDF
      .groupBy("SignalName")
      .agg(collect_list($"Value").as("Value"))
      .rdd
      .map(row => (row(0).toString -> row(1).asInstanceOf[scala.collection.mutable.WrappedArray[String]].toList))
      .collectAsMap()

其中,
reducedDF.groupBy("SignalName").agg(collect_list($"Value").as("Value"))为您提供dataframe

+----------+------------+
|SignalName|Value       |
+----------+------------+
|S3        |[V1]        |
|S2        |[V2, V1]    |
|S1        |[V1, V2, V3]|
+----------+------------+

代码的其余部分.rdd.map(row => (row(0).toString -> row(1).asInstanceOf[scala.collection.mutable.WrappedArray[String]].toList)).collectAsMap()只是将dataframe转换为您想要的输出Map

最终地图输出

Map(S1 -> List(V1, V2, V3), S3 -> List(V1), S2 -> List(V2, V1))