package org.apache.spark.examples
import DsFilter.Student
import org.apache.spark.{HashPartitioner, Partitioner}
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.types.StructType
object DsFilter {
def main(args: Array[String]): Unit = {
System.setProperty("hadoop.home.dir","E:\\office\\hadoop-2.7.1") //如果讀取不到環境變量,設置這個
val spark: SparkSession = SparkSession
.builder
.appName("Spark Examples")
.master("local[*]") //我的機器是4core
.getOrCreate()
var list = Seq[Student]()
for (i <- 1 to 10){
list= list :+ Student("cc",i,i.toString)
list= list :+ Student("chi",i,i.toString)
list= list:+Student("zbf",i,i.toString)
list= list:+Student("dd",i,i.toString)
}//創建40條記錄
val userList=Seq(User("cc",true),User("chi",true),User("dd",false),User("zbf",false))
val rdd: RDD[Student] = spark.sparkContext.parallelize(list) //parallelize(list , numpartition) 這裏可以指定分區數,默認是hash分區,既然是hash分區那麼可能分配不均
val partitions: Long = rdd.count()
println(s"rdd中的數據量=${partitions}") // 40
printRDDPartition(rdd)
/**
* 第0個分區-------(cc,1,1),(chi,1,1),(zbf,1,1),(dd,1,1),(cc,2,2),(chi,2,2),(zbf,2,2),(dd,2,2),(cc,3,3),(chi,3,3)
* 第1個分區-------(zbf,3,3),(dd,3,3),(cc,4,4),(chi,4,4),(zbf,4,4),(dd,4,4),(cc,5,5),(chi,5,5),(zbf,5,5),(dd,5,5)
* 第2個分區-------(cc,6,6),(chi,6,6),(zbf,6,6),(dd,6,6),(cc,7,7),(chi,7,7),(zbf,7,7),(dd,7,7),(cc,8,8),(chi,8,8)
* 第3個分區-------(zbf,8,8),(dd,8,8),(cc,9,9),(chi,9,9),(zbf,9,9),(dd,9,9),(cc,10,10),(chi,10,10),(zbf,10,10),(dd,10,10)
*/
val studentName: Array[String] = rdd.map(_.name).collect().distinct //獲取有多少個name, 目前是四個
val hashparitionedRddTurple: RDD[(String, Student)] = rdd.map(x=>(x.name,x)).partitionBy(new HashPartitioner(4))
val hashpartitonedRDd: RDD[Student] = hashparitionedRddTurple.map(_._2) //裏面的元素還是那40條 但是存放位置不一樣了
println("hashpartitonedRDd分區後的數據")
printRDDPartition(hashpartitonedRDd)
/**
* 第0個分區-------(cc,1,1),(chi,1,1),(dd,1,1),(cc,2,2),(chi,2,2),(dd,2,2),(cc,3,3),(chi,3,3),(dd,3,3),(cc,4,4),(chi,4,4),(dd,4,4),(cc,5,5),(chi,5,5),(dd,5,5),(cc,6,6),(chi,6,6),(dd,6,6),(cc,7,7),(chi,7,7),(dd,7,7),(cc,8,8),(chi,8,8),(dd,8,8),(cc,9,9),(chi,9,9),(dd,9,9),(cc,10,10),(chi,10,10),(dd,10,10)
* 第1個分區-------
* 第2個分區-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
* 第3個分區-------
*/
val paritionedRddTurple: RDD[(String, Student)] = rdd.map(x=>(x.name,x)).partitionBy(new MyPartition(studentName))
val partitonedRDd: RDD[Student] = paritionedRddTurple.map(_._2) //裏面的元素還是那40條 但是存放位置不一樣了
println("rdd分區後的數據")
printRDDPartition(partitonedRDd)
/**
* 第0個分區-------(cc,1,1),(cc,2,2),(cc,3,3),(cc,4,4),(cc,5,5),(cc,6,6),(cc,7,7),(cc,8,8),(cc,9,9),(cc,10,10)
* 第1個分區-------(chi,1,1),(chi,2,2),(chi,3,3),(chi,4,4),(chi,5,5),(chi,6,6),(chi,7,7),(chi,8,8),(chi,9,9),(chi,10,10)
* 第2個分區-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
* 第3個分區-------(dd,1,1),(dd,2,2),(dd,3,3),(dd,4,4),(dd,5,5),(dd,6,6),(dd,7,7),(dd,8,8),(dd,9,9),(dd,10,10)
*/
/* rdd中的數據其實看似有序,但是對於我們來說大部分無序,所以需要我們自己定義分區器,
自定義分區的作用:一個分區做一件事,
①比如我要輸出文件有序第一個文件是cc第二個是chi第三個是zbf,現在4個分區最後輸出文件是4個
②每個分區數據有不同的邏輯 比如我想cc的age+1,chi的age+2
rdd.foreachPartition(iter=>{
val list: List[Student] = iter.toList
if(list.head.name.equals("cc")){}
if(list.head.name.equals("chi")){}
})
// 下面是lowb方法,先過濾出需要的rdd然後再處理
rdd.filter(_.name.equals("cc"))
rdd.filter(_.name.equals("chi"))
多嘴說一句
rdd.foreachPartition作用 比如我現在rdd是4個分區,數據是40個
如果涉及到數據庫連接建議用foreaparition 此時只需要創建4個連接,每個連接對應一個分區
如果用foreach 則需要創建40個連接 每個連接對應一條數據
有人會說我在最外面建立一個就好對應整個數據集合,不可能,這個涉及到序列化問題,簡單來說上面兩個連接都是在worker端建立
//driver端
建立連接 對應整個數據 然後driver到work相當於兩個世界,要把driver的數據到work相當於快遞要打包(序列化),目前連接不能序列化
foreach/partition(
//work端
//建立連接 對應一條數據或者一個分區
)
*/
import spark.implicits._ //導入隱世轉換,因爲rdd是org.apache.spark.rdd包下的本身沒有toDF方法,所以導入spark.implicits._包,纔有rdd.todf
val ds: Dataset[User] = spark.createDataset(userList)
val rddToDf: DataFrame = rdd.toDF() //注意 rdd中是student樣例類所以可以直接轉化爲,如果是List(Seq("cc",1,"1"),Seq("cc",2,"2")) 要toDF("name","age","score")
rddToDf.show(5) //action算子,自己測試時可以用,實際生產個人不建議頻繁用
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 1| 1|
* | chi| 1| 1|
* | zbf| 1| 1|
* | dd| 1| 1|
* | cc| 2| 2|
* +----+---+-----+
* only showing top 5 rows
*/
//df的repartition
val dfPartitionedRdd: RDD[Student] = rddToDf.repartition(4,new Column("name")).rdd.map(x=>Student(x.getString(0),x.getInt(1),x.getString(2)))
println("rdd轉化成df後repartition後每個rdd分區")
printRDDPartition(dfPartitionedRdd)
/** 說明下,這裏df.repartition 根據name分區,是根據name 的hashcode值,以上述爲例
* cc.hashcode=1 chi.hashcode=1 dd.hashcode=0 zbf.hashcode=3
* 取餘後 dd分配到0分區,cc和chi分配到1分區,zbf分配到3分區,
* df的reparition 和 rdd.partition都是hash分區
* 第0個分區-------(dd,1,1),(dd,2,2),(dd,3,3),(dd,4,4),(dd,5,5),(dd,6,6),(dd,7,7),(dd,8,8),(dd,9,9),(dd,10,10)
* 第1個分區-------(cc,1,1),(chi,1,1),(cc,2,2),(chi,2,2),(cc,3,3),(chi,3,3),(cc,4,4),(chi,4,4),(cc,5,5),(chi,5,5),(cc,6,6),(chi,6,6),(cc,7,7),(chi,7,7),(cc,8,8),(chi,8,8),(cc,9,9),(chi,9,9),(cc,10,10),(chi,10,10)
* 第2個分區-------
* 第3個分區-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
*/
rddToDf.describe("name","age","score").show()
/** //科學計數法
* +-------+----+------------------+
* |summary|name| age|
* +-------+----+------------------+
* | count| 30| 30|
* | mean|null| 5.5|
* | stddev|null|2.9213837061606074|
* | min| cc| 1|
* | max| zbf| 10|
* +-------+----+------------------+
*/
println(" rddToDf.printSchema()=")
rddToDf.printSchema()
/**
* root
* |-- name: string (nullable = true)
* |-- age: integer (nullable = false)
* |-- score: string (nullable = true)
*/
val column1: Column = rddToDf.apply("name")
println(s"rddToDf.apply=${column1}")
/**
* rddToDf.apply=name
*/
val schema: StructType = rddToDf.schema
println(s" rddToDf.schema=${schema.mkString("|")}")
/**
* rddToDf.schema=StructField(name,StringType,true)|StructField(age,IntegerType,false)|StructField(score,StringType,true)
*/
val columns: Array[String] = rddToDf.columns
println(s" rddToDf.columns=${columns.mkString("|")}")
/**
* rddToDf.columns=name|age|score
*/
val dtypes: Array[(String, String)] = rddToDf.dtypes
println(s" rddToDf.dtypes=${dtypes.mkString("|")}")
/**
* rddToDf.dtypes=(name,StringType)|(age,IntegerType)|(score,StringType)
*/
val column: Column = rddToDf.col("name")
println(s"column=${column}")
/**
* column=name
*/
rddToDf.explain() //目前層次未到 有點難看懂
rddToDf.createTempView("Student") //創建一張臨時表
val df: DataFrame = spark.sql("select * from Student")
df.show(5)
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 1| 1|
* | chi| 1| 1|
* | zbf| 1| 1|
* | dd| 1| 1|
* | cc| 2| 2|
* +----+---+-----+
* only showing top 5 rows
*/
import org.apache.spark.sql.functions._ //下面這種屬於DSL風格sql查詢,方法也是再functions._下的包裏纔有
df.filter(col("name").equalTo("cc")).show(5)
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 1| 1|
* | cc| 2| 2|
* | cc| 3| 3|
* | cc| 4| 4|
* | cc| 5| 5|
* +----+---+-----+
*/
df.filter("age>5").show(5)
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 6| 6|
* | chi| 6| 6|
* | zbf| 6| 6|
* | dd| 6| 6|
* | cc| 7| 7|
* +----+---+-----+
* only showing top 5 rows
*/
df.groupBy("name").agg(count(col("age")),avg(col("score"))).show(3)
/**
* +----+----------+----------+
* |name|count(age)|avg(score)|
* +----+----------+----------+
* | zbf| 10| 5.5|
* | cc| 10| 5.5|
* | chi| 10| 5.5|
* +----+----------+----------+
* only showing top 3 rows
*/
df.groupBy("name").agg(max(col("age"))as("maxAge"),avg(col("score")).as("avgScore")).show(3)
/** //取別名
* +----+------+--------+
* |name|maxAge|avgScore|
* +----+------+--------+
* | zbf| 10| 5.5|
* | cc| 10| 5.5|
* | chi| 10| 5.5|
* +----+------+--------+
* only showing top 3 rows
*/
df.cube("name","age").agg(sum("score").as("score")).orderBy(col("name"),col("score")).show(100)
/**55條記錄 =40條記錄+4條name=null + 10條age=null +1條name=null&&age=null
* select name ,age ,sum(score) as score from student groupby name ,age
* union select null age ,sum(score) as score from student groupby age
* union select name,null ,sum(score) as score from student groupby name
* union select null,null,sum(score) as score from student groupby name
* +----+----+-----+
* |name| age|score|
* +----+----+-----+
* |null| 1| 4.0|
* |null| 2| 8.0|
* |null| 3| 12.0|
* |null| 4| 16.0|
* |null| 5| 20.0|
* |null| 6| 24.0|
* |null| 7| 28.0|
* |null| 8| 32.0|
* |null| 9| 36.0|
* |null| 10| 40.0|
* |null|null|220.0|
* | cc| 1| 1.0|
* | cc| 2| 2.0|
* | cc| 3| 3.0|
* | cc| 4| 4.0|
* | cc| 5| 5.0|
* | cc| 6| 6.0|
* | cc| 7| 7.0|
* | cc| 8| 8.0|
* | cc| 9| 9.0|
* | cc| 10| 10.0|
* | cc|null| 55.0|
* | chi| 1| 1.0|
* | chi| 2| 2.0|
* | chi| 3| 3.0|
* | chi| 4| 4.0|
* | chi| 5| 5.0|
* | chi| 6| 6.0|
* | chi| 7| 7.0|
* | chi| 8| 8.0|
* | chi| 9| 9.0|
* | chi| 10| 10.0|
* | chi|null| 55.0|
* | dd| 1| 1.0|
* | dd| 2| 2.0|
* | dd| 3| 3.0|
* | dd| 4| 4.0|
* | dd| 5| 5.0|
* | dd| 6| 6.0|
* | dd| 7| 7.0|
* | dd| 8| 8.0|
* | dd| 9| 9.0|
* | dd| 10| 10.0|
* | dd|null| 55.0|
* | zbf| 1| 1.0|
* | zbf| 2| 2.0|
* | zbf| 3| 3.0|
* | zbf| 4| 4.0|
* | zbf| 5| 5.0|
* | zbf| 6| 6.0|
* | zbf| 7| 7.0|
* | zbf| 8| 8.0|
* | zbf| 9| 9.0|
* | zbf| 10| 10.0|
* | zbf|null| 55.0|
* +----+----+-----+
*/
df.rollup("name","age").agg(sum("score").as("score")).orderBy(col("name"),col("score")).show(100)
/**45條記錄=40 + 4條name分組 和1條 不分組
* select name ,age ,sum(score) as score from student groupby name ,age
* union select name,null ,sum(score) as score from student groupby name
* union select null,null,sum(score) as score from student groupby name
* !!!!!區別 個人理解!!!!!!
* cube立方三維,借用kylin的思想就是從多個角度去看分組,如果我分組維度有3個,name,age,score,那麼先減去1個維度獲得【(name,age)(name,score)(age.score)】,然後減去2個維度獲得[(name),(age),(score)],再減去3個維度【】
* rollup,向上 注意比如我name age 分組,(注意順序!!)那麼我還是逐漸減少分組維度,向上遞減,先減去age這個維度獲得4條記錄,然後減去name這個維度 獲得1條記錄
* +----+----+-----+
* |name| age|score|
* +----+----+-----+
* |null|null|220.0|
* | cc| 1| 1.0|
* | cc| 2| 2.0|
* | cc| 3| 3.0|
* | cc| 4| 4.0|
* | cc| 5| 5.0|
* | cc| 6| 6.0|
* | cc| 7| 7.0|
* | cc| 8| 8.0|
* | cc| 9| 9.0|
* | cc| 10| 10.0|
* | cc|null| 55.0|
* | chi| 1| 1.0|
* | chi| 2| 2.0|
* | chi| 3| 3.0|
* | chi| 4| 4.0|
* | chi| 5| 5.0|
* | chi| 6| 6.0|
* | chi| 7| 7.0|
* | chi| 8| 8.0|
* | chi| 9| 9.0|
* | chi| 10| 10.0|
* | chi|null| 55.0|
* | dd| 1| 1.0|
* | dd| 2| 2.0|
* | dd| 3| 3.0|
* | dd| 4| 4.0|
* | dd| 5| 5.0|
* | dd| 6| 6.0|
* | dd| 7| 7.0|
* | dd| 8| 8.0|
* | dd| 9| 9.0|
* | dd| 10| 10.0|
* | dd|null| 55.0|
* | zbf| 1| 1.0|
* | zbf| 2| 2.0|
* | zbf| 3| 3.0|
* | zbf| 4| 4.0|
* | zbf| 5| 5.0|
* | zbf| 6| 6.0|
* | zbf| 7| 7.0|
* | zbf| 8| 8.0|
* | zbf| 9| 9.0|
* | zbf| 10| 10.0|
* | zbf|null| 55.0|
* +----+----+-----+
*/
df.withColumn("newname",col("name")).show(3)
/** 新增一列
* +----+---+-----+-------+
* |name|age|score|newname|
* +----+---+-----+-------+
* | cc| 1| 1| cc|
* | chi| 1| 1| chi|
* | zbf| 1| 1| zbf|
* +----+---+-----+-------+
* only showing top 3 rows
*/
df.withColumn("newname",col("name")).select("name","age","score").show(3)
/**
* +----+---+-----+-------+
* |name|age|score|newname|
* +----+---+-----+-------+
* | cc| 1| 1| cc|
* | chi| 1| 1| chi|
* | zbf| 1| 1| zbf|
* +----+---+-----+-------+
* only showing top 3 rows
*/
df.select(col("name").equalTo("cc")).show(5)
/**這個剛好和上面對應 cc chi zbf dd cc 剛好是true false false false true
* +-----------+
* |(name = cc)|
* +-----------+
* | true|
* | false|
* | false|
* | false|
* | true|
* +-----------+
* only showing top 5 rows
*/
df.select(col("age").lt(5)).show(5) //小於
/**
* +---------+
* |(age < 5)|
* +---------+
* | true|
* | true|
* | true|
* | true|
* | true|
* +---------+
* only showing top 5 rows
*/
val dsBC: Broadcast[Dataset[User]] = spark.sparkContext.broadcast(ds)
df.join(ds,df("name")===ds("name"),"outer").orderBy(col("score")).show(4)
/**兩張表關聯
* 'inner', 'outer', 'full', 'fullouter', 'full_outer', 'leftouter', 'left', 'left_outer',
* 'rightouter', 'right', 'right_outer', 'leftsemi', 'left_semi', 'leftanti', 'left_anti', 'cross'
* +----+---+-----+----+-----+
* |name|age|score|name|shuai|
* +----+---+-----+----+-----+
* | cc| 1| 1| cc| true|
* | chi| 1| 1| chi| true|
* | zbf| 1| 1| zbf|false|
* | dd| 1| 1| dd|false|
* +----+---+-----+----+-----+
* only showing top 10 rows
*/
}
//打印輸出
def printRDDPartition(rdd:RDD[Student]): Unit ={
val array: Array[Array[Student]] = rdd.glom().collect()
for(i <- 0 until array.size){
println(s"第${i}個分區-------${array(i).map(x=>{
(x.name,x.age,x.score)
}).mkString(",")}")
}
}
case class Student(name: String, age: Int,score:String){}
case class User(name: String, shuai:Boolean){}
class MyPartition(studentName:Array[String]) extends Partitioner{
override def numPartitions: Int = studentName.size //分區數
override def getPartition(key: Any): Int = { //這個方法是那40條數據,每條數據都會把name 傳進來返回一個分區號
val index: Array[(String, Int)] = studentName.zipWithIndex //給每個名字固定一個下標 =>其實也就是第幾個分區
val tuples: Array[(String, Int)] = index.filter(_._1.equals(key.toString)) //在index數組中找到傳來名字的屬於哪個分區
tuples.head._2 //head是因爲 數據去重了只有一條
}
}
}
spark中dataframe,dataset,sparksql中的各種用法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.