项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步
1.为什么要有DataFrame
Spark中的RDD叫做分布式弹性数据集。RDD是一个粗粒度的分布式计算,用函数声明式的api就能完成分布式的计算,比如wordcount,在mapreduce要写比较冗长的代码,而在Spark中可以用一行代码搞定。
既然RDD这么简单方便,为什么还要搞出一个DataFrame来呢?DataFrame是借鉴了R与pandas众DataFrame的思想,是业界处理标准化数据集的事实标准。DataFrame跟RDD相比,多了一些限制条件,但同时也有了更多的优化。比如基于Spark Catalyst优化器,提供如列裁剪,谓词下推,map join等优化。同时,采用code generation ,动态编译表达式,提升性能,比用rdd的自定义函数性能高5倍左右。
Example:
rdd.map{x => x.split("\t"); (lines(0), lines(1))}
.filter(x => x._2 >= 18)
.select(x => (x._1, x._2))
而如果采用类似DataFrame的结构:
sqlContext.table("people")
.filter(col("age") >= 19)
.select("id", "name")
用rdd读结构化文本要用map函数,需要按位置获取数据,没有schema,性能和可读性都不好。
而用dataframe可以直接通过sede读取结构化数据,性能比RDD高2到3倍左右,比MR高5倍左右,同时,具有结构化的数据,可读性更好。
Spark2.0以后的版本推出了DataSet,是更加强类型的API,用了scala的泛型,能在编译是发现更多的编译问题DataFrame是DataSet〈Row〉类型,DS在接口上和DataFrame很相似。
2.通过toDF方法创建DataFrame
sparkSQL中implicits里面有toDF方法,toDF方法可以将本地的Seq,Array或者RDD转化为DataFrame
import sqlContext.implicits._
val wordDataFrame = Seq((0, Array("Hi", "I", "heard", "about", "Spark")),(1, Array("I", "wish", "Java", "could", "use", "case", "classes")),(2, Array("Logistic", "regression", "models", "are", "neat"))).toDF("label", "words")
在spark shell中运行上面代码以后,结果如下:
scala> val wordDataFrame = Seq((0, Array("Hi", "I", "heard", "about", "Spark")),(1, Array("I", "wish", "Java", "could", "use", "case", "classes")),(2, Array("Logistic", "regression", "models", "are", "neat"))).toDF("label", "words")
wordDataFrame: org.apache.spark.sql.DataFrame = [label: int, words: array<string>]
可以看到我们已经得到了一个DataFrame。
如果toDF方法不指定字段名称,那么默认列名为"_1", “_2”, …
scala> val wordDataFrame = Seq((0, Array("Hi", "I", "heard", "about", "Spark")),(1, Array("I", "wish", "Java", "could", "use", "case", "classes")),(2, Array("Logistic", "regression", "models", "are", "neat"))).toDF()
wordDataFrame: org.apache.spark.sql.DataFrame = [_1: int, _2: array<string>]
我们还可以通过case class 与toDF的方式得到DataFrame
scala> import sqlContext.implicits._
import sqlContext.implicits._
scala> import scala.collection.JavaConversions._
import scala.collection.JavaConversions._
scala> case class word(num: Int, wordlist:Array[String])
defined class word
sc.parallelize(Seq((0, Array("Hi", "I", "heard", "about", "Spark")),(1, Array("I", "wish", "Java", "could", "use", "case", "classes")),(2, Array("Logistic", "regression", "models", "are", "neat")))).map(x => word(x._1.toInt, x._2)).foreach(x => println(x.num, x.wordlist.mkString("-")))
(1,I-wish-Java-could-use-case-classes)
(2,Logistic-regression-models-are-neat)
(0,Hi-I-heard-about-Spark)
3.通过createDataFrame创建DataFrame
在SqlContext中使用createDataFrame也可以创建DataFrame。跟toDF一样,这里创建DataFrame的数据形态也可以是本地数组或者RDD。这种方法在由于数据的结构以字符串的形式编码而无法提前定义定制类的情况下非常实用。
import org.apache.spark.sql._
import org.apache.spark.sql.types._
scala> val schema = StructType(List(StructField("integer_column", IntegerType, nullable = false),StructField("string_column", StringType, nullable = true),StructField("date_column", DateType, nullable = true)))
schema: org.apache.spark.sql.types.StructType = StructType(StructField(integer_column,IntegerType,false), StructField(string_column,StringType,true), StructField(date_column,DateType,true))
scala> val rdd = sc.parallelize(Seq(Row(1, "First Value", java.sql.Date.valueOf("2010-01-01")),Row(2, "Second Value", java.sql.Date.valueOf("2010-02-01"))))
rdd: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = ParallelCollectionRDD[4] at parallelize at <console>:39
scala> val df = sqlContext.createDataFrame(rdd, schema)
df: org.apache.spark.sql.DataFrame = [integer_column: int, string_column: string, date_column: date]
4.通过parquet文件或者json文件创建DataFrame
val df = sqlContext.read.parquet("xxx/file")
val df = spark.read.json("xxx/file.json")