为架构创建列类型

时间:2018-10-02 05:41:32

标签: scala apache-spark

我有一个文本文件,我可以读取该文本文件并进行解析以创建数据框。但是,列amountcode应该是IntegerTypes。这是我所拥有的:

def getSchema: StructType = {
        StructType(Seq(
          StructField("carrier", StringType, false),
          StructField("amount", StringType, false),
          StructField("currency", StringType, false),
          StructField("country", StringType, false),
          StructField("code", StringType, false),
        ))
      }

  def getRow(x: String): Row = {
    val columnArray = new Array[String](5)
    columnArray(0) = x.substring(40, 43)
    columnArray(1) = x.substring(43, 46)
    columnArray(2) = x.substring(46, 51)
    columnArray(3) = x.substring(51, 56)
    columnArray(4) = x.substring(56, 64)
    Row.fromSeq(columnArray)
  }

因为我已定义Array[String],所以列只能是StringTypes,而不能是String和Integer的各种。为了详细解释我的问题,发生了什么:

首先,我创建一个空的数据框:

  var df = spark.sqlContext.createDataFrame(spark.sparkContext.emptyRDD[Row], getSchema)

然后我有一个for循环,遍历所有目录中的每个文件。注意:我需要验证每个文件,并且无法一次读取所有文件。

for (each file parse):
  df2  = spark.sqlContext.createDataFrame(spark.sparkContext.textFile(inputPath)
    .map(x => getRow(x)), schema)
df = df.union(df2)

我现在拥有所有文件的完整数据框。但是,列amountcode仍然是StringTypes。如何使它们成为IntegerTypes?

请注意:在for循环过程中,我无法转换列,因为这会花费很多时间。我想保持当前结构尽可能的相似。在for循环的最后,我可以将这些列转换为IntegerTypes,但是,如果该列包含的值不是Integer怎么办?我希望列不为NULL。

是否有一种方法可以使两个指定的列成为IntegerType,而无需对代码进行大量更改?

1 个答案:

答案 0 :(得分:2)

使用数据集怎么办?

首先创建一个对数据建模的案例类:

case class MyObject(
    carrier: String,
    amount: Double,
    currency: String,
    country: String,
    code: Int)

创建另一个案例类,将第一个案例类附加其他信息(潜在错误,源文件):

case class MyObjectWrapper(
                      myObject: Option[MyObject],
                      someError: Option[String],
                      source: String
                      )

然后创建一个解析器,从myObject中的文件转换一行:

object Parser {
  def parse(line: String, file: String): MyObjectWrapper = {
    Try {
      MyObject(
        carrier = line.substring(40, 43),
        amount = line.substring(43, 46).toDouble,
        currency = line.substring(46, 51),
        country = line.substring(51, 56),
        code = line.substring(56, 64).toInt)
    } match {
      case Success(objectParsed) => MyObjectWrapper(Some(objectParsed), None, file)
      case Failure(error) => MyObjectWrapper(None, Some(error.getLocalizedMessage), file)
    }
  }
}

最后,解析您的文件:

import org.apache.spark.sql.functions._
val ds = files
  .filter( {METHOD TO SELECT CORRECT FILES})
  .map( { GET INPUT PATH FROM FILES} )
  .map(path => spark.read.textFile(_).map(Parser.parse(_, path))
  .reduce(_.union(_))

这应该为您提供具有所需类型和API的数据集[MyObjectWrapper]。

然后,您可以解析那些内容:

ds.filter(_.someError == None)

或者接受您未能解析的内容(用于调查):

ds.filter(_.someError != None)