企业spark案例 —— 出租车轨迹分析(1)

企业spark案例 —— 出租车轨迹分析

仅供学习交流使用

任务描述

本关任务:将出租车轨迹数据规整化,清洗掉多余的字符串。
相关知识

为了完成本关任务,你需要掌握:1.如何使用 SparkSQL 读取 CSV 文件,2.如何使用正则表达式清洗掉多余字符串。
SparkSQL 读取 CSV

val spark = SparkSession.builder().appName("Step1").master("local").getOrCreate()
spark.read.option("header", true).option("delimiter", "CSV分隔符").csv("文件存储的位置")

option 参数说明:

header 为 true : 将 CSV 第一行数据作为头部信息,换一句来说,就是将 CSV 的第一行数据作为 SparkSQL 表的字段

delimiter : 分隔符,例如,CSV 文件默认以英文逗号进行字段分隔,那么 delimiter 为英文逗号,如果文件以分号进行字段分隔,那么 delimiter 为分号

SparkSQL 自定义UDF函数

用户定义函数(User-defined functions,UDFs)是大多数SQL环境的关键特性,用于扩展系统的内置功能。UDF允许开发人员通过抽象其低级语言实现来在更高级语言(如SQL)中启用新功能。 Apache Spark也不例外,并且提供了用于将UDF与Spark SQL工作流集成的各种选项。

UDF对表中的单行进行转换,以便为每行生成单个对应的输出值。例如,大多数 SQL环境提供UPPER函数返回作为输入提供的字符串的大写版本。

用户自定义函数可以在Spark SQL中定义和注册为UDF,并且可以关联别名,这个别名可以在后面的SQL查询中使用。作为一个简单的示例,我们将定义一个 UDF来将以下JSON数据中的温度从摄氏度(degrees Celsius)转换为华氏度(degrees Fahrenheit):

{"city":"St. John's","avgHigh":8.7,"avgLow":0.6}
{"city":"Charlottetown","avgHigh":9.7,"avgLow":0.9}
{"city":"Halifax","avgHigh":11.0,"avgLow":1.6}
{"city":"Fredericton","avgHigh":11.2,"avgLow":-0.5}
{"city":"Quebec","avgHigh":9.0,"avgLow":-1.0}
{"city":"Montreal","avgHigh":11.1,"avgLow":1.4}
...

scala:

val df = sqlContext.read.json("temperatures.json")
df.registerTempTable("citytemps")
// Register the UDF with our SQLContext
sqlContext.udf.register("CTOF", (degreesCelcius: Double) => ((degreesCelcius * 9.0 / 5.0) + 32.0))
sqlContext.sql("SELECT city, CTOF(avgLow) AS avgLowF, CTOF(avgHigh) AS avgHighF FROM citytemps").show()

java:

DataFrame df = sqlContext.read().json("temperatures.json");
df.registerTempTable("citytemps");
// Register the UDF with our SQLContext
sqlContext.udf().register("CTOF", new UDF1<Double, Double>() {
  @Override
  public Double call(Double degreesCelcius) {
    return ((degreesCelcius * 9.0 / 5.0) + 32.0);
  }
}, DataTypes.DoubleType);
sqlContext.sql("SELECT city, CTOF(avgLow) AS avgLowF, CTOF(avgHigh) AS avgHighF FROM citytemps").show();

python:

df = sqlContext.read.json("temperatures.json")
df.registerTempTable("citytemps")
# Register the UDF with our SQLContext
sqlContext.registerFunction("CTOF", lambda degreesCelsius: ((degreesCelsius * 9.0 / 5.0) + 32.0))
sqlContext.sql("SELECT city, CTOF(avgLow) AS avgLowF, CTOF(avgHigh) AS avgHighF FROM citytemps").show()

为什么使用正则表达式?

典型的搜索和替换操作要求您提供与预期的搜索结果匹配的确切文本。虽然这种技术对于对静态文本执行简单搜索和替换任务可能已经足够了,但它缺乏灵活性,若采用这种方法搜索动态文本,即使不是不可能,至少也会变得很困难。

