Как изменить тип данных столбца в StructField для StructType? - PullRequest
0 голосов
/ 29 января 2019

Я пытаюсь изменить тип данных столбца, присутствующего в кадре данных I, который я читаю из базы данных RDBMS.Для этого я получил схему кадра данных следующим образом:

val dataSchema = dataDF.schema

Чтобы увидеть схему кадра данных, я использовал следующий оператор:

println(dataSchema.schema)

Output: StructType(StructField(je_header_id,LongType,true), StructField(je_line_num,LongType,true), StructField(last_update_date,TimestampType,true), StructField(last_updated_by,DecimalType(15,0),true), StructField(creation_date,TimestampType,true), StructField(created_by,DecimalType(15,0),true), StructField(created_by_name,StringType,true), StructField(entered_dr,DecimalType(38,30),true), StructField(entered_cr,DecimalType(38,30),true))

Мои требованиянайти DecimalType и изменить его на DoubleType из приведенной выше схемы.Я могу получить имя столбца и типы данных, используя: dataSchema.dtype, но он дает мне типы данных в формате ((columnName1, column datatype),(columnName2, column datatype)....(columnNameN, column datatype))

Я пытаюсь найти способ разобрать StructType и изменить схему в dataSchemaНапрасно.

Может кто-нибудь сообщить мне, если есть способ разобрать StructType, чтобы я мог изменить тип данных в соответствии с моим требованием и получить в следующем формате

StructType(StructField(je_header_id,LongType,true), StructField(je_line_num,LongType,true), StructField(last_update_date,TimestampType,true), StructField(last_updated_by,DoubleType,true), StructField(creation_date,TimestampType,true), StructField(created_by,DoubleType,true), StructField(created_by_name,StringType,true), StructField(entered_dr,DoubleType,true), StructField(entered_cr,DoubleType,true))

Ответы [ 2 ]

0 голосов
/ 29 января 2019

Вот еще один способ:

data.show(false)
data.printSchema

+----+------------------------+----+----------------------+
|col1|col2                    |col3|col4                  |
+----+------------------------+----+----------------------+
|1   |0.003200000000000000    |a   |23.320000000000000000 |
|2   |78787.990030000000000000|c   |343.320000000000000000|
+----+------------------------+----+----------------------+

root
 |-- col1: integer (nullable = false)
 |-- col2: decimal(38,18) (nullable = true)
 |-- col3: string (nullable = true)
 |-- col4: decimal(38,18) (nullable = true) 

Создайте необходимую схему:
Пример:

val newSchema = StructType(
  Seq(
    StructField("col1", StringType, true),
    StructField("col2", DoubleType, true),
    StructField("col3", StringType, true),
    StructField("col4", DoubleType, true)
  )
)

Приведите столбцы к требуемому типу данных.

val newDF = data.selectExpr(newSchema.map(
   col => s"CAST ( ${col.name} As ${col.dataType.sql}) ${col.name}"
  ): _*)

newDF.printSchema

root
 |-- col1: string (nullable = false)
 |-- col2: double (nullable = true)
 |-- col3: string (nullable = true)
 |-- col4: double (nullable = true) 

newDF.show(false)
+----+-----------+----+------+
|col1|col2       |col3|col4  |
+----+-----------+----+------+
|1   |0.0032     |a   |23.32 |
|2   |78787.99003|c   |343.32|
+----+-----------+----+------+
0 голосов
/ 29 января 2019

Чтобы изменить схему DataFrame, специфичную для данного типа данных, вы можете сопоставить шаблон с StructField dataType, как показано ниже:

import org.apache.spark.sql.types._

val df = Seq(
  (1L, BigDecimal(12.34), "a", BigDecimal(10.001)),
  (2L, BigDecimal(56.78), "b", BigDecimal(20.002))
).toDF("c1", "c2", "c3", "c4")

val newSchema = df.schema.fields.map{
  case StructField(name, _: DecimalType, nullable, _)
    => StructField(name, DoubleType, nullable)
  case field => field
}
// newSchema: Array[org.apache.spark.sql.types.StructField] = Array(
//   StructField(c1,LongType,false), StructField(c2,DoubleType,true),
//   StructField(c3,StringType,true), StructField(c4,DoubleType,true)
// )

Однако, если предположить,Ваша конечная цель - преобразовать набор данных с изменением типа столбца, было бы проще просто пройти столбцы для целевого типа данных итеративно cast их, как показано ниже:

import org.apache.spark.sql.functions._

val df2 = df.dtypes.
  collect{ case (dn, dt) if dt.startsWith("DecimalType") => dn }.
  foldLeft(df)((accDF, c) => accDF.withColumn(c, col(c).cast("Double")))

df2.printSchema
// root
//  |-- c1: long (nullable = false)
//  |-- c2: double (nullable = true)
//  |-- c3: string (nullable = true)
//  |-- c4: double (nullable = true)

[ОБНОВЛЕНИЕ]

По дополнительному требованию из комментариев, если вы хотите изменить схему только для DecimalType с положительной шкалой, просто примените сопоставление с шаблоном Regex в качестве условия guard в методе collect:

val pattern = """DecimalType\(\d+,(\d+)\)""".r

val df2 = df.dtypes.
  collect{ case (dn, dt) if pattern.findFirstMatchIn(dt).map(_.group(1)).getOrElse("0") != "0" => dn }.
  foldLeft(df)((accDF, c) => accDF.withColumn(c, col(c).cast("Double")))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...