在阅读了一些技术文章之后,据说数据框只知道列的名称,而不知道类型。但是,当亲自调用数据框的`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)
*/
}
}
在此示例中,数据框架构的年龄类型是整数,数据集架构的年龄类型是long,类Person的年龄类型是long。
答案 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根据数据(基于int
和1
的{{1}}选择最佳数据类型。
在第二个示例中,您将创建一个DataSet,该DataSet会基于提供的架构保留数据类型。所以:
2
创建一个val ds = rdd.map(line => Person(line._1,line._2) ).toDS()
,以保持指定的架构完整。