Scala 如何使用具有复杂字段的自定义模式高效地读取源数据?

Scala 如何使用具有复杂字段的自定义模式高效地读取源数据?,scala,apache-spark,apache-spark-sql,distributed-computing,apache-spark-dataset,Scala,Apache Spark,Apache Spark Sql,Distributed Computing,Apache Spark Dataset,假设我有以下用逗号分隔的原始源数据,,但是有一些X字段具有非常自定义的格式。为了简单起见,我将这个示例最小化为3个字段/列。在这种情况下,自定义字段是具有特殊格式的地址(键/值用大括号括起来)。可能还有其他格式完全不同的字段 Bob,35,[street:75917;city:new york city;state:ny;zip:10000] ... Roger,75,[street:81659;city:los angeles;state:ca;zip:99999] 个案类别: case c

假设我有以下用逗号分隔的原始源数据
,但是有一些
X
字段具有非常自定义的格式。为了简单起见,我将这个示例最小化为3个字段/列。在这种情况下,自定义字段是具有特殊格式的
地址
(键/值用大括号括起来)。可能还有其他格式完全不同的字段

Bob,35,[street:75917;city:new york city;state:ny;zip:10000]
...
Roger,75,[street:81659;city:los angeles;state:ca;zip:99999]
个案类别:

case class Person(name: String, age: Int, address: Address)
case class Address(street: String, city: String, state: String, zip: Int)
将源数据(包括解析地址字段)处理到
数据集[Person]
中最有效的方法是什么

目前,我想到了两种选择:

选项1执行逐行手动转换:

val df = df.read.csv(source)
val dataset = df.map(row => 
    Person(row.getString("_c0"), row.getInt("_c1"), getAddress(row.getString("_c3")))
).as[Person]
选项2-对自定义格式的列使用
UDF
(用户定义的函数),并使用
with column
with column重命名

val udfAddress : UserDefinedFunction = udf((address: String) => toAddressObject(address))
var df = df.read.csv(source)
df = df.withColumnRenamed("_c0", "name").withColumn("name", col("name").cast(StringType))
       .withColumnRenamed("_c1", "age").withColumn("age", col("age").cast(IntegerType))
       .withColumnRenamed("_c2",  "address").withColumn("address", udfAddress(col("address")))
val dataset = df.as[Person]

一般来说,在选项1选项2之间,什么更有效?为什么?另外,如果有另一个选项在处理/解析自定义格式字段时更有效,我也愿意使用其他选项。有没有更好的方法可以手动使用StructFields组合StructType?谢谢

备选方案之一可能是-

请注意,我没有进行任何性能测试

加载测试数据
val数据=
"""
|鲍勃,35岁[街道:75917;城市:纽约市;州:纽约;邮编:10000]
|罗杰,75岁[街道:81659;城市:洛杉矶;州:加利福尼亚;邮政编码:99999]
“.stripMargin”
val stringDS=data.split(System.lineSeparator())
.map(“.split”(“\\,”).map(“.replaceAll”(“^[\t]+\t]+$”,”).mkString(“|”)
.toSeq.toDS()
val df=spark.read
.选项(“sep”和“|”)
.选项(“推断模式”、“真”)
//.选项(“标题”、“正确”)
//.选项(“空值”、“空值”)
.csv(stringDS)
df.show(假)
df.printSchema()
/**
* +-----+---+----------------------------------------------------+
*|uC0 | uC1 | uC2|
* +-----+---+----------------------------------------------------+
*|鲍勃| 35 |[街道:75917;城市:纽约市;州:纽约;邮编:10000]|
*|罗杰| 75 |[街道:81659;城市:洛杉矶;州:加利福尼亚;邮政编码:99999]|
* +-----+---+----------------------------------------------------+
*
*根
*|--_c0:string(nullable=true)
*|--_c1:整数(可为空=真)
*|--_c2:string(nullable=true)
*/
将行的Dataframe转换为Person
val person=ScalaReflection.schemaFor[person].dataType.asInstanceOf[StructType]
val toAddr=udf((地图:地图[String,String])=>地址(地图(“街道”)、地图(“城市”)、地图(“州”)、地图(“zip”).toInt))
val p=df.withColumn(“_c2”,translate($”_c2“,“[]”,“))
.withColumn(“_c2”,expr(“str_to_map(_c2,;”,“:”)”)
。带列(“\u c2”,toAddr($”\u c2”))
.toDF(person.map(u.name):u*)
.作为[人]
p、 显示(假)
p、 printSchema()
/**
* +-----+---+---------------------------------+
*|姓名|年龄|地址|
* +-----+---+---------------------------------+
*|鲍勃| 35 |[75917,纽约市,纽约州,10000]|
*|罗杰| 75 |[81659,加利福尼亚州洛杉矶,99999]|
* +-----+---+---------------------------------+
*
*根
*|--name:string(nullable=true)
*|--age:integer(nullable=true)
*|--地址:struct(nullable=true)
*| |--street:string(nullable=true)
*| |--city:string(nullable=true)
*| |--state:string(nullable=true)
*| |--zip:integer(nullable=false)
*/