【问题标题】:Remove Null from Array Columns in Dataframe in Scala with Spark (1.6)使用 Spark (1.6) 从 Scala 中 Dataframe 中的数组列中删除 Null
【发布时间】:2018-10-17 07:38:22
【问题描述】:

我有一个数据框,其中包含一个键列和一个包含结构数组的列。 Schema 如下所示。

root
 |-- id: string (nullable = true)
 |-- desc: array (nullable = false)
 |    |-- element: struct (containsNull = true)
 |    |    |-- name: string (nullable = true)
 |    |    |-- age: long (nullable = false)

数组“desc”可以有任意数量的空值。我想使用 spark 1.6 创建一个最终的数据框,其中的数组没有任何空值:

一个例子是:

Key  .   Value
1010 .   [[George,21],null,[MARIE,13],null]
1023 .   [null,[Watson,11],[John,35],null,[Kyle,33]]

我希望最终的数据框为:

Key  .   Value
1010 .   [[George,21],[MARIE,13]]
1023 .   [[Watson,11],[John,35],[Kyle,33]]

我尝试使用 UDF 和案例类来执行此操作,但得到了

java.lang.ClassCastException: org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema cannot be cast to....

非常感谢任何帮助,如果需要,我更愿意在不转换为 RDD 的情况下这样做。另外我是 spark 和 scala 的新手,所以提前谢谢!!!

【问题讨论】:

    标签: scala apache-spark spark-dataframe


    【解决方案1】:

    鉴于原始数据框具有以下架构

    root
     |-- id: string (nullable = true)
     |-- desc: array (nullable = true)
     |    |-- element: struct (containsNull = true)
     |    |    |-- name: string (nullable = true)
     |    |    |-- age: long (nullable = false)
    

    定义一个udf 函数从数组中删除空值应该可以工作

    import org.apache.spark.sql.functions._
    def removeNull = udf((array: Seq[Row])=> array.filterNot(_ == null).map(x => element(x.getAs[String]("name"), x.getAs[Long]("age"))))
    
    df.withColumn("desc", removeNull(col("desc")))
    

    其中elementcase class

    case class element(name: String, age: Long)
    

    你应该得到

    +----+-----------------------------------+
    |id  |desc                               |
    +----+-----------------------------------+
    |1010|[[George,21], [MARIE,13]]          |
    |1010|[[Watson,11], [John,35], [Kyle,33]]|
    +----+-----------------------------------+
    

    【讨论】:

    • 直到 array.filternot(_ == null ) 我得到了零件,但是为什么要使用地图呢?我的意思是你能澄清一下地图在做什么吗?
    • map 只是创建要返回的结构
    【解决方案2】:

    这是另一个版本:

    case class Person(name: String, age: Int)
    
    root
     |-- id: string (nullable = true)
     |-- desc: array (nullable = true)
     |    |-- element: struct (containsNull = true)
     |    |    |-- name: string (nullable = true)
     |    |    |-- age: integer (nullable = false)
    
    +----+-----------------------------------------------+
    |id  |desc                                           |
    +----+-----------------------------------------------+
    |1010|[[George,21], null, [MARIE,13], null]          |
    |1023|[[Watson,11], null, [John,35], null, [Kyle,33]]|
    +----+-----------------------------------------------+
    
    
    val filterOutNull = udf((xs: Seq[Row]) => {
      xs.flatMap {
        case null => Nil
        // convert the Row back to your specific struct:
        case Row(s: String,i: Int) => List(Person(s, i))
      }
    })
    
    val result = df.withColumn("filteredListDesc", filterOutNull($"desc"))
    
    +----+-----------------------------------------------+-----------------------------------+
    |id  |desc                                           |filteredListDesc                   |
    +----+-----------------------------------------------+-----------------------------------+
    |1010|[[George,21], null, [MARIE,13], null]          |[[George,21], [MARIE,13]]          |
    |1023|[[Watson,11], null, [John,35], null, [Kyle,33]]|[[Watson,11], [John,35], [Kyle,33]]|
    +----+-----------------------------------------------+-----------------------------------+
    

    【讨论】:

    • 您好。我有一个问题.....为什么在 UDF 中不使用 Seq[Person] 而是使用 Seq[Row]。当案例类与架构中定义的结构基本相同时,为什么使用 Seq[Person] 会出现转换错误?
    • 简而言之,spark 很容易使用行/结构/数组等内部类型,而不是案例类等 JVM 对象。
    猜你喜欢
    • 1970-01-01
    • 2021-12-07
    • 2019-07-02
    • 2018-09-27
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    • 2020-12-06
    相关资源
    最近更新 更多