比如说判断邮箱格式是否正确、手机号格式是否正确,这种需求的话,如果不使用正则匹配的话,那么就需要写很多逻辑进行 equals 操作,想一想都很麻烦,麻烦的原因是 equals 操作只能确切匹配,缺乏灵活度,而正则就不同了,正则可以使用限定符,匹配字符出现的次数,这样一来灵活度都高了。
正则表达式的限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。
字符 描述

  • 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
  • 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
    ? 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1}。
    {n} n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
    {n,} n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。
    {n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。
    编程要求

在右侧编辑器补充代码,将出租车轨迹数据规整化,清洗掉多余的字符串,并使用 DataFrame.show() 打印输出。

CSV文件内容如下:

清洗掉红框里面的 $ 、@ 字符,由于这两字符出现的次数没有规律,所以需要使用正则匹配。

清洗后内容如下:

特别说明:本案例的 CSV 文件是以 \t 进行字段分隔,文件路径为 /root/data.csv
测试说明

平台将对你编写的代码进行评测:

预期输出:

+-------------------+---------+-----------+--------+------------+----------+--------------------+
|            TRIP_ID|CALL_TYPE|ORIGIN_CALL| TAXI_ID|ORIGIN_STAND| TIMESTAMP|            POLYLINE|
+-------------------+---------+-----------+--------+------------+----------+--------------------+
|1372636858620000589|        C|       null|20000589|        null|1372636858|[[-8.618643,41.14...|
|1372637303620000596|        B|       null|20000596|           7|1372637303|[[-8.639847,41.15...|
|1372636951620000320|        C|       null|20000320|        null|1372636951|[[-8.612964,41.14...|
|1372636854620000520|        C|       null|20000520|        null|1372636854|[[-8.574678,41.15...|
|1372637091620000337|        C|       null|20000337|        null|1372637091|[[-8.645994,41.18...|
|1372636965620000231|        C|       null|20000231|        null|1372636965|[[-8.615502,41.14...|
|1372637210620000456|        C|       null|20000456|        null|1372637210|[[-8.57952,41.145...|
|1372637299620000011|        C|       null|20000011|        null|1372637299|[[-8.617563,41.14...|
|1372637274620000403|        C|       null|20000403|        null|1372637274|[[-8.611794,41.14...|
|1372637905620000320|        C|       null|20000320|        null|1372637905|[[-8.615907,41.14...|
|1372636875620000233|        C|       null|20000233|        null|1372636875|[[-8.619894,41.14...|
|1372637984620000520|        C|       null|20000520|        null|1372637984|[[-8.56242,41.168...|
|1372637343620000571|        A|      31508|20000571|        null|1372637343|[[-8.618868,41.15...|
|1372638595620000233|        C|       null|20000233|        null|1372638595|[[-8.608716,41.15...|
|1372638151620000231|        C|       null|20000231|        null|1372638151|[[-8.612208,41.14...|
|1372637610620000497|        B|       null|20000497|          13|1372637610|[[-8.585145,41.16...|
|1372638481620000403|        B|       null|20000403|          28|1372638481|[[-8.584263,41.16...|
|1372639135620000570|        A|      33180|20000570|        null|1372639135|[[-8.666757,41.17...|
|1372637482620000005|        C|       null|20000005|        null|1372637482|[[-8.599239,41.14...|
|1372639181620000089|        C|       null|20000089|        null|1372639181|[[-8.643807,41.16...|
+-------------------+---------+-----------+--------+------------+----------+--------------------+
only showing top 20 rows

开始你的任务吧,祝你成功!

参考代码

import org.apache.spark.sql.SparkSession



object Step1 {
    def main(args: Array[String]): Unit = {
        val spark = SparkSession.builder().appName("Step1").master("local").getOrCreate()

        /**********begin**********/
        val frame = spark.read.option("header", true).option("delimiter", "\t").csv("/root/data.csv")
        frame.createTempView("data")
        spark.udf.register("cleanData", (x: String) => {
            x.replaceAll("\\@+", "").replaceAll("\\$+", "")
        })
        spark.sql(
        """
            |select cleanData(TRIP_ID) as TRIP_ID,cleanData(CALL_TYPE) as CALL_TYPE,cleanData(ORIGIN_CALL) as ORIGIN_CALL,
            |cleanData(TAXI_ID) as TAXI_ID,cleanData(ORIGIN_STAND) as ORIGIN_STAND ,cleanData(TIMESTAMP) as TIMESTAMP,
            |cleanData(POLYLINE) as POLYLINE
            |from data
        """.stripMargin).show()
        /**********end**********/
        spark.stop()
    }
}
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值