Spark-DataFrame

Spark早期的API中(即RDD),由於Java JVM和Py4J之間的通信,每當使用RDD執行PySpark程序時,潛在地需要巨大的開銷來執行作業。

DataFrame和Catalyst優化器(以及Tungsten項目)的意義是在和非優化的RDD查詢比較時增加PySpark查詢的性能。

使用DataFrame,過去不僅有明顯的Python性能改進,現在還有Python、Scale、SQL和R之間的性能校驗。

通過RDD轉換創建DataFrame

首先創建我們的jsonRDD:

jsonRDD = sc.textFile(r'C:\Users\Guitar\Desktop\json.txt',2)

json.txt文件中我們按行存放了一些格式化的json字符串:

{"title": "第1章 黃山真君和九洲一號羣","link": "http://m.baidu.com/tc?appui=alaxs&gid=3965456126&tn=utouch&page=ct&url=http%3A%2F%2Fwww.uukanshu.com%2Fb%2F33933%2F55150.html#2","totalpage": 0,"partsize": 0,"order": 0,"currency": 0,"unreadble": false,"isVip": false}
{"title": "第2章 且待本尊算上一卦","link": "http://m.baidu.com/tc?appui=alaxs&gid=3965456126&tn=utouch&page=ct&url=http%3A%2F%2Fwww.92txt.net%2Fbook%2F25%2F25882%2F10074048.html#2","totalpage": 0,"partsize": 0,"order": 0,"currency": 0,"unreadble": false,"isVip": false}
{"title": "第3章 一張丹方","link": "http://m.baidu.com/tc?appui=alaxs&gid=3965456126&tn=utouch&page=ct&url=http%3A%2F%2Fwww.92txt.net%2Fbook%2F25%2F25882%2F10074053.html#2","totalpage": 0,"partsize": 0,"order": 0,"currency": 0,"unreadble": false,"isVip": false}
...

創建 DataFrame

現在,我們已經創建了RDD,利用SparkSession的read.json方法,RDD將會被轉換成一個DataFrame,以下是創建DataFrame的代碼:

xs_chapter = spark.read.json(jsonRDD)

創建一個臨時表

利用DataFrame的.createOrReplaceTempView方法創建一個臨時視圖表:

xs_chapter.createOrReplaceTempView("xs_chapter")
  • 創建臨時表是一次DataFrame轉換,我們可以通過臨時表用SQL查詢DataFrame中的數據。

簡單的DataFrame查詢

show()

運行.show方法默認顯示前10行,當然也可以傳入參數n來控制顯示的行數。

+--------+-----+--------------------+-----+--------+--------------+---------+---------+
|currency|isVip|                link|order|partsize|         title|totalpage|unreadble|
+--------+-----+--------------------+-----+--------+--------------+---------+---------+
|       0|false|http://m.baidu.co...|    0|       0|第1章 黃山真君和九洲一號羣|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|  第2章 且待本尊算上一卦|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|      第3章 一張丹方|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|  第4章 h市三品後天雷劫|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|    第5章 要相信科學!|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|      第6章 銅卦仙師|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|   第7章 被羣滅的不良們|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|  第8章 羽柔子和羅信街區|        0|    false|
|       0|false|http://m.baidu.co...|    0|       0|   第9章 另一個羅信街區|        0|    false|
+--------+-----+--------------------+-----+--------+--------------+---------+---------+

SQL查詢

spark.sql("select isVip,partsize,title from xs_chapter").take(3)
# Out: 
[Row(isVip=False, partsize=0, title='第1章 黃山真君和九洲一號羣'),
 Row(isVip=False, partsize=0, title='第2章 且待本尊算上一卦'),
 Row(isVip=False, partsize=0, title='第3章 一張丹方')]

RDD的交互操作

事實上有兩種從RDD變換到DataFrame(或者Dataset[T])的不同方法:使用反射推斷模式或以編程方式指定模式。

  • 模式可以理解爲DataFrame字段的數據類型

使用反射來推斷模式

