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

如何优化数据帧到scala/spark中Map[String,List[String]]的转换?,scala,spark-dataframe,Scala,Spark Dataframe,我有一个包含很多信号的数据帧,我想把它转换成Map[String,List[String]] 我有正在运行的代码,但我有一个问题,执行它需要很长时间。对于仅有的几百个信号,它需要大约13分钟 这是我在开始时得到的inputDataFrame(示例): 然后我想过滤重复的内容 var reducedDF = inputDataFrame.select("SignalName","Value").dropDuplicates() reduedDF.show的输出: +----------+----

我有一个包含很多信号的数据帧,我想把它转换成Map[String,List[String]]

我有正在运行的代码,但我有一个问题,执行它需要很长时间。对于仅有的几百个信号,它需要大约13分钟

这是我在开始时得到的inputDataFrame(示例):

然后我想过滤重复的内容

var reducedDF = inputDataFrame.select("SignalName","Value").dropDuplicates()
reduedDF.show的输出:

+----------+-----+
|SignalName|Value|
+----------+-----+
|        S1|   V1|
|        S1|   V2|
|        S1|   V3|
|        S2|   V1|
|        S2|   V2|
|        S3|   V1|
+----------+-----+
下一步是在不重复的情况下获取SignalName的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的每个可能值作为类型列表[String]的列表,并将其添加到映射:

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())

}

问题是线条贴图+=。。。对于106个信号,大约需要13分钟!有没有更有效的方法

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

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

是首选

您不需要经历如此复杂的过程来获得所需的输出。您可以通过以下操作获得所需的输出

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”)
将数据帧作为

+----------+------------+
|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))

现在我得到了错误消息:线程“main”org.apache.spark.sql.AnalysisException中的异常:未定义函数collect_list;您是否导入了
导入org.apache.spark.sql.functions.collect_list
?是的,我导入了,但我使用了spark 1.6.2!我认为这是个问题。您是否导入了sqlContext.implicits.\u?如果没有,你必须这样做。如果是,请检查是,我还导入了
sqlContext.implicits.\u
但仍然有错误!我以前看过这个页面,但它对我帮助不大,因为我的Spark版本<2.0
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()
+----------+------------+
|SignalName|Value       |
+----------+------------+
|S3        |[V1]        |
|S2        |[V2, V1]    |
|S1        |[V1, V2, V3]|
+----------+------------+
Map(S1 -> List(V1, V2, V3), S3 -> List(V1), S2 -> List(V2, V1))