【Spark】DataFrame

    Spark的專門數據結構是RDD,即分佈式數據集的抽象,spark引擎的底層抽象,spark生態系統中其他組件的實現基礎,但是,他無元信息,使得rdd程序不易理解,不優雅,需要自己優化程序。爲了減少優化程序帶來的勞動力,這裏引入Spark Sql。Spark Sql的編程抽象是dataframe,構建在spark core 上,爲RDD提供元信息,使得分佈式計算引擎有更多機會自動優化程序。文中只是部分內容介紹,後期會逐步細化內容。

Spark SQL && Code

Spark Sql支持:
    sql,類sql(hiveql)查詢;支持sql執行分佈式數據操作或大規模分析
    從已存在的hive安裝讀取數據;
    具備集成關係過程和函數過程能力:實現、優化、複雜的邏輯,在分佈式計算設置上進行擴展;
    dataframes api能夠重點關注程序要做什麼;
    catalyst優化器是spark sql 和 dataframe 的支點:推斷各種數據格式的模式,內置兼容器,控制優化代碼生成。

-- 前提是已經搭建並啓動集羣、spark。
-- 創建spark環境
from pyspark.sql import SparkSession
spark = SparkSession.builder.master("spark://master:7077").appName("df_demo2").getOrCreate()

-- 導入py模塊
from pyspark.sql.functions import *
from pyspark.sql.types import *
   
--創建dataframe :1        
-- 將元組轉爲多列DataFrame
kvDF = spark.createDataFrame([(1,2),(3,4)], schema=["key","value"])

# kvDF.show()

# 要在一個DataFrame中顯示列名,可以調用columns函數
# print(kvDF.columns)

# 選擇指定的列(注意引用列的不同方式)
# kvDF.select("value").show()     # 列爲字符串類型
# kvDF.select(col("key")).show()  # 列爲Column類型
# kvDF.select(column("key")).show() # 列爲Column類型

# type(kvDF.select(column("key")))

--創建dataframe :2       
parquetFile = "/spark_demo/movies/movies.parquet"
movies = spark.read.parquet(parquetFile)
movies.printSchema()

movies.show(15, truncate=False)
# movies.show()   # 默認顯示20行,並且截斷

-- dataframe轉換操作
# 1)select
# movies.select("movie_title","produced_year").show(5)
# 將年份列轉換到年代列
movies2 = movies.select(col('movie_title') ,(col('produced_year') - col('produced_year') % 10).alias("produced_year"))                        
movies2.show(5)

# 2)selectExpr
movies.selectExpr("*","(produced_year - (produced_year % 10)) as decade").show(5)

# 使用SQL表達式和內置函數
# movies.selectExpr("count(distinct(movie_title)) as movies","count(distinct(actor_name)) as actors").show(5)

# 將年份列轉換到年代列
# movies2 = movies.selectExpr("movie_title", "(produced_year-produced_year%10) as produced_decade")
# movies2.show(5)

# 3)filter, where
# movies.filter('produced_year < 2000').show(5)
movies.where('produced_year < 2000').show(5)
# movies.filter('produced_year >= 2000').show(5)
# movies.where('produced_year >= 2000').show(5)

# # 相等比較
# movies.filter('produced_year = 2000').show(5)
# movies.where('produced_year = 2000').show(5)

# # 不等比較使用的操作符是 !=
# movies.select("movie_title","produced_year").filter('produced_year != 2000').show(5)
# movies.select("movie_title","produced_year").where('produced_year != 2000').show(5)
# movies.select("movie_title","produced_year").filter(col('produced_year') != 2000).show(5)

# # 組合一個或多個比較表達式, 我們將使用OR和AND表達式運算符
# movies.filter('produced_year >= 2000' and length('movie_title') < 5).show(5)

# # 另一種實現相同結果的方法是調用filter函數兩次
movies.filter('produced_year >= 2000').filter(length('movie_title') < 5).show(5)

