@TOC
这篇文章的所有数据可以通过以下链接获取
链接:https://pan.baidu.com/s/1-Vlo3S8tjMyM6E0kwGALwQ
提取码:nlri
DataFrame可处理结构化数据(按行和列组织的数据,其中每列仅包含特定类型的值)。原本SQL是处理这类数据最常用的方法,而Spark的SparkSQL组件集成了SQL的用法。
DataFrame介绍
RDD代表了一种低级,直接的方式来处理Spark中的数据,它是Spark运行时的核心,而DataFrame API用于类似于表的形式处理结构化的分布式数据。DataFrame的灵感来自几种语言:Python的Pandas包中的DataFrame,R中的DataFrame和Julia语言的DataFrame。Spark的不同之处在于它们的分布式特性和Spark的Catalyst,它可以基于可插拔数据源、规则和数据类型实时优化资源使用。
假设有一个需要连接两种类型数据的查询,Spark SQL可以通过在DataFrame中加载这两个数据源来实现。如下图所示
两个客户端(使用DataFrame DSL的Spark应用程序和通过JDBC连接的非Spark客户端应用程序)执行连接两份数据的查询,这两份数据来自不同的数据源,一个是关系型数据库,另一个是HDFS中的Parquet文件。图中Hive是在Hadoop MapReduce之上构建的抽象层的分布式仓库,被广泛应用于数据查询和分析。SparkSQL允许将DataFrame注册到表目录中的表中,这些表不保存数据,而保存有关如何访问结构化数据的信息。
基于RDD创建DataFrame
基于RDD创建DataFrame的创建有3种方式。
tuple RDD—>DataFrame
import org.apache.spark.sql.SparkSession
import org.apache.spark.{
SparkConf, SparkContext}
object App {
def main(args:Array[String]):Unit={
val sparkAppName = "Basic"
val spark = SparkSession.builder().appName("SparkInAction")
.master("local[*]").getOrCreate()
val sc = spark.sparkContext
import spark.implicits._
val itPostsRows = sc.textFile("/ch05/italianPosts.csv")
val itPostsSplit = itPostsRows.map(x => x.split("~"))
val itPostsRDD = itPostsSplit.map(x => (x(0),x(1),x(2),x(3),x(4),x(5),x(6),x(7),x(8),x(9),x(10),x(11),x(12)))
val itPostsDFrame = itPostsRDD.toDF()
itPostsDFrame.show(10)
}
}
italianPosts.csv文件是 Stack Exchange在2009年发布的,包含社区中的所有问题/答案的匿名数据。它包含意大利语的问题(和答案)与以下字段(用波浪符号“~”分隔)
commentCount:与问题/答案相关的评论数
lastActivityDate:上次修改的日期和时间
ownerUserId:所有者的用户ID
body:问题/答案的文本内容
score:基于upvote和downvotes的总分
creationDate:创建的日期和时间
viewCount:浏览计数
title:问题的标题
tags:已标记问题的标签集
answerCount:相关答案的数量
acceptedAnswerId:如果问题包含其接受的答案的ID
postTypeId:帖子的类型:1是问题,2为答案
id:帖子的类型:1是问题,2为答案
以上show方法如果没有传入参数,则会默认返回20条。可以在RDD转化为DataFrame时指定列名如下,
val itPostsDF = itPostsRDD.toDF("commentCount", "lastActivityDate", "ownerUserId", "body", "score", "creationDate", "viewCount", "title", "tags", "answerCount", "acceptedAnswerId", "postTypeId", "id")
itPostsDF.printSchema
itPostsDF.printSchema的输出结果如下:
root
|-- commentCount: string (nullable = true)
|-- lastActivityDate: string (nullable = true)
|-- ownerUserId: string (nullable = true)
|-- body: string (nullable = true)
|-- score: string (nullable = true)
|-- creationDate: string (nullable = true)
|-- viewCount: string (nullable = true)
|-- title: string (nullable = true)
|-- tags: string (nullable = true)
|-- answerCount: string (nullable = true)
|-- acceptedAnswerId: string (nullable = true)
|-- postTypeId: string (nullable = true)
|-- id: string (nullable = true)
printSchema方法显示DataFrame有关列的信息。这里有几个问题,比如说ID应该是long类型,date列应为时间戳。这种不足会在后面两个方法得以解决。
case class 将RDD转换为DataFrame
将RDD转换为Data的第二种方法是将RDD中的每一行映射到案例类,然后再使用toDF方法。首先需要定义将保存数据的case class如下
import java.sql