程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

通过赔率预测比赛

通过赔率预测比赛

         根据历往博彩公司开的赔率以及每次比赛的胜负,运用分类算法(本质上是二元分类,因为只是针对胜负)进行未来比赛的预测。得到的结果并不算很理想。主场情况下,最好只能达到七成的准确率,客场更差,对于二串一的玩法这种准确率无法胜任。

         采用的分类算法是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;
}
复制代码

posted on   unixfy  阅读(999)  评论(0编辑  收藏  举报

努力加载评论中...

导航

点击右上角即可分享
微信分享提示