这个demo本来是在一个月以前做的,用来完成大数据课程的小作业,结果老师发了一个更详细的要求做结课考试,导致这个demo和课程的要求产生了一些偏差。刚好我最近在自学GO语言所以昨天就把本来用Java写的代码用Go重写,主要目的是拿来练手检验一下自学成果。
按照老师的要求,这个demo实现的主要内容是从一个文本文件中读取数据集并且按照贝叶斯分类器的方式对目标数据的结果进行预测。
简单的说一下公式P(C|A)=(P(A1|C)*P(A2|C)*······*P(An|c)*P(C))/(P(A1)*·······*P(An))。关于朴素贝叶斯分类器详见百度百科朴素贝叶斯
数据集来自于一书上的一个小例子,数据集结构是:“季节|天气|航空管制|航线|晚点情况” 。
3个package:models主要用于存放用于封装数据的结构体,TxtReader用于存放读取文件用到的方法,NaiveBayesian用于存放分类器相关函数。关于代码的详细解释以注释方式放在代码中间。
models包:
Massage.go实现结构体Massage用于对TxtReader读取到的数据进行封装。
MassageNum.go,MassageProbability.go里面存放的结构体和Massage相同,只不过变量类型分别为int和float32,用于计算过程中的计数和结算概率。
package models
type Massage struct {
Season string
Weather string
AirControl string
AirLine string
Delay string
}
TxtReader包:
TxtReader.go实现两个方法:TxtReader和GetMassage,TxtReader函数用于读取文件并将文件内容以作为返回值返回,GetMassage函数用于把TxtReader得到的一个个string类型的数据封装成Massage类型。
package TxtReader
import (
"fmt"
"io"
"os"
"bufio"
"strings"
"../models"
)
func TxtReader (filename string) (ret []string,lineNum int) {
var LineString []string //一个string类型的切片,用于为返回值
var count int=0 //一个int类型的切片,用于记录行数并作为返回值输出
file,err:=os.OpenFile(filename,os.O_RDONLY,0) //打开文件
if err != nil{ //如果文件正确打开,err返回值为空
fmt.Println("File open error!")
}
defer file.Close()
reader := bufio.NewReader(file) //通过file创建一个reader
for {
line,err :=reader.ReadString('\n') //按行对reader进行处理,将结果赋值给line string类型
LineString = append(LineString,line) //使用append函数把line添加到切片LineString的后面
count++ //行数增加
if err != nil || err == io.EOF{
break //如果返回的err不为空说明读取结束,break掉for循环
}
}
return LineString,count //go支持多返回值
}
func GetMassage(linString string)(ret models.Massage) {
str := strings.Fields(linString)//每次处理TxtReader得到的切片中的一个,Fields函数会把参数按照空格拆分,并返回一个string切片
var massage models.Massage //创建一个新的Massage结构的对象
for num,str2 := range str{ //使用range函数可以遍历数组或切片。range函数返回是一个key-value结构的结果,key是序号,value是内容
if num==0{
massage.Season=str2
}
if num==1{
massage.Weather=str2
}
if num==2{
massage.AirControl=str2
}
if num==3{
massage.AirLine=str2
}
if num==4{
massage.Delay=str2
}
}
return massage
}
NaiveBayesian包:
NaiveBatesianClassification.go用于把计算概率并得出结果。
package NaiveBayesian
import (
"../models"
"fmt"
)
//一个计数函数,用于得到目标信息在数据集中的个数
func GetMassageNum(massage1 models.Massage,massage2 models.Massage,massageNum models.MassageNum)(ret models.MassageNum){
//massage1是数据集内容,massage2是目标信息,如果信息相同,那么maasageNum增加1
if massage1.Season==massage2.Season{
massageNum.Season++
}
if massage1.Weather==massage2.Weather{
massageNum.Weather++
}
if massage1.AirControl == massage2.AirControl{
massageNum.AirControl++
}
if massage1.AirLine == massage2.AirLine{
massageNum.AirLine++
}
return massageNum
}
//用于计算数据集中晚点情况在“yer”和“no”两中情况下的数字,如果晚点情况和信息都相同,计数用的MassageNum加一
func GetConditionalMassageNum(massage1 models.Massage,massage2 models.Massage,CMassageNum models.MassageNum,delay string) (massageNum models.MassageNum) {
if massage1.Season==massage2.Season && massage2.Delay==delay{
CMassageNum.Season++
}
if massage1.Weather==massage2.Weather && massage2.Delay==delay{
CMassageNum.Weather++
}
if massage1.AirControl == massage2.AirControl && massage2.Delay==delay{
CMassageNum.AirControl++
}
if massage1.AirLine == massage2.AirLine && massage2.Delay==delay{
CMassageNum.AirLine++
}
if massage2.Delay == delay{
CMassageNum.Delay++
}
return CMassageNum
}
//用于计算在不考虑晚点的情况下目标信息出现的概率。也就是公式中分母上的数字
func GetProbability(massageNum models.MassageNum,LineNum int) (ret models.MassageProbaility) {
var Massageprobability models.MassageProbaility
Massageprobability.Season=float32(massageNum.Season)/float32(LineNum)//类型转换
Massageprobability.Weather=float32(massageNum.Weather)/float32(LineNum)
Massageprobability.AirControl=float32(massageNum.AirControl)/float32(LineNum)
Massageprobability.AirLine=float32(massageNum.AirLine)/float32(LineNum)
return Massageprobability
}
//用于计算在考虑晚点的抢矿西啊目标信息出现的概率(条件概率)。也就是公式中分子上的数字
func GetMassageConditionalProbability(massageNum models.MassageNum,LineNum int) (ret models.MassageProbaility) {
var Massageprobability models.MassageProbaility
Massageprobability.Season=float32(massageNum.Season)/float32(massageNum.Delay)
Massageprobability.Weather=float32(massageNum.Weather)/float32(massageNum.Delay)
Massageprobability.AirControl=float32(massageNum.AirControl)/float32(massageNum.Delay)
Massageprobability.AirLine=float32(massageNum.AirLine)/float32(massageNum.Delay)
Massageprobability.Delay =float32(massageNum.Delay)/float32(LineNum)
return Massageprobability
}
//在得到分子和分母的数字以后,计算出目标为“yes”或者“no”的概率
func GetConditionalProbability(massageProbability models.MassageProbaility,CMProbability models.MassageProbaility)(ret float32) {
var (
ConditionalProbability float32
conditionalProbability_up float32
conditionalProbability_down float32
)
conditionalProbability_up=CMProbability.Season*CMProbability.Weather*CMProbability.AirLine*CMProbability.AirControl*CMProbability.Delay
conditionalProbability_down=massageProbability.Season*massageProbability.Weather*massageProbability.AirControl*massageProbability.AirLine
ConditionalProbability=conditionalProbability_up/conditionalProbability_down
return ConditionalProbability
}
//一个方便调用的外部接口,在输入数字以后预测根据目标信息是否会晚点,返回值为bool型,如果晚点返回“true”否则返回“false”
func GetNaiveBatesianClassification(massageNum models.MassageNum,CMassageNum_yes models.MassageNum,CMassageNum_no models.MassageNum,LineNum int)(ret bool) {
Probability := GetProbability(massageNum,LineNum)//概率计算的分母
CMProbability_yes :=GetMassageConditionalProbability(CMassageNum_yes,LineNum)//晚点的分子
CMProbability_no :=GetMassageConditionalProbability(CMassageNum_no,LineNum)//不晚点的分子
var CP_YES = GetConditionalProbability(Probability,CMProbability_yes)//晚点的概率
var CP_NO = GetConditionalProbability(Probability,CMProbability_no)//不晚点的概率
fmt.Println(CP_YES)
fmt.Println(CP_NO)
if CP_YES>CP_NO{//比较哪一个概率大
return true
} else{
return false
}
}
主函数MainProgram
package main
import (
"./NaiveBayesian"
"./TxtReader"
"./models"
"fmt"
)
func main(){
var filename string //文件路径参数
filename="d:/NaiveBatesianClassification.txt"
LineString,LineNum := TxtReader.TxtReader(filename)//获得[]string切片作为GetMassage函数的参数
var massageNew models.Massage //作为目标信息的对象
massageNew.Season="Summer"
massageNew.Weather="RainyOrSnowy"
massageNew.AirLine="CA"
massageNew.AirControl="no"//初始化
var massageNum models.MassageNum //记录目标信息出现次数
var CMassageNum_yes models.MassageNum//在晚点状态下目标信息出现次数
var CMassageNum_no models.MassageNum//未晚点状态下目标信息出现次数
for _,str := range LineString{//遍历LineString对需要的信息计数
massage :=TxtReader.GetMassage(str)
massageNum =NaiveBayesian.GetMassageNum(massageNew,massage,massageNum)
CMassageNum_yes=NaiveBayesian.GetConditionalMassageNum(massageNew,massage,CMassageNum_yes,"yes")
CMassageNum_no=NaiveBayesian.GetConditionalMassageNum(massageNew,massage,CMassageNum_no,"no")
}
if NaiveBayesian.GetNaiveBatesianClassification(massageNum,CMassageNum_yes,CMassageNum_no,LineNum){//
fmt.Println("yes")
}else {
fmt.Println("no")
}
}