【问题标题】:Renaming column names of a DataFrame in Spark Scala在 Spark Scala 中重命名 DataFrame 的列名
【发布时间】:2016-06-06 04:44:45
【问题描述】:

我正在尝试在 Spark-Scala 中转换 DataFrame 的所有标题/列名。到目前为止,我想出了以下代码,它只替换了一个列名。

for( i <- 0 to origCols.length - 1) {
  df.withColumnRenamed(
    df.columns(i), 
    df.columns(i).toLowerCase
  );
}

【问题讨论】:

    标签: scala apache-spark dataframe apache-spark-sql


    【解决方案1】:

    如果结构是扁平的:

    val df = Seq((1L, "a", "foo", 3.0)).toDF
    df.printSchema
    // root
    //  |-- _1: long (nullable = false)
    //  |-- _2: string (nullable = true)
    //  |-- _3: string (nullable = true)
    //  |-- _4: double (nullable = false)
    

    您可以做的最简单的事情是使用toDF 方法:

    val newNames = Seq("id", "x1", "x2", "x3")
    val dfRenamed = df.toDF(newNames: _*)
    
    dfRenamed.printSchema
    // root
    // |-- id: long (nullable = false)
    // |-- x1: string (nullable = true)
    // |-- x2: string (nullable = true)
    // |-- x3: double (nullable = false)
    

    如果您想重命名各个列,您可以使用selectalias

    df.select($"_1".alias("x1"))
    

    可以很容易地推广到多列:

    val lookup = Map("_1" -> "foo", "_3" -> "bar")
    
    df.select(df.columns.map(c => col(c).as(lookup.getOrElse(c, c))): _*)
    

    withColumnRenamed:

    df.withColumnRenamed("_1", "x1")
    

    foldLeft 一起使用来重命名多个列:

    lookup.foldLeft(df)((acc, ca) => acc.withColumnRenamed(ca._1, ca._2))
    

    对于嵌套结构 (structs),一种可能的选择是通过选择整个结构来重命名:

    val nested = spark.read.json(sc.parallelize(Seq(
        """{"foobar": {"foo": {"bar": {"first": 1.0, "second": 2.0}}}, "id": 1}"""
    )))
    
    nested.printSchema
    // root
    //  |-- foobar: struct (nullable = true)
    //  |    |-- foo: struct (nullable = true)
    //  |    |    |-- bar: struct (nullable = true)
    //  |    |    |    |-- first: double (nullable = true)
    //  |    |    |    |-- second: double (nullable = true)
    //  |-- id: long (nullable = true)
    
    @transient val foobarRenamed = struct(
      struct(
        struct(
          $"foobar.foo.bar.first".as("x"), $"foobar.foo.bar.first".as("y")
        ).alias("point")
      ).alias("location")
    ).alias("record")
    
    nested.select(foobarRenamed, $"id").printSchema
    // root
    //  |-- record: struct (nullable = false)
    //  |    |-- location: struct (nullable = false)
    //  |    |    |-- point: struct (nullable = false)
    //  |    |    |    |-- x: double (nullable = true)
    //  |    |    |    |-- y: double (nullable = true)
    //  |-- id: long (nullable = true)
    

    请注意,它可能会影响nullability 元数据。另一种可能性是通过强制转换重命名:

    nested.select($"foobar".cast(
      "struct<location:struct<point:struct<x:double,y:double>>>"
    ).alias("record")).printSchema
    
    // root
    //  |-- record: struct (nullable = true)
    //  |    |-- location: struct (nullable = true)
    //  |    |    |-- point: struct (nullable = true)
    //  |    |    |    |-- x: double (nullable = true)
    //  |    |    |    |-- y: double (nullable = true)
    

    或:

    import org.apache.spark.sql.types._
    
    nested.select($"foobar".cast(
      StructType(Seq(
        StructField("location", StructType(Seq(
          StructField("point", StructType(Seq(
            StructField("x", DoubleType), StructField("y", DoubleType)))))))))
    ).alias("record")).printSchema
    
    // root
    //  |-- record: struct (nullable = true)
    //  |    |-- location: struct (nullable = true)
    //  |    |    |-- point: struct (nullable = true)
    //  |    |    |    |-- x: double (nullable = true)
    //  |    |    |    |-- y: double (nullable = true)
    

    【讨论】:

    • 嗨@zero323 当使用 withColumnRenamed 我得到 AnalysisException can't resolve 'CC8. 1' 给定输入列...即使 CC8.1 在 DataFrame 中可用,它也会失败,请指导。
    • @u449355 我不清楚这是嵌套列还是包含点的列。在后一种情况下,反引号应该可以工作(至少在某些基本情况下)。
    • : _*)df.select(df.columns.map(c =&gt; col(c).as(lookup.getOrElse(c, c))): _*) 中是什么意思
    • 回答 Anton Kim 的问题:: _* 是 scala 所谓的“splat”运算符。它基本上将一个类似数组的东西分解为一个不包含的列表,当您想将数组传递给一个接受任意数量的 args 但没有采用 List[] 的版本的函数时,这很有用。如果你对 Perl 很熟悉,那就是 some_function(@my_array) # "splatted"some_function(\@my_array) # not splatted ... in perl the backslash "\" operator returns a reference to a thing 之间的区别。
    • 这句话对我来说真的很模糊df.select(df.columns.map(c =&gt; col(c).as(lookup.getOrElse(c, c))): _*).. 你能分解一下吗?尤其是lookup.getOrElse(c,c) 部分。
    【解决方案2】:

    对于那些对 PySpark 版本感兴趣的人(实际上它在 Scala 中是相同的 - 请参阅下面的评论):

        merchants_df_renamed = merchants_df.toDF(
            'merchant_id', 'category', 'subcategory', 'merchant')
    
        merchants_df_renamed.printSchema()
    

    结果:


    |-- 商家 ID:整数(可为空 = true)
    |-- 类别:字符串(可为空 = true)
    |-- 子类别:字符串(可为空 = true)
    |-- 商家:字符串 (nullable = true)

    【讨论】:

    • 使用toDF()重命名DataFrame中的列必须小心。这种方法比其他方法工作得慢得多。我有 DataFrame 包含 100M 记录和简单的计数查询需要约 3 秒,而使用 toDF() 方法的相同查询需要约 16 秒。但是当使用select col AS col_new 方法重命名时,我又得到了~3s。速度提高 5 倍以上! Spark 2.3.2.3
    【解决方案3】:
    def aliasAllColumns(t: DataFrame, p: String = "", s: String = ""): DataFrame =
    {
      t.select( t.columns.map { c => t.col(c).as( p + c + s) } : _* )
    }
    

    如果不明显,这会为每个当前列名添加前缀和后缀。当您有两个具有相同名称的一个或多个列的表,并且您希望连接它们但仍然能够消除结果表中的列的歧义时,这可能很有用。如果在“普通”SQL 中有类似的方法来执行此操作,那肯定会很好。

    【讨论】:

    • 肯定喜欢,漂亮又优雅
    【解决方案4】:

    假设数据框 df 有 3 列 id1、name1、price1 并且您希望将它们重命名为 id2、name2、price2

    val list = List("id2", "name2", "price2")
    import spark.implicits._
    val df2 = df.toDF(list:_*)
    df2.columns.foreach(println)
    

    我发现这种方法在很多情况下都很有用。

    【讨论】:

      【解决方案5】:

      拖表连接不重命名连接键

      // method 1: create a new DF
      day1 = day1.toDF(day1.columns.map(x => if (x.equals(key)) x else s"${x}_d1"): _*)
      
      // method 2: use withColumnRenamed
      for ((x, y) <- day1.columns.filter(!_.equals(key)).map(x => (x, s"${x}_d1"))) {
          day1 = day1.withColumnRenamed(x, y)
      }
      

      有效!

      【讨论】:

        【解决方案6】:
        Sometime we have the column name is below format in SQLServer or MySQL table
        
        Ex  : Account Number,customer number
        
        But Hive tables do not support column name containing spaces, so please use below solution to rename your old column names.
        
        Solution:
        
        val renamedColumns = df.columns.map(c => df(c).as(c.replaceAll(" ", "_").toLowerCase()))
        df = df.select(renamedColumns: _*)
        

        【讨论】:

          猜你喜欢
          • 2019-06-13
          • 2021-12-18
          • 2016-01-06
          • 2018-02-12
          • 2019-08-17
          • 2021-04-24
          • 1970-01-01
          • 1970-01-01
          • 2019-08-16
          相关资源
          最近更新 更多