使用定义的StructType

时间:2018-07-27 16:07:14

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

有没有一种方法可以使用StructType转换数据帧的所有值?

让我用一个例子来说明我的问题:

假设我们从文件读取后获得了一个数据框(我提供了生成该数据框的代码,但是在我的真实世界项目中,我是从文件读取后获得了该数据框):

    import org.apache.spark.sql.{Row, SparkSession}
    import org.apache.spark.sql.types._
    import org.apache.spark.sql.functions._
    import spark.implicits._
    val rows1 = Seq(
      Row("1", Row("a", "b"), "8.00", Row("1","2")),
      Row("2", Row("c", "d"), "9.00", Row("3","4"))
    )

    val rows1Rdd = spark.sparkContext.parallelize(rows1, 4)

    val schema1 = StructType(
      Seq(
        StructField("id", StringType, true),
        StructField("s1", StructType(
          Seq(
            StructField("x", StringType, true),
            StructField("y", StringType, true)
          )
        ), true),
        StructField("d", StringType, true),
        StructField("s2", StructType(
          Seq(
            StructField("u", StringType, true),
            StructField("v", StringType, true)
          )
        ), true)
      )
    )

    val df1 = spark.createDataFrame(rows1Rdd, schema1)

    println("Schema with nested struct")
    df1.printSchema()

    root
    |-- id: string (nullable = true)
    |-- s1: struct (nullable = true)
    |    |-- x: string (nullable = true)
    |    |-- y: string (nullable = true)
    |-- d: string (nullable = true)
    |-- s2: struct (nullable = true)
    |    |-- u: string (nullable = true)
    |    |-- v: string (nullable = true)

现在让我们说,我的客户端为我提供了他想要的数据的架构(与读取的数据帧的架构等效,但是具有不同的数据类型(包含StringTypes,IntegerTypes ...)):

    val wantedSchema = StructType(
      Seq(
        StructField("id", IntegerType, true),
        StructField("s1", StructType(
          Seq(
            StructField("x", StringType, true),
            StructField("y", StringType, true)
          )
        ), true),
        StructField("d", DoubleType, true),
        StructField("s2", StructType(
          Seq(
            StructField("u", IntegerType, true),
            StructField("v", IntegerType, true)
          )
        ), true)
      )
    )

使用提供的StructType转换数据框的值的最佳方法是什么?

如果有一种方法可以应用到数据帧上,并且通过自身强制转换所有值来应用新的StructType,那将是很棒的选择。

PS:这是一个小的数据框,仅作为示例,在我的项目中,该数据框包含更多的行。 如果这是一个只有几列的小型Dataframe,我可以很容易地进行转换,但就我而言,我正在寻找一种智能的解决方案,可以通过应用StructType来转换所有值,而不必手动转换每个列/值。代码。

我将非常感谢您能提供的任何帮助,非常感谢!

2 个答案:

答案 0 :(得分:0)

没有自动方法来执行转换。您可以在Spark SQL中表达转换逻辑,以便一次转换所有内容-但是,如果您有很多字段,那么生成的SQL可能会变得很大。但是至少您可以将所有转换都放在一个地方。

示例:

   df1.selectExpr("CAST (id AS INTEGER) as id",
    "STRUCT (s1.x, s1.y) AS s1",
    "CAST (d AS DECIMAL) as d",
    "STRUCT (CAST (s2.u AS INTEGER), CAST (s2.v AS INTEGER)) as s2").show()

要注意的一件事是,每当转换失败时(例如,d不是数字),您都会得到一个NULL。一种选择是在转换之前运行一些验证,然后过滤掉df1记录以仅转换有效记录。

答案 1 :(得分:0)

经过大量研究,这是一种通用的解决方案,可以按照模式强制转换数据框:

val castedDf = df1.selectExpr(wantedSchema.map(
  field => s"CAST ( ${field.name} As ${field.dataType.sql}) ${field.name}"
): _*)

这是强制转换的数据框的架构:

castedDf.printSchema
root
|-- id: integer (nullable = true)
|-- s1: struct (nullable = true)
|    |-- x: string (nullable = true)
|    |-- y: string (nullable = true)
|-- d: double (nullable = true)
|-- s2: struct (nullable = true)
|    |-- u: integer (nullable = true)
|    |-- v: integer (nullable = true)

我希望它能对某人有所帮助,我花了5天的时间寻找这种简单/通用的解决方案。