spark+dataframe+小汽车摇号倍率与中签率分析

  1.  实验环境

Ubuntu 16.04 - VMware Workstation 16 Pro

java - openjdk version “1.8.0_292”

Scala code runner version 2.11.8

Spark 2.1.0

Hadoop 2.7.1

  1. 实验背景
    1. 背景

为了限制机动车保有量,2011年北京市推出了小汽车摇号政策。在2016年,为了照顾那些长时间没有摇中号码牌的申请者,摇号政策又推出了“倍率制度”,根据参与摇号的次数为每个人赋予不同的倍率系数。这样,大家的中签率就由原来的整齐划一的基础概率,变为了“基础概率*倍率系数”。

    1. 数据介绍

2011-2019北京市小汽车摇号数据下载地址:百度网盘 请输入提取码 ,提取码ajs6

目录下有apply和lucky两个目录,前者是申请记录,后者是中签记录,数据文件为Parquet。里面的每条记录包含两个字段:String类型的carNum,以及Int型的batchNum。 在实现过程中,所谓的倍率其实就是增加申请号码的副本数量

  1. 问题A
    1. 任务要求

大多数人反馈,即使背了8倍,10倍的倍率,照样摇不上。为了验证加倍率的方式是否能提高中签率,请大家用spark SQL(基于DataFrame)分析摇号和中签的历史数据,定量分析:倍率和中签率之间有何关系,判断当前加倍率的方案是否合理,如何分析,方案自定。(提示:关注中签者的倍率变化

    1. 实验分析

由于倍率制度是在2016年之后推出的,分析数据时我们只需要关注2016年之后的倍率和中签率之间的关系。由于每个倍率申请者基数不一致,我们并不能简单地使用每个倍率中签者数量除以中签者总数,所以我们采取的方法是观察每个倍率中签者在该倍率基数中的比例。

    1. 操作与代码实现

打开一个终端,开启hadoop:

 

将本地数据放入hdfs中:

 

可以看到hdfs目录中多了car-num-dataset一项,用命令打印car-num-dataset文件夹内容,可以看到数据已经正常传入:

 

开启spark:

 

引用头文件,使用spark sql DataFrame

import org.apache.spark.sql.DataFrame

import spark.implicits._

分别读入两个parquet文件apply和lucky,生成DataFrame

val applyNumbersDF: DataFrame = spark.read.parquet("hdfs://localhost:9000/user/hadoop/car-num-dataset/apply")

val luckyNumbersDF: DataFrame = spark.read.parquet("hdfs://localhost:9000/user/hadoop/car-num-dataset/lucky")

applyNumbersDF和luckyNumbersDF格式为:

   

对applyNumbersDF使用filter函数过滤出batchNum在2016年之后的数据,并以carNum编号和batchNum摇号日期使用groupBy函数聚合并使用count函数得出申请者的倍率。最后将使用count函数默认得到的列名“count”用withColumnRenamed函数改为“applyNum”,方便之后操作。

val applyCount = applyNumbersDF.filter(applyNumbersDF("batchNum")>201600).groupBy("carNum","batchNum").count().withColumnRenamed("count","applyNum")

applyCount数据格式:

 

对luckyNumbersDF使用filter函数过滤出batchNum在2016年之后的数据,使用join函数将其与applyCount中得到的倍率表聚合,这一步相当于luckyNumbersDF在applyCount表中查找每一项的applyNum的值写入表中。

val luckyCount = luckyNumbersDF.filter(luckyNumbersDF("batchNum")>201600).join(applyCount,Seq("carNum","batchNum"))

luckyCount数据格式:

 

对applyCount使用groupyBy函数count函数的联合方式对“applyNum”每个倍率的申请者数目进行统计,并使用sort函数升序排列。对luckyCount以同样的方式对每个倍率的中签者数目进行统计,并将count函数默认的列明改为“luckyNum”。

val applyNum = applyCount.groupBy("applyNum").count().sort(applyCount("applyNum").asc)

val luckyNum = luckyCount.groupBy("applyNum").count().sort(luckyCount("applyNum").asc).withColumnRenamed("count","luckyNum")

applyNum和luckyNum的数据:

    

 

 

这里我们可以发现,倍率为13的申请者由于基数太少中签率低的原因,并没有中签者产生。

为了方便调试和使之后的计算不必重复,我们将applyNum和luckyNum在这里进行存储:

applyNum.write.parquet("hdfs://localhost:9000/user/hadoop/applyNum.parquet")

luckyNum.write.parquet("hdfs://localhost:9000/user/hadoop/luckNum.parquet")

加载存储的applyNum和luckyNum数据

val applyNum: DataFrame = spark.read.parquet("hdfs://localhost:9000/user/hadoop/applyNum.parquet")

val luckyNum: DataFrame = spark.read.parquet("hdfs://localhost:9000/user/hadoop/luckNum.parquet")

使用join函数将两个表格合并,并使用withColumn函数生成新列”rate”,用原luckyNum中每个倍率的”luckyNum”中签数除以原applyNum中每个倍率的”count”申请总数得到每个倍率的中签率。

val rate1 = luckyNum.join(applyNum,Seq("applyNum"))

val rate = rate1.withColumn("rate", rate1("luckyNum")/rate1("count"))

 

可以观察到倍率从1增加到9,中签率在不断上升,倍率到9之后继续增加,中签率有所下降,这有可能是高倍率的申请者基数过少导致的。

通过对倍率和中签率的分析,在倍率低时中签率呈拟线性增加,当倍率达到一定时,中签率有所下降。说明该倍率机制在低倍率中表现良好,但是高倍率的申请者由于基数过少、倍率增幅不大的原因没有体现出优势,存在一定的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值