前几天在做大数据的期中作业,老师给了我们一堆表(几十kb到几百mb不等),要我们去分析一点东西出来写成报告。我本以为要用自己的电脑做,正准备去装环境,老师突然说可以把他们实验室的容器暴露给我们, 诶嘿,不用配环境了,舒服呀~
好了,不多废话,下面进入正题。

1、spark与pyspark

spark是在内存上进行计算的,效率非常高。spark的编程语言是scala,pyspark的编程语言是python,pyspark程序需要通过py4j调用java的方法,具体原理可参考文章:PySpark的背后原理 我所使用的pyspark为1.x版本。

2、DataFrame与RDD

pyspark中定义了一个DataFrame数据类型,它与python中的DataFrame是两个截然不同的数据类型,之后我提到的DataFrame都是指代前者。关于RDD我就不多说了,只是我在使用pyspark过程中几乎都是与它们俩在打交道,而且在这过程中我发现一个困扰我至今的问题,所以我想稍微提一下:
按理来讲,DataFrame对于结构型数据的处理性能是要优于RDD许多的,但是在我所使用的pyspark版本中,DataFrame竟然没有类似RDD.map()的属性。当我想要对DataFrame里的元素进行遍历操作时,我就不得不将它转换为RDD,再通过RDD.map()进行操作。也许它有这样的函数,只是我没找到?

3、简单的数据操作

必要的准备工作:

#创建SparkSession对象
import pyspark
from pyspark.sql import SparkSession
spark=SparkSession \
.builder \
.appName('hello_pyspark') \
.getOrCreate()

读取.csv格式的文件,使用spark.read.csv(filepath,header=True,inferSchema=True)函数,返回一个DataFrame类型:

content = spark.read.csv('/notebooks/data/content.csv',header=True,inferSchema=True)

我们可以将DataFrame注册为一张临时表(注意:只在pyspark1.x版本里有效,在2.0以上版本请参考官方文档给出的新函数),使用DataFrame.registerTempTable(TempTableName)函数。

content.registerTempTable("contentTable")

通过DataFrame.show()函数,显示前20行数据:

content.show()

运行结果的部分截图:

spark set 语法 spark使用的语言_大数据


基础的准备工作到这里就算完成了,下面说说如何对数据进行操作。比如我想将Length列取出并将里面的所有元素转换为int类型(“用时3小时25分” => 205),该怎么做呢?

我的做法是:

第一步,使用sql语句从表中取出Length列,得到一个DataFrame对象。

LengthDF = spark.sql("select Length from contentTable")
LengthDF.show()

结果:

spark set 语法 spark使用的语言_python_02


将DataFrame对象转换成RDD对象:

LengthRDD = LengthDF.rdd
LengthRDD.take(20) #取LengthRDD的前20行数据并输出

spark set 语法 spark使用的语言_spark set 语法_03


这里就要注意了,执行rdd.map(func)时,pyspark会把rdd的每一个元素作为func函数的输入,把输出作为新的rdd的元素,也就是说对原来rdd的每一个元素进行一次变换,返回一个每一个元素变换后得到的新的rdd。那么显然我们还需要写一个用来转换的函数:

import re
def func(row):
    #rdd的每一个元素都是Row类型的,需要将其强转为String类型
    #如Row(Length='用时25分')被强转为"Row(Length='用时25分')"
    row = str(row) 
    #使用正则表达式取出小时数和分钟数的字段,并转换为int类型.这里有两种pattern.
    patternA = re.compile(r"用时(.+)小时(.+)分")
    patternB = re.compile(r"用时(.+)分")
    if re.search(patternA,row): 
    #我使用的python版本较低,不能在if表达式中赋值,这样会增加时间复杂度
        result = re.search(patternA,row)
        hour = int(result.group(1),10) #第二个参数表示转换为10进制数
        minu = int(result.group(2),10)
        time = hour*60 + minu
    elif re.search(patternB,row):
        time = int(re.search(patternB,row).group(1),10)
    else:
        time = -1
    return time

这个函数接受一个Row类型的值作为输入,返回一个int值。接下来,我们执行map操作:

l = LengthRDD.map(func)
l.take(20)

spark set 语法 spark使用的语言_python_04


OK,满足预期结果~