【问题标题】:Update DataFrame col names from a list avoiding using var从列表中更新 DataFrame 列名,避免使用 var
【发布时间】:2020-07-22 07:52:43
【问题描述】:

我有一个定义列的列表:

case class ExcelColumn(colName: String, colType: String, colCode: String)

val cols = List(
  ExcelColumn("Products Selled", "text", "products_selled"),
  ExcelColumn("Total Value", "int", "total_value"),
)

还有一个文件(带有标题列Products SelledTotal Value 的csv)作为数据帧读取。

  val df = spark.read
      .option("header", "true")
      .option("inferSchema", "true")
      .csv(filePath)

  // csv file have header as colNames
  var finalDf = df
      .withColumn("row_id", monotonically_increasing_id)
      .select(cols
         .map(_.name.trim)
         .map(col): _*)

  // convert df col names as colCodes (for kudu table columns)
  cols.foreach(col => finalDf = finalDf.withColumnRenamed(col.name.trim, col.colCode.trim))

在最后一行,我将数据框列名从Products Selled 更改为products_selled。因此,finalDf 是 var

我想知道是否是将 finalDf 声明为 val 而不是 var 的解决方案。

我尝试了类似下面的代码,但 withColumnRenamed 返回一个新的 DataFrame,但我无法在 cols.foreach 之外执行此操作

cols.foreach(col => finalDf.withColumnRenamed(col.name.trim, col.colCode.trim))

【问题讨论】:

    标签: scala dataframe apache-spark


    【解决方案1】:

    使用select可以重命名列。

    重命名select 中的列比foldLeft 快​​,检查post 进行比较。

    试试下面的代码。

    case class ExcelColumn(colName: String, colType: String, colCode: String)
    
    val cols = List(
      ExcelColumn("Products Selled", "string", "products_selled"),
      ExcelColumn("Total Value", "int", "total_value"),
    )
    
    val colExpr = cols.map(c => trim(col(c.colName)).as(c.colCode.trim))
    

    如果您在ExcelColumn case 类中存储有效的列数据类型,您可以使用如下所示的列数据类型。

    val colExpr = cols.map(c => trim(col(c.colName).cast(c.colType)).as(c.colCode.trim))
    
    
    finalDf.select(colExpr:_*)
    

    【讨论】:

    • 这比在循环中多次调用withColumnRenamed 更好,因为它生成的解析计划不太复杂,Catalyst 更容易优化。
    • 在@powers 答案之后,我将此答案作为解决方案进行了检查,因为看起来性能更高。但是,我发现了一个大问题。将 select 与此表达式一起使用,新的数据框将具有 StringType 的所有列。我需要保留前一个 df 的架构。
    • 谢谢。需要将 kudu db 类型映射到 spark.sql StructField 类型。所以,如果 colCode 是“int”,这将被映射到 IntegerType (spark.sql.types)
    【解决方案2】:

    更好的方法是使用foldLeftwithColumnRenamed

    case class ExcelColumn(colName: String, colType: String, colCode: String)
    
    val cols = List(
      ExcelColumn("Products Selled", "text", "products_selled"),
      ExcelColumn("Total Value", "int", "total_value"),
    )
    
    val resultDF = cols.foldLeft(df){(acc, name ) =>
      acc.withColumnRenamed(name.colName.trim, name.colCode.trim)
    } 
    

    原始架构:

    root
     |-- Products Selled: integer (nullable = false)
     |-- Total Value: string (nullable = true)
     |-- value: integer (nullable = false)
    

    新架构:

    root
     |-- products_selled: integer (nullable = false)
     |-- total_value: string (nullable = true)
     |-- value: integer (nullable = false)
    

    【讨论】:

      猜你喜欢
      • 2018-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-28
      相关资源
      最近更新 更多