小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
接前文,同样查询祖孙关系,这次我们使用DataFrame 来完成这个题目。
还是使用原来的那个文件(以空格分割)。
Spark的DataFrame时一种和Pandas 的DataFrame类似的API。
也就是说它为了我们提供的许多非常好用的读取结构化数据的方法。
在Pandas中我们可以通过使用readcsv来读取数据获得一个python的DataFrame对象,像下面这样
import pandas as pd
pd.read_csv("somefile")
在spark中,我们也会有类似的操作。
像下面这样
val session = SparkSession.builder().appName("关联").getOrCreate()
val table = session.read.option("sep", " ").csv("hdfs://master:8020/user/hdfs/bigdata9/table.txt")
值得注意的地方有两处,一处是我们使用spark DataFrame API需要引入一个新的对象 SparkSession 而不再是原来的那些对象。
还有一处是指定分割符号的地方,csv 默认是以逗号作为分割符号,而这里我们实际上并不是以逗号分隔,我们需要通过 .option 函数像上面那样设置。
Spark DataFrame 与python的Data Frame 还有个不同的地方在于它是不可变 的,这点在稍后会用到。
像上面那样读取的数据实际上是没有指定列名称的,默认情况下Spark 会把它们设置成 _c0 , _c1 这种的列名称。修改列名称可以使用如下的方法(在创建的时候顺便修改):
table=session.read .... withColumnRenamed("parent", "grandparent")
.withColumnRenamed("child", "parent")
也就是在先前创建DataFrame的时候加两个链式调用。
为了找出我们表中的祖孙关系,我们可以使用DataFrame 提供的join操作。
为了更简便的操作我们可以利用上面说过的它的不可变的特性,把table 作为另一个表并修改下列信息即可。
val tableCopy = table.as("table2")
.withColumnRenamed("parent", "grandparent")
.withColumnRenamed("child", "parent")
我们现在有了两个表,并且很显然可以看出parent就是我们用来连接的字段。那么我们只需要像下面这样,就可以得到查询结果了
table.join(tableCopy, "parent")
.select("child", "grandparent")
.show()
结果如下:
+------+-----------+
| child|grandparent|
+------+-----------+
|Steven| Frank|
|Steven| Mary|
|Steven| Jesse|
|Steven| Alice|
| Jone| Frank|
| Jone| Mary|
| Jone| Jesse|
| Jone| Alice|
|Philip| Jesse|
|Philip| Alice|
| Mark| Jesse|
| Mark| Alice|
+------+-----------+
小结:
DataFrame 比起RDD显著降低了我们的心智负担,提供了与Pandas类似的一种近似SQL的数据处理办法,让我们轻松的就能完成许多在RDD 不能轻易实现的操作。