一、问题
在爬取到双色球开奖的历史数据和开奖当日20-22点的气候数据之后,我们面临的问题是:
- 选择什么样的算法寻找天气数据和双色球开奖结果的关系(只选择蓝球)
- 为实现1的算法,需对数据做什么样的处理?
- 选择什么技术实现模型训练
首先,将问题简化。若只考虑天气(气温,露点,气压,风向,风速,天气情况)和双色球开奖结果蓝球(1-16)之间的关系。这就是一个多分类问题。模型的计算可选择两种方向:
- Python 实现的 TensorFlow
- Scala 实现的Spark MLlib
TensorFlow 是谷歌开源的Python 实现的包,TensorFlow项目领导Rajat Monga说到:“TensorFlow对于AlphaGo来说更多的是底层支撑技术,我们的作用是让AlphaGo运作更顺畅。而如何提升棋艺则是Deepmind团队的工作”。所以TensorFlow 是一个很好的选择。
Spark MLlib是基于Scala 实现的在Hadoop 的一站式大数据平台。所以Spark MLlib也是不错的选择。
由于一、二都是基于Scala 实现,所以我们选择方案二。
二、实现
1. 数据清洗
从双色球开奖的历史数据中,包含的列有
{ 期号,红球序列,蓝球,奖池金额,一等奖金额 }
数据格式:
(String, String, String, String,String)
样例数据:
对数据进行清洗,并实数化
def clean2ColorBall:Map[String,List[Int]] = {
val data = Source.fromFile("/Users/hhl/mypro/SparkAppExamples/Hello.txt").getLines().filter(!_.contains("16115期"))
.map(_.split(";")).map(x=>{
val qh = x(0).substring(0,7)
val date = x(1).replace("-","")
val r1 = x(2).toInt
val r2 = x(3).toInt
val r3= x(4).toInt
val r4 = x(5).toInt
val r5 = x(6).toInt
val r6 = x(7).toInt
val b7 = x(8).toInt
val saleroom = x(9).replace("元","").replace(",","").trim.toInt
val jackpot = x(10).replace("元","").replace(",","").trim.toInt
val res = List(r1,r2,r3,r4,r5,r6,b7,saleroom,jackpot)
date -> res
}).toMap
data
}
- feature 的选择和准备
选择
{气温,露点,气压,风向,风速,天气情况,奖池金额}
作为Feature,按照下列逻辑做数据清洗,并转化为DataFrame,存储为Parquet.
def cleanWx(spark:SparkSession) = {
val m = clean2ColorBall
val data = Source.fromFile("/Users/hhl/mypro/SparkAppExamples/wx.txt").getLines()
.map(_.split(";")).filter(_.size !=1)
.map(x=>{
// 气温,露点,湿度,气压,风向,风速,状况 6个feature
val date = x(0)
val time = x(1).replace("PM","").trim
val qw = x(2).toDouble
val ld = x(3).toDouble
val sd = x(4).replace("%","").toDouble
val qy = x(5).toDouble
val fx = x(6)
val fs = x(7).toDouble
val zk = x(8)
(date,time,qw,ld,sd,qy,fx,fs,zk)
}).filter(_._6 != -1).toList
// 归一化,实数化
val fxList = data.map(_._7).distinct.zipWithIndex.toMap
val zkList = data.map(_._9).distinct.zipWithIndex.toMap
val res = data.map(x=>(x._1,x._2,x._3,x._4,x._5,x._6,
fxList.getOrElse(x._7,-1).toDouble,x._8,zkList.getOrElse(x._9,-1).toDouble)).groupBy(_._1)
.map(x=>x._2.sortBy(_._2).head).toList
val res1 = res.sortBy(x=>(x._1,x._2)).map(x=>{
val lq = m.getOrElse(x._1,List(-1,-1,-1,-1,-1,-1,-1))(6).toDouble
val jc = m.getOrElse(x._1,List(-1,-1,-1,-1,-1,-1,-1))(7).toDouble
val values = Array(x._3 + 100 ,x._4 + 100,x._5,x._6,x._7,x._8,x._9,jc) // naive beyes 不支持负值
val featureVecotr =Vectors.dense(values.init)
val lable = lq
(lable,featureVecotr)
})
import spark.implicits._
val df = spark.createDataset(res1).repartition(1).toDF("label","features")
df.write.mode(SaveMode.Overwrite).parquet("/Users/hhl/mypro/SparkAppExamples/res.txt")
//df.write.format("csv").mode(SaveMode.Overwrite).save("/Users/hhl/mypro/SparkAppExamples/res.txt")
}
- 模型训练和结果预测
选择朴素贝叶斯算法训练模型,并验证模型预测的准确度
val data = spark.read.parquet("/Users/hhl/mypro/SparkAppExamples/res.txt")
// Split the data into training and test sets (30% held out for testing)
val Array(trainingData, testData) = data.randomSplit(Array(0.7, 0.3), seed = 1234L)
// Train a NaiveBayes model.
val model = new NaiveBayes()
.fit(trainingData)
// Select example rows to display.
val predictions = model.transform(testData)
predictions.show(100)
// Select (prediction, true label) and compute test error
val evaluator = new MulticlassClassificationEvaluator()
.setLabelCol("label")
.setPredictionCol("prediction")
.setMetricName("accuracy")
val accuracy = evaluator.evaluate(predictions)
println("Test set accuracy = " + accuracy)
-
准确度
对测试数据的预测结果:
准确度:
只有 8%。。。。
四、总结
根据峰值运动预测双色球走势的流程基本上结束了。最终的结果如一盆冷水,透心凉。想通过技术手段赚大钱的愿望再次破灭了。不过,却有几点重要的经验:
- 如何编写一个并发的反反爬虫的Scala 爬虫
- 如何进行数据清洗
- 如何使用朴素贝叶斯训练模型
由此,又引起新的问题,为什么准确度这么低?如何提高准确度呢?这也许才是这个简单的Demo最大的收获。知道自己不知道,明确了方向,才能更进一步。