如何在不使用Scala案例类的情况下指定CSV文件的架构?

时间:2016-11-17 11:32:20

标签: scala apache-spark apache-spark-sql

我正在将CSV文件加载到DataFrame中,如下所示。

val conf=new SparkConf().setAppName("dataframes").setMaster("local")
val sc=new SparkContext(conf)
val spark=SparkSession.builder().getOrCreate()
import spark.implicits._

val df = spark.
  read.  
  format("org.apache.spark.csv").
  option("header", true).
  csv("/home/cloudera/Book1.csv")
scala> df.printSchema()
root
 |-- name: string (nullable = true)
 |-- address: string (nullable = true)
 |-- age: string (nullable = true)

如何将age列更改为Int类型?

3 个答案:

答案 0 :(得分:28)

鉴于val spark=SparkSession.builder().getOrCreate()猜测您正在使用Spark 2.x.

首先,请注意Spark 2.x具有CSV格式的原生支持,因此不需要通过其长名称指定格式,即org.apache.spark.csv,而只需csv

spark.read.format("csv")...

由于您使用csv运算符,因此隐含了CSV格式,因此您可以跳过/删除format("csv")

// note that I removed format("csv")
spark.read.option("header", true).csv("/home/cloudera/Book1.csv")

有了这个,你有很多选择,但我强烈建议使用一个案例类......只是架构。如果您对Spark 2.0中的操作方式感到好奇,请参阅最后的解决方案。

演员

您可以使用cast运算符。

scala> Seq("1").toDF("str").withColumn("num", 'str cast "int").printSchema
root
 |-- str: string (nullable = true)
 |-- num: integer (nullable = true)

使用StructType

您还可以使用自己手工制作的模式StructTypeStructField,如下所示:

import org.apache.spark.sql.types._    
val schema = StructType(
  StructField("str", StringType, true) :: 
  StructField("num", IntegerType, true) :: Nil)

scala> schema.printTreeString
root
 |-- str: string (nullable = true)
 |-- num: integer (nullable = true)

val q = spark.
  read.
  option("header", true).
  schema(schema).
  csv("numbers.csv")
scala> q.printSchema
root
 |-- str: string (nullable = true)
 |-- num: integer (nullable = true)

架构DSL

我最近发现的很有趣的是所谓的 Schema DSL 。使用StructTypeStructField构建的上述架构可以重写如下:

import org.apache.spark.sql.types._
val schema = StructType(
  $"str".string ::
  $"num".int :: Nil) 
scala> schema.printTreeString
root
 |-- str: string (nullable = true)
 |-- num: integer (nullable = true)

// or even
val schema = new StructType().
  add($"str".string).
  add($"num".int)
scala> schema.printTreeString
root
 |-- str: string (nullable = true)
 |-- num: integer (nullable = true)

编码器

编码器非常易于使用,很难相信您不会想要它们,即使只是在不处理StructTypeStructFieldDataType的情况下构建架构

// Define a business object that describes your dataset
case class MyRecord(str: String, num: Int)

// Use Encoders object to create a schema off the business object
import org.apache.spark.sql.Encoders    
val schema = Encoders.product[MyRecord].schema
scala> schema.printTreeString
root
 |-- str: string (nullable = true)
 |-- num: integer (nullable = false)

答案 1 :(得分:20)

inferSchema选项可通过以下方式自动识别变量的类型:

val df=spark.read
  .format("org.apache.spark.csv")
  .option("header", true)
  .option("inferSchema", true) // <-- HERE
  .csv("/home/cloudera/Book1.csv")

spark-csv最初是databricks的外部库,但包含在spark版本2.0之后的核心spark中。您可以参考图书馆github page上的文档来查找可用选项。

答案 2 :(得分:-2)

在这种情况下,您可以使用UDF:

第1步:创建一个将String转换为Int的udf。

val stringToIntUDF = udf((value:String)=>value.toInt)

第2步:将此UDF应用于要转换的列!

val updatedDF = df.withColumns("age",stringToIntUDF(df("age")))
updatedDF.printSchema

这应该会给你你想要的结果!

如果您只想从CSV文件推断出架构。然后@vdep解决方案似乎正在做正确的事情!

val df=spark.read
  .format("org.apache.spark.csv")
  .option("header",true)
  .option("inferSchema", "true") // <-- HERE
  .csv("/home/cloudera/Book1.csv")