# 4)distinct, dropDuplicates
# movies.select("movie_title").distinct().selectExpr("count(movie_title) as movies").show()
movies.dropDuplicates(["movie_title"]).selectExpr("count(movie_title) as movies").show()

# 5)sort(columns), orderBy(columns)
# movieTitles = movies.dropDuplicates(["movie_title"]) \
#                     .selectExpr("movie_title", "length(movie_title) as title_length")
# movieTitles.show(5)
                                                                
# movieTitles.sort('title_length').show(15) # 默認是升序

# movieTitles.sort(col('title_length').desc()).show(15,False)  # 降序
movieTitles.sort(desc('title_length')).show(15,False)       # 同上,降序
                                                                
# 降序
# movieTitles.orderBy(col('title_length').desc()).show(5)
                                                                
# 按不同的順序對兩列進行排序
# movieTitles.orderBy(col('title_length').desc(), 'produced_year').show(5)

# 6)limit(n)
# 首先創建一個帶有演員名字及名字長度的DataFrame
actorNameDF = movies.select("actor_name") \
                    .distinct() \
                    .selectExpr("actor_name", "length(actor_name) as length")
# actorNameDF.show(5)         
    
# 按長度對名字排序,並獲得top 10
# actorNameDF.orderBy(col('length').desc()).limit(10).show(truncate=False)
actorNameDF.orderBy(desc('length')).limit(10).show(truncate=False)

from pyspark.sql.types import Row

# 7)union(otherDataFrame)
# 我們想在標題爲“12”的電影中添加一個缺失的演員,
# shortNameMovieDF = movies.where('movie_title == "12"')
# shortNameMovieDF.show()

# # 用一行創建一個DataFrame
forgottenActor = [Row("Brychta, Edita", "12", 2007)]
forgottenActorRDD = spark.sparkContext.parallelize(forgottenActor)     #  生成RDD

forgottenActorDF = spark.createDataFrame(forgottenActorRDD, shortNameMovieDF.schema)
forgottenActorDF.show()

# # 現在添加缺失的演員姓名
completeShortNameMovieDF = shortNameMovieDF.union(forgottenActorDF)
completeShortNameMovieDF.show()

# 8)withColumn(colName, column):向DataFrame增加一個新的列
# 增加一個新列,基於某一列表達式
# movies.withColumn("decade", (col('produced_year') - col('produced_year') % 10)).show(5)

# 現在用新值替換produced_year
movies.withColumn("produced_year", (col('produced_year') - col('produced_year') % 10)).show(5)

# 9)withColumnRenamed(existingColName, newColName):修改列名
movies.withColumnRenamed("actor_name", "actor") \
      .withColumnRenamed("movie_title", "title") \
      .withColumnRenamed("produced_year", "year") \
      .show(5)

# 10)drop(columnName1, columnName2):刪除指定的列
movies.drop("actor_name", "me").printSchema()

movies.drop("actor_name", "me").show(5)

# 11)sample(fraction), sample(fraction, seed), sample(fraction, seed, withReplacement)
# 帶有無放回和fraction的抽樣
movies.sample(False, 0.0003).show(3)

# 帶有有放回和fraction、seed的抽樣
movies.sample(True, 0.0003, 123456).show(3)

# 12)randomSplit(weights):隨機分割數據集
# 權重需要是一個Array
smallerMovieDFs = movies.randomSplit([0.6, 0.3, 0.1])
type(smallerMovieDFs)
type(smallerMovieDFs[0])

# 看看各部分計數之和是否等於1
t1 = movies.count()           # 31393
print(t1)

t2 = smallerMovieDFs[0].count() # 18881
print(t2)

t3 = smallerMovieDFs[0].count() + smallerMovieDFs[1].count() + smallerMovieDFs[2].count() # 31393
print(t3)

-- spark.stop() #關閉作業
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章