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