spark scala içinde struct kullanarak şema yarat
Ölçekleme konusunda yeniyim ve yeni bir özel şemaya dayalı dosyaları okumak için öğe dizisinden özel şema yapmaya çalışıyorum.
Dizileri json dosyasından okuyorum ve explode yöntemini kullandım ve sütun dizisindeki her eleman için bir veri çerçevesi oluşturdum.
val otherPeople = sqlContext.read.option("multiline", "true").json(otherPeopleDataset)
val column_values = otherPeople.withColumn("columns", explode($"columns")).select("columns.*")
column_values.printSchema()
elde edilen çıktı:
column_values: org.apache.spark.sql.DataFrame = [column_id: string, data_sensitivty: string ... 3 more fields]
root
|-- column_id: string (nullable = true)
|-- data_sensitivty: string (nullable = true)
|-- datatype: string (nullable = true)
|-- length: string (nullable = true)
|-- name: string (nullable = true)
val column_values = ddb_schema.withColumn("columns", explode($"columns")).select("columns.*")
val column_name = column_values.select("name", "datatype", "length")
column_name.show(4)
+------------------+--------+------+
| name|datatype|length|
+------------------+--------+------+
| object_number| varchar| 100|
| function_type| varchar| 100|
| hof_1| decimal| 17,3|
| hof_2| decimal| 17,2|
| region| varchar| 100|
| country| varchar| null|
+------------------+--------+------+
Şimdi yukarıda listelenen tüm değerler için aşağıdaki kodu kullanarak dinamik olarak val şeması oluşturmaya çalışıyorum
val schemaColumns = column_name.collect()
val schema = schemaColumns.foldLeft(new StructType())(
(schema, columnRow) => schema.add(columnRow.getAs[String]("name"), getFieldType(columnRow.getAs[String]("datatype")), true)
)
def getFieldType(typeName: String): DataType = typeName match {
case "varchar" => StringType
// TODO include other types here
case _ => StringType
}
Yukarıdaki sorun şu ki, veri türlerini struct'ta alabiliyorum , ancak aynı zamanda yalnızca veri türü ondalık için (scale and preicion) elde etmek istiyorum, en fazla izin verilen bir koşulla, ondalık için uzunluk null ise veya mevcut değilse varsayılan değeri (10,0) olarak almalıyız ve mevcut değer 38'den büyükse varsayılan değeri (38,0) olarak almalıyız
Yanıtlar
Bu yaklaşım iyi çalışıyor.
Size kodunuzu ve beklenen sonucu tamamlayan tam bir örnek gösteriyorum.
Daha fazla değişken ekleyebilirsin val data.
/**
* to obtain a tuple with precision and scale
* @param precision Option[String]
* @return (Int, Int)
*/
def getDecimalScale(precision: Option[String]): (Int, Int) = {
precision match {
case Some(pr) => {
pr.split(",").toList match {
case List(h, _) if h.toInt >= 38 => (38,0)
case List(h, t) => (h.toInt,t.head.toString.toInt)
case _ => (10, 0)
}
}
case None => (10, 0)
}
}
val data = List(("object_number", "varchar", "100"), ("function_type", "varchar", "100"),
("hof_1", "decimal", "17,3"), ("hof_2", "decimal", "17,2"),
("hof_3", "decimal", null),("hof_4", "decimal", "39,2"),
("region", "varchar", "100"), ("country", "varchar", null))
import spark.implicits._
val column_name = sc.parallelize(data).toDF("name","datatype","length")
column_name.show()
/*
+-------------+--------+------+
| name|datatype|length|
+-------------+--------+------+
|object_number| varchar| 100|
|function_type| varchar| 100|
| hof_1| decimal| 17,3|
| hof_2| decimal| 17,2|
| hof_3| decimal| null|
| hof_4| decimal| 39,2|
| region| varchar| 100|
| country| varchar| null|
+-------------+--------+------+
*/
val schemaColumns = column_name.collect()
schemaColumns.foreach(println)
/*
[object_number,varchar,100]
[function_type,varchar,100]
[hof_1,decimal,17,3]
[hof_2,decimal,17,2]
[hof_3,decimal,null]
[hof_4,decimal,39,2]
[region,varchar,100]
[country,varchar,null]
*/
val schema = schemaColumns.foldLeft(new StructType())(
(schema, columnRow) => {
columnRow.getAs[String]("datatype") match {
case "varchar" => schema.add(columnRow.getAs[String]("name"), StringType, true)
case "decimal" => {
val (pr, sc) = getDecimalScale(Option(columnRow.getAs[String]("length")))
schema.add(columnRow.getAs[String]("name"), new DecimalType(precision = pr, scale = sc), true)
}
case _ => schema.add(columnRow.getAs[String]("name"), StringType, true)
}
}
)
schema.printTreeString()
/*
root
|-- object_number: string (nullable = true)
|-- function_type: string (nullable = true)
|-- hof_1: decimal(17,3) (nullable = true)
|-- hof_2: decimal(17,2) (nullable = true)
|-- hof_3: decimal(10,0) (nullable = true)
|-- hof_4: decimal(38,0) (nullable = true)
|-- region: string (nullable = true)
|-- country: string (nullable = true)
*/
Kesinlikle ondalık veri türü burada belirtildiği gibi oluşturulabilir :
DataTypes.createDecimalType()
Ondalık tür için "getFieldType" işlevinde durum eklenebilir, smth. sevmek:
case "decimal" => DataTypes.createDecimalType(10,0)