赞
踩
仅供学习交流使用
本关任务:将出租车轨迹数据规整化,清洗掉多余的字符串。
相关知识
为了完成本关任务,你需要掌握: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种。
字符 描述
在右侧编辑器补充代码,将出租车轨迹数据规整化,清洗掉多余的字符串,并使用 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()
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。