Apache spark 在spark中,如何在不重新分配的情况下重命名dataframe的列名?

Apache spark 在spark中,如何在不重新分配的情况下重命名dataframe的列名?,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我有一个名为dataDF的数据框架,我想重命名其中的列。其他数据帧映射DF具有“原始名称”->“代码名称”映射。我想根据具有这些值的mapDF,将dataDF的列名称从“原始名称”更改为“代码名称”。我试图在循环中重新分配dataDF,但当数据量很大时,会产生低性能,并且会失去并行性。这可以用一种更好的方式来实现并行性和使用大型dataDF数据集的良好性能吗 import sparkSession.sqlContext.implicits._ var dataDF = Seq((10,

我有一个名为dataDF的数据框架,我想重命名其中的列。其他数据帧映射DF具有“原始名称”->“代码名称”映射。我想根据具有这些值的mapDF,将dataDF的列名称从“原始名称”更改为“代码名称”。我试图在循环中重新分配dataDF,但当数据量很大时,会产生低性能,并且会失去并行性。这可以用一种更好的方式来实现并行性和使用大型dataDF数据集的良好性能吗

import sparkSession.sqlContext.implicits._
    var dataDF = Seq((10, 20, 30, 40, 50),(100, 200, 300, 400, 500),(10, 222, 333, 444, 555),(1123, 2123, 3123, 4123, 5123),(1321, 2321, 3321, 4321, 5321))
      .toDF("col_1", "col_2", "col_3", "col_4", "col_5")
    dataDF.show(false)

    val mapDF = Seq(("col_1", "code_1", "true"),("col_3", "code_3", "true"),("col_4", "code_4", "true"),("col_5", "code_5", "true"))
      .toDF("original_name", "code_name", "important")
    mapDF.show(false)

    val map_of_codename = mapDF.rdd.map(x => (x.getString(0), x.getString(1))).collectAsMap()

    dataDF.columns.foreach(x => {
      if (map_of_codename.contains(x))
        dataDF = dataDF.withColumnRenamed(x, map_of_codename.get(x).get)
      else
        dataDF = dataDF.withColumnRenamed(x, "None")
    }
    )
    dataDF.show(false)

========================
dataDF
+-----+-----+-----+-----+-----+
|col_1|col_2|col_3|col_4|col_5|
+-----+-----+-----+-----+-----+
|10   |20   |30   |40   |50   |
|100  |200  |300  |400  |500  |
|10   |222  |333  |444  |555  |
|1123 |2123 |3123 |4123 |5123 |
|1321 |2321 |3321 |4321 |5321 |
+-----+-----+-----+-----+-----+

mapDF
+-------------+---------+---------+
|original_name|code_name|important|
+-------------+---------+---------+
|col_1        |code_1   |true     |
|col_3        |code_3   |true     |
|col_4        |code_4   |true     |
|col_5        |code_5   |true     |
+-------------+---------+---------+

expected DF:
+------+----+------+------+------+
|code_1|None|code_3|code_4|code_5|
+------+----+------+------+------+
|10    |20  |30    |40    |50    |
|100   |200 |300   |400   |500   |
|10    |222 |333   |444   |555   |
|1123  |2123|3123  |4123  |5123  |
|1321  |2321|3321  |4321  |5321  |
+------+----+------+------+------+


您也可以尝试使用别名,如下所示:

val alias=dataDF.columns.map(columnName=>$“${columnName}.as(map_of_codename.getOrElse(columnName,“None”))
dataDF.select(别名:*).show()
dataDF.select(别名:*).explain(真)
然后,执行计划将由单个投影节点组成,如,它可能有助于减少优化阶段:

== Analyzed Logical Plan ==
code_1: int, None: int, code_3: int, code_4: int, code_5: int
Project [col_1#16 AS code_1#77, col_2#17 AS None#78, col_3#18 AS code_3#79, col_4#19 AS code_4#80, col_5#20 AS code_5#81]
+- Project [_1#5 AS col_1#16, _2#6 AS col_2#17, _3#7 AS col_3#18, _4#8 AS col_4#19, _5#9 AS col_5#20]
   +- LocalRelation [_1#5, _2#6, _3#7, _4#8, _5#9]
也就是说,我不确定它是否能解决性能问题,因为在这两种情况下,您的
foreach
和上面的建议都可以通过
CollapseProject
规则将物理计划优化到单个节点

仅供参考,
withColumnRenamed
在引擎盖下使用了类似的方法,但它对每一列都单独使用:

def WithColumnRename(现有名称:String,新名称:String):数据帧={
val解析器=sparkSession.sessionState.analyzer.resolver
val output=queryExecution.Analysis.output
val shouldname=output.exists(f=>resolver(f.name,existingName))
如果(应重命名){
val columns=output.map{col=>
if(冲突解决程序(列名称,现有名称)){
列(列).as(新名称)
}否则{
列(列)
}
}
选择(列:*)
}否则{
toDF()
}
}

您是否有任何与观察到的性能问题相关的意见?哪些措施有助于确定操作需要时间?也许它不一定与列重命名有关?您稍后将如何处理这些重命名的列?

一种方法是先获得完整的映射列列表,而不使用spark,然后转到for循环以重命名所有列而不是call columns.foreach

下面是我的解决方案示例(很抱歉,我不是Scala方面的专家,有些数据解析可能很难看)

var dataDF=Seq((10,20,30,40,50),(100,200,300,400,500),(10,222,333,444555),(1123,2123,3123,4123,5123),(1321,2321,3321,4321,5321))
.toDF(“第1列”、“第2列”、“第3列”、“第4列”、“第5列”)
dataDF.show(false)
val mapDF=序列((“列1”,“代码列1”,“真”),(“列3”,“代码列3”,“真”),(“列4”,“代码列4”,“真”),(“列5”,“代码列5”,“真”))
.toDF(“原始名称”、“代码名称”、“重要”)
val schema_mapping=mapDF.select(“原始名称”、“代码名称”).collect()
//用于映射None列(第2列)
val none_mapping=old_schema.map(x=>if(!schema_mapping.map(x=>x(0))。包含(x))数组[字符串](x,“none”))。筛选器(!=())
对于(i println(“无法将”+schema_映射(i)(0).toString+”重命名为“+schema_映射(i)(1).toString”)
}
}
对于(i println(“无法重命名”)
}
}
dataDF.show(false)

在spark UI中,为每个列重命名它将成为一个阶段,但是当我们使用DAG可视化查看时,这些阶段应该并行执行。

而不是使用
with columnRename
使用
.toDF()
,并提供新名称。
var dataDF = Seq((10, 20, 30, 40, 50),(100, 200, 300, 400, 500),(10, 222, 333, 444, 555),(1123, 2123, 3123, 4123, 5123),(1321, 2321, 3321, 4321, 5321))
  .toDF("col_1", "col_2", "col_3", "col_4", "col_5")
dataDF.show(false)

val mapDF = Seq(("col_1", "code_1", "true"),("col_3", "code_3", "true"),("col_4", "code_4", "true"),("col_5", "code_5", "true"))
  .toDF("original_name", "code_name", "important")

val schema_mapping = mapDF.select("original_name", "code_name").collect()
//For mapping of None column (col 2)
val none_mapping = old_schema.map(x => if (!schema_mapping.map(x => x(0)).contains(x)) Array[String](x, "None")).filter(_ != ())


for(i <- 0 until schema_mapping.length){
    try {
        dataDF = dataDF.withColumnRenamed(schema_mapping(i)(0).toString, schema_mapping(i)(1).toString)
    }
    catch{
        case e : Throwable => println("cannot rename" +  schema_mapping(i)(0).toString + " to " + schema_mapping(i)(1).toString)
    }
}

for(i <- 0 until none_mapping.length){
    try {
        dataDF = dataDF.withColumnRenamed(none_mapping(i).asInstanceOf[Array[String]](0), none_mapping(i).asInstanceOf[Array[String]](1))
    }
    catch{
        case e : Throwable => println("cannot rename")
    }
}

dataDF.show(false)