有没有一种方法可以使用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来转换所有值,而不必手动转换每个列/值。代码。
我将非常感谢您能提供的任何帮助,非常感谢!
答案 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天的时间寻找这种简单/通用的解决方案。