通过赔率预测比赛
通过赔率预测比赛
根据历往博彩公司开的赔率以及每次比赛的胜负,运用分类算法(本质上是二元分类,因为只是针对胜负)进行未来比赛的预测。得到的结果并不算很理想。主场情况下,最好只能达到七成的准确率,客场更差,对于二串一的玩法这种准确率无法胜任。
采用的分类算法是KNN,KNN的实现也可以详见之前的C++Blog上的博文,特征向量间距离是采用欧氏距离,特征向量间距离和相似度的计算可以详见C++Blog博文。
发表一下个人对于基于统计学习方法的看法:机器学习或者基于统计的方法是没有办法的办法,无法通过规则得到良好的结果,所以只能依赖于历史数据,经过大规模运算得到一个看上去还可以的结果。这种看上去还可以的结果对于一些领域是有用的,但是对准确度要求较高的领域是不适用的。
历往赔率和胜负信息如下:
星期五 20131206[00:00 -- 24:00]
301 欧冠杯 2013/12/6 23:40 费内巴切 中央陆军 3.03 1.35 亚 欧 析 2.8 78:74
302 欧冠杯 2013/12/6 23:40 布罗斯 艾菲斯 4.9 1.16 亚 欧 析 4.8 89:78
303 欧冠杯 2013/12/6 23:40 巴塞隆拿 楠泰尔 1.19 4.52 亚 欧 析 1.06 82:78
304 欧冠杯 2013/12/6 23:40 红星 帕纳辛 4.87 1.16 亚 欧 析 1.06 63:69
305 欧冠杯 2013/12/6 23:40 里塔斯 泰斯拉米卡 5.34 1.13 亚 欧 析 1.03 63:79
306 NBA 2013/12/6 23:40 76人 山猫 3.21 1.34 亚 欧 析 1.31 88:105
307 NBA 2013/12/6 23:40 雄鹿 奇才 4.84 1.17 亚 欧 析 5.1 109:105
308 NBA 2013/12/6 23:40 骑士 老鹰 3.01 1.37 亚 欧 析 1.21 89:108
309 NBA 2013/12/6 23:40 掘金 凯尔特人 1.53 2.46 亚 欧 析 2.63 98:106
310 NBA 2013/12/6 23:40 魔术 尼克斯 2.73 1.44 亚 欧 析 1.3 83:121
311 NBA 2013/12/6 23:40 勇士 火箭 2.58 1.49 亚 欧 析 1.44 83:105
312 NBA 2013/12/6 23:40 雷霆 鹈鹕 1.44 2.73 亚 欧 析 1.23 109:95
313 NBA 2013/12/7 9:45 猛龙 太阳 2.45 1.54 亚 欧 析 1.42 97:106
314 NBA 2013/12/7 10:45 爵士 开拓者 5.91 1.12 亚 欧 析 1.02 98:130
315 NBA 2013/12/7 10:45 湖人 国王 2.36 1.57 亚 欧 析 2.06 106:100
星期六 20131207[00:00 -- 24:00]
301 NBA 2013/12/8 0:40 掘金 76人 1.33 3.21 亚 欧 析 1.2 103:92
302 NBA 2013/12/8 0:40 快船 骑士 1.38 2.96 亚 欧 析 3.17 82:88
303 NBA 2013/12/8 0:40 活塞 公牛 2.14 1.68 亚 欧 析 2 92:75
304 NBA 2013/12/8 0:40 勇士 灰熊 1.75 2.04 亚 欧 析 1.54 108:82
305 NBA 2013/12/8 0:40 热火 森林狼 1.35 3.17 亚 欧 析 1.23 103:82
306 NBA 2013/12/8 9:15 篮网 雄鹿 1.67 2.16 亚 欧 析 1.54 90:82
308 NBA 2013/12/8 9:45 国王 爵士 2.28 1.61 亚 欧 析 2 112:102
309 NBA 2013/12/8 10:45 小牛 开拓者 2.78 1.43 亚 欧 析 2.92 108:106
星期日 20131208[00:00 -- 24:00]
301 NBA 2013/12/9 0:40 凯尔特人 尼克斯 2.76 1.43 亚 欧 析 2.6 114:73
302 NBA 2013/12/9 0:40 热火 活塞 1.57 2.38 亚 欧 析 1.32 110:95
303 NBA 2013/12/9 0:40 魔术 火箭 7.76 1.08 亚 欧 析 1.03 88:98
304 NBA 2013/12/9 0:40 步行者 雷霆 2.56 1.5 亚 欧 析 1.43 94:118
星期一 20131209[00:00 -- 24:00]
301 NBA 2013/12/9 23:40 勇士 山猫 1.41 2.86 亚 欧 析 2.86 111:115
302 NBA 2013/12/9 23:40 快船 76人 1.18 4.63 亚 欧 析 1.11 94:83
303 NBA 2013/12/9 23:40 掘金 奇才 1.82 1.94 亚 欧 析 1.68 75:74
304 NBA 2013/12/9 23:40 魔术 灰熊 4.21 1.21 亚 欧 析 1.1 85:94
305 NBA 2013/12/10 9:45 开拓者 爵士 1.31 3.34 亚 欧 析 1.15 105:94
306 NBA 2013/12/10 10:45 小牛 国王 1.52 2.47 亚 欧 析 2.45 97:112
星期二 20131210[00:00 -- 24:00]
301 NBA 2013/12/11 7:45 尼克斯 骑士 2.13 1.69 亚 欧 析 1.52 94:109
302 NBA 2013/12/11 8:00 热火 步行者 2.26 1.61 亚 欧 析 1.51 84:90
303 NBA 2013/12/11 7:45 马刺 猛龙 1.25 3.84 亚 欧 析 1.11 116:103
304 NBA 2013/12/11 8:15 雷霆 老鹰 1.57 2.37 亚 欧 析 1.36 101:92
305 NBA 2013/12/11 8:15 凯尔特人 篮网 2.07 1.72 亚 欧 析 1.62 96:104
306 NBA 2013/12/11 8:15 森林狼 活塞 1.89 1.88 亚 欧 析 1.76 121:94
307 NBA 2013/12/11 8:45 雄鹿 公牛 3.3 1.33 亚 欧 析 2.86 78:74
308 NBA 2013/12/11 11:15 太阳 湖人 2.23 1.63 亚 欧 析 2.18 114:108
星期三 20131211[00:00 -- 24:00]
301 NBA 2013/12/12 7:45 魔术 山猫 2.69 1.45 亚 欧 析 2.55 92:83
302 NBA 2013/12/12 8:15 快船 凯尔特人 1.43 2.76 亚 欧 析 1.34 96:88
303 NBA 2013/12/12 8:45 雷霆 灰熊 1.37 3.02 亚 欧 析 1.19 116:100
304 NBA 2013/12/12 8:45 马刺 雄鹿 1.12 5.96 亚 欧 析 1.02 109:77
305 NBA 2013/12/12 8:45 76人 森林狼 7.6 1.08 亚 欧 析 1.02 99:106
306 NBA 2013/12/12 8:45 活塞 鹈鹕 2.15 1.67 亚 欧 析 1.57 106:111
307 NBA 2013/12/12 8:45 公牛 尼克斯 2.38 1.57 亚 欧 析 1.42 78:83
308 NBA 2013/12/12 10:45 爵士 国王 3.19 1.33 亚 欧 析 3.4 122:101
309 NBA 2013/12/12 11:15 小牛 勇士 2.81 1.42 亚 欧 析 1.3 93:95
星期四 20131212[00:00 -- 24:00]
301 欧冠杯 2013/12/12 23:35 巴塞隆拿 中央陆军 2.79 1.42 亚 欧 析 1.32 65:79
302 欧冠杯 2013/12/12 23:35 艾菲斯 萨尔基利斯 1.94 1.81 亚 欧 析 1.58 63:65
303 欧冠杯 2013/12/12 23:35 泰斯拉米卡 红星 1.91 1.87 亚 欧 析 1.65 65:81
304 欧冠杯 2013/12/12 23:35 锡耶纳 亚高斯 3.9 1.23 亚 欧 析 1.1 73:78
305 欧冠杯 2013/12/12 23:35 拜仁 乌尼卡哈 2.94 1.37 亚 欧 析 1.28 72:77
306 欧冠杯 2013/12/12 23:35 里塔斯 TA马卡比 6.08 1.1 亚 欧 析 1.02 71:78
307 欧冠杯 2013/12/12 23:35 游击队 费内巴切 5.72 1.12 亚 欧 析 5.3 79:77
308 NBA 2013/12/12 23:35 快船 篮网 1.8 1.98 亚 欧 析 1.92 93:102
309 NBA 2013/12/13 11:15 火箭 开拓者 2.17 1.67 亚 欧 析 1.5 104:111
星期五 20131213[00:00 -- 24:00]
301 欧冠杯 2013/12/13 23:40 帕纳辛 火车头 2.33 1.56 亚 欧 析 2.28 82:63
302 欧冠杯 2013/12/13 23:40 楠泰尔 布迪维林克 2.17 1.65 亚 欧 析 1.5 87:97
303 欧冠杯 2013/12/13 23:40 萨斯图 加拉塔萨雷 3.77 1.24 亚 欧 析 1.09 57:76
304 欧冠杯 2013/12/13 23:40 布罗斯 米兰 3.26 1.31 亚 欧 析 1.14 73:74
306 NBA 2013/12/13 23:40 山猫 步行者 7.08 1.09 亚 欧 析 1 94:99
307 NBA 2013/12/13 23:40 骑士 魔术 2.07 1.73 亚 欧 析 1.88 109:100
308 NBA 2013/12/13 23:40 76人 猛龙 3.43 1.3 亚 欧 析 1.15 100:108
309 NBA 2013/12/13 23:40 奇才 老鹰 2.88 1.4 亚 欧 析 1.28 99:101
310 NBA 2013/12/13 23:40 尼克斯 凯尔特人 2.26 1.62 亚 欧 析 1.4 86:90
311 NBA 2013/12/13 23:40 篮网 活塞 2.9 1.4 亚 欧 析 1.35 99:103
312 NBA 2013/12/13 23:40 灰熊 鹈鹕 2.19 1.65 亚 欧 析 1.56 98:104
313 NBA 2013/12/13 23:40 湖人 雷霆 7.66 1.08 亚 欧 析 1.01 97:122
314 NBA 2013/12/14 9:15 公牛 雄鹿 1.55 2.39 亚 欧 析 1.38 91:90
315 NBA 2013/12/14 9:15 森林狼 马刺 4.1 1.23 亚 欧 析 1.11 110:117
316 NBA 2013/12/14 9:45 爵士 掘金 5.26 1.14 亚 欧 析 4.65 103:93
317 NBA 2013/12/14 9:45 国王 太阳 3.08 1.36 亚 欧 析 1.28 107:116
318 NBA 2013/12/14 11:15 火箭 勇士 2.29 1.61 亚 欧 析 2 116:112
星期六 20131214[00:00 -- 24:00]
301 NBA 2013/12/15 0:40 湖人 山猫 2.18 1.67 亚 欧 析 1.94 88:85
302 NBA 2013/12/15 0:40 快船 奇才 1.51 2.5 亚 欧 析 1.32 113:97
303 NBA 2013/12/15 0:40 骑士 热火 7.7 1.08 亚 欧 析 1.01 107:114
304 NBA 2013/12/15 0:40 老鹰 尼克斯 1.87 1.9 亚 欧 析 1.91 106:111
305 NBA 2013/12/15 0:40 猛龙 公牛 2.27 1.63 亚 欧 析 2.11 99:77
306 NBA 2013/12/15 0:40 开拓者 76人 1.15 5.26 亚 欧 析 1.06 139:105
307 NBA 2013/12/15 9:15 雄鹿 小牛 6.18 1.11 亚 欧 析 1.02 93:106
308 NBA 2013/12/15 9:45 马刺 爵士 1.32 3.28 亚 欧 析 1.17 100:84
了解程序实现细节可以阅读源码和注释。
// 通过赔率预测比赛 #include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> #include <map> #include <cassert> using namespace std; // 检验数据的规范性 bool TestNormal(const string& filename) { ifstream fin(filename.c_str()); if (!fin) { cerr << "File error!" << endl; exit(1); } string line; int totaltmp = 0; bool firstsample = true; while (getline(fin, line)) { if (line.empty()) { continue; } if (line[0] != '3') { continue; } istringstream sin(line); string tmp; int n = 0; while (sin >> tmp) { ++n; } if (firstsample) { totaltmp = n; firstsample = false; } else { if (totaltmp != n) { return false; } } } fin.close(); return true; } // 样例结构体 struct Sample { string type; // 类型 vector<double> data; // 数据 }; // 获取类型 string GetType(const string& scores) { string s1, s2; auto pos = scores.find(':'); s1 = scores.substr(0, pos); s2 = scores.substr(pos + 1); int sa = atoi(s1.c_str()); int sb = atoi(s2.c_str()); if (sa > sb) { return "1"; } else if (sa < sb) { return "2"; } else { return "-1"; } } // 读取数据 void ReadData(const string& filename, const string& nonleague, vector<Sample>& samples) { ifstream fin("HistoryData.txt"); if (!fin) { cerr << "Data file error!" << endl; exit(1); } samples.clear(); string line; while (getline(fin, line)) { if (line.empty()) { continue; } if (line[0] != '3') { continue; } istringstream sin(line); string odds1, odds2, scores; sin >> odds1 >> odds2; if (odds2 == nonleague) // 排除指定联赛 { continue; } sin >> odds1 >> odds2 >> odds1 >> odds2; sin >> odds1 >> odds2; Sample tmpsmp; double oddsa = atof(odds1.c_str()); double oddsb = atof(odds2.c_str()); tmpsmp.data.push_back(oddsa); tmpsmp.data.push_back(oddsb); sin >> odds1 >> odds2; sin >> odds2; tmpsmp.type = GetType(odds2); samples.push_back(tmpsmp); } fin.clear(); } void PrintData(const vector<Sample>& samples) { for (auto i = 0; i != samples.size(); ++i) { cout << samples[i].type << ' '; for (auto j = 0; j != samples[i].data.size(); ++j) { cout << samples[i].data[j] << ' '; } cout << endl; } } // 读取数据后,根据数据得到特征 void GenerateFeatures(vector<Sample>& samples) { for (auto i = 0; i != samples.size(); ++i) { double a = 0.0, b = 0.0; double x = samples[i].data[0]; double y = samples[i].data[1]; // 原数据中X、Y已在数据中 samples[i].data.clear(); // X,Y samples[i].data.push_back(x); samples[i].data.push_back(y); // X-1,Y-1 a = x - 1; b = y - 1; samples[i].data.push_back(a); samples[i].data.push_back(b); // 1/(Y-1),1/(X-1) a = 1 / (y - 1); b = 1 / (x - 1); samples[i].data.push_back(a); samples[i].data.push_back(b); // 1+1/(Y-1),1+1/(X-1) a = 1 + 1 / (y - 1); b = 1 + 1 / (x - 1); samples[i].data.push_back(a); samples[i].data.push_back(b); // (X-1)*(Y-1) a = (x - 1) * (y - 1); samples[i].data.push_back(a); // (1/(Y-1))*(1/(X-1)) a = (1 / (y - 1)) * (1 / (x - 1)); samples[i].data.push_back(a); // X*Y a = x * y; samples[i].data.push_back(a); // (1/(Y-1)+1)*(1/(X-1)+1) a = (1 / (y - 1) + 1) * (1 / (x - 1) + 1); samples[i].data.push_back(a); // X-Y,Y-X a = x - y; b = y - x; samples[i].data.push_back(a); samples[i].data.push_back(b); // X/Y,Y/X a = x / y; b = y / x; samples[i].data.push_back(a); samples[i].data.push_back(b); // 1/(Y-1)-(X-1),1/(X-1)-(Y-1) a = 1 / (y - 1) - (x - 1); b = 1 / (x - 1) - (y - 1); samples[i].data.push_back(a); samples[i].data.push_back(b); // (1/(Y-1)-(X-1))/(1/(Y-1)) // (1/(X-1)-(Y-1))/(1/(X-1)) a = (1 / (y - 1) - (x - 1)) / (1 / (y - 1)); b = (1 / (x - 1) - (y - 1)) / (1 / (x - 1)); samples[i].data.push_back(a); samples[i].data.push_back(b); // (1/(Y-1)-(X-1))/(X-1) // (1/(X-1)-(Y-1))/(Y-1) a = (1 / (y - 1) - (x - 1)) / (x - 1); b = (1 / (x - 1) - (y - 1)) / (y - 1); samples[i].data.push_back(a); samples[i].data.push_back(b); // (1/(Y-1)-(X-1))/(1/(Y-1)+1) // (1/(X-1)-(Y-1))/(1/(X-1)+1) a = (1 / (y - 1) - (x - 1)) / (1 / (y - 1) + 1); b = (1 / (x - 1) - (y - 1)) / (1 / (x - 1) + 1); samples[i].data.push_back(a); samples[i].data.push_back(b); // (1/(Y-1)-(X-1))/X // (1/(X-1)-(Y-1))/Y a = (1 / (y - 1) - (x - 1)) / x; b = (1 / (x - 1) - (y - 1)) / y; samples[i].data.push_back(a); samples[i].data.push_back(b); // 总共26个特征 } } // 获取训练集和测试集 void GetTrainAndTest(const vector<Sample>& samples, vector<Sample>& train, vector<Sample>& test, int testnum) { assert(testnum > 0 && testnum < samples.size()); train.clear(); test.clear(); train.assign(samples.begin(), samples.begin() + samples.size() - testnum); test.assign(samples.begin() + samples.size() - testnum, samples.end()); } // 计算欧氏距离 // 还有其他向量的距离和相似度 // 这里暂且用欧氏距离 double EuclideanDistance(const vector<double>& v1, const vector<double>& v2) { assert(v1.size() == v2.size()); double ret = 0.0; for (auto i = 0; i != v1.size(); ++i) { ret += (v1[i] - v2[i]) * (v1[i] - v2[i]); } return sqrt(ret); } // 初始化距离矩阵 // 该距离矩阵根据训练样本和测试样本而得 // 该矩阵的行数为测试样本集的样本个数 // 该矩阵的列数为训练样本集的样本个数 void InitEuclideanDistanceMatrix(const vector<Sample>& test, const vector<Sample>& train, vector<vector<double> >& dm) { dm.clear(); for (auto i = 0; i != test.size(); ++i) { vector<double> vd; for (auto j = 0; j != train.size(); ++j) { vd.push_back(EuclideanDistance(test[i].data, train[j].data)); } dm.push_back(vd); } } // KNN算法实现 // 设定不同的K值,给每个测试样例予以一个类型 // 距离和权重成反比 // 还有其他机器学习有监督分类算法,这里暂且用KNN // 二元分类算法 void KNNProcess(vector<Sample>& test, const vector<Sample>& train, const vector<vector<double> >& dm, int k) { assert(k > 0 && k <= train.size()); for (auto i = 0; i != test.size(); ++i) { multimap<double, string> dts; for (auto j = 0; j != train.size(); ++j) { if (dts.size() < k) { dts.insert(make_pair(dm[i][j], train[j].type)); } else { auto cit = dts.end(); --cit; if (dm[i][j] < cit->first) { dts.erase(cit); dts.insert(make_pair(dm[i][j], train[j].type)); } } } // 对最相似的K个样例进行数据统计 map<string, double> tds; string type = ""; double weight = 0.0; for (auto cit = dts.begin(); cit != dts.end(); ++cit) { // 不考虑权重的情况,在K个最相似的样例,只要出现就加1 // ++tds[cit->second]; // 这里是考虑距离与权重的关系,距离和权重成反比 tds[cit->second] += 1.0 / cit->first; if (tds[cit->second] > weight) { weight = tds[cit->second]; type = cit->second; } } test[i].type = type; } } // 输出测试样例 // 每行一个样例 // 每行先是样例种类,后面是样例数据 void PrintTest(const vector<Sample>& test) { for (auto i = 0; i != test.size(); ++i) { cout << test[i].type << ' '; for (auto j = 0; j != test[i].data.size(); ++j) { cout << test[i].data[j] << ' '; } cout << endl; } } // 对结果进行评估统计 // 这里对预测准确的进行统计,得到准确率 // 一种更为科学的方法是针对每种类别,计算每种类别的召回率、准确率、F值 // 召回率:类别内预测对的样例数目除以原来这个类别的样例数目 // 准确率:类别内预测对的样例数目除以所有预测这个类别的样例数目 // F值:召回率和准确率的加权值 // 这里我们只是用所有类别中预测对的样例数目除以测试样例数目 // 由于所有类别的之前数目和测试数目一直,所以这个值相当于召回率和准确率 void StatisticalEvaluation(const vector<Sample>& test, const vector<Sample>& samples) { int rightnum = 0; for (auto i = samples.size() - test.size(); i != samples.size(); ++i) { if (test[i - (samples.size() - test.size())].type == samples[i].type) { ++rightnum; } } cout << rightnum << ' ' << test.size() << endl; } // 统计准确率和召回率 // 重要的是准确率,现实操作中,只能通过预测的结果进行选择 // 召回率再高不起作用,我们只能通过准确率来保证选择的正确 void GetPrecisionAndRecall(const vector<Sample>& test, const vector<Sample>& samples) { map<string, int> rights; map<string, int> result; map<string, int> truth; for (auto i = samples.size() - test.size(); i != samples.size(); ++i) { if (test[i - (samples.size() - test.size())].type == samples[i].type) { ++rights[samples[i].type]; } ++result[test[i - (samples.size() - test.size())].type]; ++truth[samples[i].type]; } for (auto cit = rights.begin(); cit != rights.end(); ++cit) { cout << cit->first << ':'; cout << cit->second << ' '; cout << result[cit->first] << '(' << 1.0 * cit->second / result[cit->first]/*除0未考虑*/ << ')' << ' '; cout << truth[cit->first] << '(' << 1.0 * cit->second / truth[cit->first] /*除0未考虑*/ << ')' << endl; } } int main() { if (!TestNormal("HistoryData.txt")) { cerr << "数据格式错误!" << endl; exit(1); } else { //cout << "OK!" << endl; } vector<Sample> samples; ReadData("HistoryData.txt", "", samples); //PrintData(samples); GenerateFeatures(samples); vector<Sample> train, test; GetTrainAndTest(samples, train, test, samples.size() * 0.3); //GetTrainAndTest(samples, train, test, 10); vector<vector<double> > dm; InitEuclideanDistanceMatrix(test, train, dm); for (int i = 1; i <= train.size(); ++i) { KNNProcess(test, train, dm, i); //PrintTest(test); //cout << i << ' '; //StatisticalEvaluation(test, samples); cout << i << endl; GetPrecisionAndRecall(test, samples); } return 0; }
(完)
文档信息
·版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
·博客地址:http://www.cnblogs.com/unixfy
·博客作者:unixfy
·作者邮箱:goonyangxiaofang(AT)163.com
·如果你觉得本博文的内容对你有价值,欢迎对博主 小额赞助支持
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步