数据框是否知道列的类型?

时间:2019-01-16 01:46:45

标签: apache-spark dataframe apache-spark-sql dataset

在阅读了一些技术文章之后,据说数据框只知道列的名称,而不知道类型。但是,当亲自调用数据框的`printSchema函数后,可以打印出列的名称和类型。我对此非常怀疑。我期待着您的答复。

例:

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SparkSession

case class Person(name:String,age:Long)
object DS {
  def main(args: Array[String]): Unit = {
    val config = new SparkConf().setAppName("sparkSql").setMaster("local[*]")
    val sc = new SparkContext(config)
    val spark = SparkSession.builder().config(config).getOrCreate()
    val seq = Seq(("aa",1),("bb",2))
    import spark.implicits._
    val rdd = sc.makeRDD(seq)
    val df = rdd.toDF("name","age")
    val ds = rdd.map(line =>{Person(line._1,line._2)}).toDS()

    println("dataframe schema:")
    df.printSchema()
/*
    dataframe schema:
      root
    |-- name: string (nullable = true)
    |-- age: integer (nullable = true)
*/
    println("dataset schema:")
    ds.printSchema()
/*
    dataset schema:
      root
    |-- name: string (nullable = true)
    |-- age: long (nullable = true)
*/
  }
}

img

在此示例中,数据框架构的年龄类型是整数,数据集架构的年龄类型是long,类Person的年龄类型是long。

2 个答案:

答案 0 :(得分:1)

这取决于您正在读取的文件类型。

如果它是不带标题的CSV文件,则需要使用架构提供列名和数据类型。

这是一个带标头的CSV文件,然后在读取文件时需要使用“ inferSchema”->“ true”作为选项。此选项自动推断架构和数据类型。但是,数据类型是由实际数据的前几条记录自动驱动的。

val df = spark.read.options(Map("inferSchema"->"true","delimiter"->"|","header"->"true")).csv(filePath)

出于任何原因,如果一列的前几条记录具有值整数,而其他记录具有字符串,那么您将遇到问题,因此,最好的做法是显式提供架构。

您的代码按预期工作。

以下语句根据数据Seq((“ aa”,1),(“ bb”,2))自动将数据类型推断为Int年龄

val df = rdd.toDF("name","age")

但是,当您将数据框转换为数据集时

val ds = rdd.map(line =>{Person(line._1,line._2)}).toDS()

在这里,您正在转换为“年龄”字段具有Long数据类型的Person,因此,您看到的与预期的一样长。请注意,从Int到Long的自动转换是由Scala(向上投射)而不是Spark完成的。

希望这可以澄清!!

下面的链接很好地介绍了如何提供复杂的架构。希望这能给您更多的想法。

https://medium.com/@mrpowers/adding-structtype-columns-to-spark-dataframes-b44125409803

谢谢

答案 1 :(得分:0)

在使用rdd.toDF("name", "age")的第一个示例中,没有显式提供DataFrame的架构。而且,DataFrame实际上只是DataSet[Row]。因此,Spark根据数据(基于int1的{​​{1}}选择最佳数据类型。

在第二个示例中,您将创建一个DataSet,该DataSet会基于提供的架构保留数据类型。所以:

2

创建一个val ds = rdd.map(line => Person(line._1,line._2) ).toDS() ,以保持指定的架构完整。