在建立DataFrame和運行查詢的過程中,DataFrame的模式是自動定義的。

最初,行對象通過傳遞一列鍵/值對作爲行類的**kwargs來構造。然後,SparkSQL將行對象的RDD轉變爲一個DataFrame,在DataFrame中鍵就是列,數據類型通過採樣數據來推斷。

利用DataFrame的printSchema方法查看模式的定義:

xs_chapter.printSchema()
root
 |-- currency: long (nullable = true)
 |-- isVip: boolean (nullable = true)
 |-- link: string (nullable = true)
 |-- order: long (nullable = true)
 |-- partsize: long (nullable = true)
 |-- title: string (nullable = true)
 |-- totalpage: long (nullable = true)
 |-- unreadble: boolean (nullable = true)

假設某個字段的類型不是我們預期的,該如何調整?

編程指定模式

我們通過在SparkSQL中引入數據類型(pyspark.sql.types),以編程方式來指定模式,並生成一些.csv數據,如下例所示:

# 導入數據類型
from pyspark.sql.types import *

# 生成逗號分隔的數據
csvRDD = sc.parallelize([(123,'張三',18,'游泳'),(456,'李四',19,'游泳'),(789,'小王',23,'游泳')])

csvRDD.take(3)
Out[33]: [(123, '張三', 18, '游泳'), (456, '李四', 19, '游泳'), (789, '小王', 23, '游泳')]

# 指定對應字段的模式(數據類型)
schema = StructType([StructField('id',LongType(),True),StructField('name',StringType(),True),StructField('age',LongType(),True),StructField('hobby',StringType(),True)])

# 創建DataFrame
swimmers = spark.createDataFrame(csvRDD,schema)

# 創建臨時表
swimmers.createOrReplaceTempView('swimmers')

swimmers.printSchema()
root
 |-- id: long (nullable = true)
 |-- name: string (nullable = true)
 |-- age: long (nullable = true)
 |-- hobby: string (nullable = true)

swimmers.show()
+---+----+---+-----+
| id|name|age|hobby|
+---+----+---+-----+
|123|  張三| 18|   游泳|
|456|  李四| 19|   游泳|
|789|  小王| 23|   游泳|
+---+----+---+-----+

使用DataFrame API查詢

行數

swimmers.count()
# Out: 3

篩選

swimmers.select('id','name','age').filter('age=18').show()
+---+----+---+
| id|name|age|
+---+----+---+
|123|  張三| 18|
+---+----+---+

swimmers.select(swimmers.id,swimmers.name,swimmers.age).filter(swimmers.age==18).show()
+---+----+---+
| id|name|age|
+---+----+---+
|123|  張三| 18|
+---+----+---+

swimmers.select('id','name','age').filter('age like "1%"').show()
+---+----+---+
| id|name|age|
+---+----+---+
|123|  張三| 18|
|456|  李四| 19|
+---+----+---+

使用SQL查詢

如上文,我們可以使用SparkSession的sql方法查詢swimmers,前提是針對swimmers建立了臨時視圖表

行數

spark.sql('select count(*) from swimmers').show()
+--------+
|count(1)|
+--------+
|       3|
+--------+

篩選

spark.sql('select * from swimmers where age>18').show()
+---+----+---+-----+
| id|name|age|hobby|
+---+----+---+-----+
|456|  李四| 19|   游泳|
|789|  小王| 23|   游泳|
+---+----+---+-----+

spark.sql('select * from swimmers where age like "1%"').show()
+---+----+---+-----+
| id|name|age|hobby|
+---+----+---+-----+
|123|  張三| 18|   游泳|
|456|  李四| 19|   游泳|
+---+----+---+-----+

結語

使用Spark DataFrame,Python開發人員可以利用一個簡單的並且潛在地加快速度的抽象層。最初Spark中的Python速度較慢的一個主要原因源自於Python子進程和JVM之間的通信層。對於Python DataFrame的用戶,我們有一個在Scala DataFrame周圍的Python包裝器,Scala DataFrame避免了Python子進程/JVM的通信開銷。

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