2017年10月26日木曜日

pandas I/Oインターフェイス

pandas ⇔ CSV

CSVファイル


#'csv/20151963_3.csv'

日付,日目,順位,No.,名前,パー数,アンダーパー回数,アンダー総数,オーバーパー回数,オーバー総数
20150515,3,1,1003636,イ ボミ,12,6,6,0,0
20150515,3,2,1000541,大山 志保,12,5,5,1,1
20150515,3,3,1003634,テレサ・ルー,14,3,3,1,1
20150515,3,4,1000642,上田 桃子,12,5,5,1,1
20150515,3,5,1004236,ジョン ジェウン,11,3,3,5,4
20150515,3,6T,1000770,酒井 美紀,13,4,4,1,1
20150515,3,6T,1000921,申 ジエ,15,3,3,0,0
20150515,3,6T,1002910,柏原 明日架,12,4,4,2,2
  

    

CSVファイルの読み込み


import pandas as P
pd = P.read_csv('csv/20151963_3.csv',sep=',')
    

CSVファイルを読んだpandasのdataFrameの中身


pd
Out[12]: 
          日付  日目   順位      No.          名前  パー数  アンダーパー回数  アンダー総数  オーバーパー回数  \
0   20150515   3    1  1003636        イ ボミ   12         6       6         0   
1   20150515   3    2  1000541       大山 志保   12         5       5         1   
2   20150515   3    3  1003634      テレサ・ルー   14         3       3         1   
3   20150515   3    4  1000642       上田 桃子   12         5       5         1   
4   20150515   3    5  1004236  ジョン ジェウン   11         3       3         5   
5   20150515   3   6T  1000770       酒井 美紀   13         4       4         1   
6   20150515   3   6T  1000921        申 ジエ   15         3       3         0   
7   20150515   3   6T  1002910      柏原 明日架   12         4       4         2   
    

CSVファイルの書込み


pd.to_csv('csv/test.csv',sep=',',header=False,index=False)
    

CSVファイル


#'csv/test.csv'

20150515,3,1,1003636,イ ボミ,12,6,6,0,0
20150515,3,2,1000541,大山 志保,12,5,5,1,1
20150515,3,3,1003634,テレサ・ルー,14,3,3,1,1
20150515,3,4,1000642,上田 桃子,12,5,5,1,1
20150515,3,5,1004236,ジョン ジェウン,11,3,3,5,4
20150515,3,6T,1000770,酒井 美紀,13,4,4,1,1
20150515,3,6T,1000921,申 ジエ,15,3,3,0,0
20150515,3,6T,1002910,柏原 明日架,12,4,4,2,2
20150515,3,9T,1000820,葭葉 ルミ,14,4,4,0,0
20150515,3,9T,1000912,全 美貞,12,5,4,3,2
20150515,3,9T,1003675,フェービー・ヤオ,11,4,4,3,3
  

    

pandas(dataFrame) ⇔ LIST

dataFrameからListへの変換


L = pd.values.tolist()

L
Out[17]: 
[[20150515, 3, '1', 1003636, 'イ ボミ', 12, 6, 6, 0, 0],
 [20150515, 3, '2', 1000541, '大山 志保', 12, 5, 5, 1, 1],
 [20150515, 3, '3', 1003634, 'テレサ・ルー', 14, 3, 3, 1, 1],
 [20150515, 3, '4', 1000642, '上田 桃子', 12, 5, 5, 1, 1],
 [20150515, 3, '5', 1004236, 'ジョン ジェウン', 11, 3, 3, 5, 4],
 [20150515, 3, '6T', 1000770, '酒井 美紀', 13, 4, 4, 1, 1],
 [20150515, 3, '6T', 1000921, '申 ジエ', 15, 3, 3, 0, 0],
 [20150515, 3, '6T', 1002910, '柏原 明日架', 12, 4, 4, 2, 2],
 [20150515, 3, '9T', 1000820, '葭葉 ルミ', 14, 4, 4, 0, 0],
 [20150515, 3, '9T', 1000912, '全 美貞', 12, 5, 4, 3, 2],
 [20150515, 3, '9T', 1003675, 'フェービー・ヤオ', 11, 4, 4, 3, 3],
    ・
    ・
 [20150515, 3, '56', 1000708, '三塚 優子', 10, 2, 2, 8, 6]]
     

ListをdataFrameに変換


labels = ['日付','日目','順位','No.','名前','パー数','アンダーパー回数','アンダー総数','オーバーパー回数','オーバー総数']
df = P.DataFrame.from_records(L, columns=labels)
    


df
Out[35]: 
          日付  日目   順位      No.          名前  パー数  アンダーパー回数  アンダー総数  オーバーパー回数  \
0   20150515   3    1  1003636        イ ボミ   12         6       6         0   
1   20150515   3    2  1000541       大山 志保   12         5       5         1   
2   20150515   3    3  1003634      テレサ・ルー   14         3       3         1   
3   20150515   3    4  1000642       上田 桃子   12         5       5         1   
4   20150515   3    5  1004236  ジョン ジェウン   11         3       3         5   
5   20150515   3   6T  1000770       酒井 美紀   13         4       4         1   
6   20150515   3   6T  1000921        申 ジエ   15         3       3         0   
7   20150515   3   6T  1002910      柏原 明日架   12         4       4         2   
    

pandas(dataFrame)からHTML(table)への出力


html = df.to_html()

type(html)
Out[24]: str

html
Out[25]: 
<table border="1" class="dataframe">\n  
  <thead>\n    
    <tr style="text-align: right;">\n
      <th></th>\n
      <th>日付</th>\n
      <th>日目</th>\n
      <th>順位</th>\n      
    



pandas(dataFrame)のデータを抽出


df.at[7,'名前']
Out[33]: '柏原 明日架'

df.loc[7,'名前']
Out[35]: '柏原 明日架'

df.loc[0:5,'名前']
Out[36]: 
0          イ ボミ
1         大山 志保
2        テレサ・ルー
3         上田 桃子
4    ジョン ジェウン
5         酒井 美紀
Name: 名前, dtype: object

df.loc[0:5,'日付':'名前']
Out[37]: 
         日付  日目  順位      No.          名前
0  20150515   3   1  1003636        イ ボミ
1  20150515   3   2  1000541       大山 志保
2  20150515   3   3  1003634      テレサ・ルー
3  20150515   3   4  1000642       上田 桃子
4  20150515   3   5  1004236  ジョン ジェウン
5  20150515   3  6T  1000770       酒井 美紀

    

2017年10月24日火曜日

人工知能の学習データの量と正解率

女子競艇レースの予測にレーサーの勝率を使用した場合

https://python-8.blogspot.jp/2017/10/python.htmlの続きでレース予測に使用する学習用データを増やし場合の予測結果をまとめました。

学習用データ 学習データ数 検証 正解率
2015~2016年 3427 381 48%
2016年 1785 199 52%
2016年後半のみ 900 100 49%

上図の通り女子競艇レースの結果を2015~2016年、2016年、2016年後半のみの3種類を学習用データとして与え、正解率への影響を調べてみました。学習データにはレーサーの勝率のみを使用しています。予測プログラムは以下の通りになります。


from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.callbacks import EarlyStopping
import pandas as pd, numpy as np
import psycopg2,sys

class SQL:
    """ SQL script """
    def __init__(self) :
        self.SQL = "SELECT COUNT(*) "
        self.SQL = self.SQL + "FROM SAMPLE_1 ;"
    def pastData(self,date) :
        self.SQL = "SELECT * "
        self.SQL = self.SQL + "FROM SAMPLE_1 "
        self.SQL = self.SQL + "WHERE DATE_PART('YEAR',date) = '"
        self.SQL = self.SQL + date
        self.SQL = self.SQL + "' ORDER BY date ;"
    def predictData(self,date):
        self.SQL = "SELECT * "
        self.SQL = self.SQL + "FROM SAMPLE_1 "
        self.SQL = self.SQL + "WHERE date = '"
        self.SQL = self.SQL + date
        self.SQL = self.SQL + "' ;"

def execute_SQL_pandas(SQL) :
    """ Read race result from DataBase """
    try :
        conn = psycopg2.connect(database="ddddddd", user="uuuuu", password="xxxxx", host="127.0.0.1", port="oooo")    
        results = pd.read_sql(SQL,conn)        
        return results
    except psycopg2.OperationalError as e:        
        print('Unable to connect!\n{0}').format(e)
        sys.exit(1)    
    finally:
        if conn:
            conn.close()

def predict(pastData,predictData) :
    """Predict boat race """
    #過去のレース結果にこれから始まるデータを追加する
    pastData = pastData.append(predictData, ignore_index=True)
    #レーサー毎の勝率を正規化する
    pastData["white_racer"] /= max(pastData['white_racer']) 
    pastData["black_racer"] /= max(pastData['black_racer']) 
    pastData["red_racer"] /= max(pastData['red_racer']) 
    pastData["blue_racer"] /= max(pastData['blue_racer']) 
    pastData["yellow_racer"] /= max(pastData['yellow_racer']) 
    pastData["green_racer"] /= max(pastData['green_racer']) 
    #学習用のデータを一つにまとめる
    inputData = pastData[["white_racer",
                          "black_racer",
                          "red_racer",
                          "blue_racer",
                          "yellow_racer",
                          "green_racer"
                  ]].as_matrix()
    #結果の識別(今回は6種類)
    label_no = 6
    label = {"1":[1,0,0,0,0,0], 
             "2":[0,1,0,0,0,0], 
             "3":[0,0,1,0,0,0], 
             "4":[0,0,0,1,0,0], 
             "5":[0,0,0,0,1,0],
             "6":[0,0,0,0,0,1]}
    #結果をラベルに置き換える
    resultLabel = np.empty((len(pastData),label_no))
    for i, v in enumerate(pastData["result"]):
        resultLabel[i] = label[v]
    #訓練データとテストデータを分ける 
    n = int(len(pastData)/3)*2 + 1
    trainIn, trainLabel = inputData[1:n], resultLabel[1:n]
    testIn,  testLabel  = inputData[n:len(pastData)-12], resultLabel[n:len(pastData)-12] 


    #モデルの構造を定義
    model = Sequential()
    model.add(Dense(512, input_shape=(6,)))
    model.add(Activation('relu'))
    model.add(Dropout(0.1))

    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dropout(0.1))

    model.add(Dense(label_no))
    model.add(Activation('softmax'))

    #モデルを構築
    model.compile(
        loss='categorical_crossentropy',
        optimizer="rmsprop",
        metrics=['accuracy'])

    #データで訓練開始
    model.fit(
        trainIn, trainLabel,
        batch_size=100,
        nb_epoch=20,
        validation_split=0.1,
        callbacks=[EarlyStopping(monitor='val_loss', patience=2)],
        verbose=1)

    #モデルの評価を開始
    score = model.evaluate(testIn, testLabel)
    #レースの予想を開始
    predictInData = inputData[len(pastData)-12:len(pastData)]
    predictions = model.predict(predictInData)
    
    return score,predictions

if __name__ == '__main__':
    
    #2015年のレース結果をデータベースから読み出す
    sql = SQL()
    sql.pastData('2015')
    pastData2015 = execute_SQL_pandas(sql.SQL)

    #2016年のレース結果をデータベースから読み出す
    sql.pastData('2016')
    pastData2016 = execute_SQL_pandas(sql.SQL)
    
    #2015年と2016年のレース結果を結合する
    pastData = pastData2015.append(pastData2016, ignore_index=True)
    
    #予想するレースのデータをデータベースから読み出す
    sql.predictData('2017-01-14')
    predictData = execute_SQL_pandas(sql.SQL)
    #予想する
    score,predictions = predict(pastData,predictData)
    

    

学習データを増やしても正解率は改善しない

2015~2016年の2年分のレース結果を学習データに使用した場合の結果。


Train on 3427 samples, validate on 381 samples
Epoch 1/20
3427/3427 [==============================] - 1s - loss: 1.3895 - acc: 0.4835 - val_loss: 1.3966 - val_acc: 0.4646
Epoch 2/20
3427/3427 [==============================] - 1s - loss: 1.3169 - acc: 0.5001 - val_loss: 1.3206 - val_acc: 0.4987
Epoch 3/20
3427/3427 [==============================] - 1s - loss: 1.3072 - acc: 0.5042 - val_loss: 1.2881 - val_acc: 0.5459
Epoch 4/20
3427/3427 [==============================] - 1s - loss: 1.2989 - acc: 0.5066 - val_loss: 1.3045 - val_acc: 0.5013
Epoch 5/20
3427/3427 [==============================] - 1s - loss: 1.2977 - acc: 0.5127 - val_loss: 1.2742 - val_acc: 0.5381
Epoch 6/20
3427/3427 [==============================] - 1s - loss: 1.2947 - acc: 0.5083 - val_loss: 1.2805 - val_acc: 0.5249
Epoch 7/20
3427/3427 [==============================] - 1s - loss: 1.2891 - acc: 0.5153 - val_loss: 1.2683 - val_acc: 0.5512
Epoch 8/20
3427/3427 [==============================] - 1s - loss: 1.2861 - acc: 0.5185 - val_loss: 1.2708 - val_acc: 0.5276
Epoch 9/20
3427/3427 [==============================] - 1s - loss: 1.2853 - acc: 0.5147 - val_loss: 1.2687 - val_acc: 0.5302
Epoch 10/20
3427/3427 [==============================] - 1s - loss: 1.2810 - acc: 0.5142 - val_loss: 1.2799 - val_acc: 0.5249
1856/1891 [============================>.] - ETA: 0saccuracy= 0.499206768921
    



2016年後半のみを学習データとして与えて予測を実施。


if __name__ == '__main__':
    
    #過去のレース結果をデータベースから読み出す
    sql = SQL()
    
    sql.pastData('2016')
    pastData2016 = execute_SQL_pandas(sql.SQL)
            
    #予想するレースのデータをデータベースから読み出す
    sql.predictData('2017-01-14')
    predictData = execute_SQL_pandas(sql.SQL)
    #予想する
    score,predictions = predict(pastData2016[1476:],predictData)
    
    print('accuracy=', score[1])


    

2016年後半のみを学習データとして与えて予測を実施した結果。


Train on 900 samples, validate on 100 samples
Epoch 1/20
900/900 [==============================] - 0s - loss: 1.5447 - acc: 0.4044 - val_loss: 1.3123 - val_acc: 0.6100
Epoch 2/20
900/900 [==============================] - 0s - loss: 1.4403 - acc: 0.4578 - val_loss: 1.2292 - val_acc: 0.6200
Epoch 3/20
900/900 [==============================] - 0s - loss: 1.3868 - acc: 0.4778 - val_loss: 1.1930 - val_acc: 0.6200
Epoch 4/20
900/900 [==============================] - 0s - loss: 1.3804 - acc: 0.4722 - val_loss: 1.1864 - val_acc: 0.6000
Epoch 5/20
900/900 [==============================] - 0s - loss: 1.3791 - acc: 0.4700 - val_loss: 1.2399 - val_acc: 0.5700
Epoch 6/20
900/900 [==============================] - 0s - loss: 1.3706 - acc: 0.4722 - val_loss: 1.2084 - val_acc: 0.6000
Epoch 7/20
900/900 [==============================] - 0s - loss: 1.3515 - acc: 0.4756 - val_loss: 1.2319 - val_acc: 0.5600
480/487 [============================>.] - ETA: 0saccuracy= 0.498973306138
    

今回はレーサーの勝率のみで予測しているので、与える入力データとレース結果との因果関係により必要なデータ量にも影響を与える可能性があるので引き続き検証を続ける。

2017年10月22日日曜日

【円グラフ】matplotlibを使ってデータを視覚化する

グラフ作りはEXCELよりも簡単⁈

pythonでグラフを作る場合、はじめはコードを書く手間ひまが掛かりますが、いったん作ればその後はデータを変更するだけでいいので、何度も繰り返すような作業ではEXCELよりも効率的です。

データベースからデータを読み込んだ後たったの7行で円グラフが完成。



import pandas as pd
import matplotlib.pyplot as plt
import psycopg2,sys

def SQL_readData(year) :
    
    sql = 'SELECT result,COUNT(*) FROM sample_1 '
    sql = sql + "WHERE DATE_PART('YEAR',date) = '"  
    sql = sql + year
    sql = sql + "' GROUP BY result "
    sql = sql + "ORDER BY result ;"
    
    return sql


def execute_SQL(SQL) :
    
    try :        
        conn = psycopg2.connect(database="ddddd", user="uuuuuu", password="xxxxxx", host="127.0.0.1", port="oooo")    
        results = pd.read_sql(SQL,conn)        
        return results        
    except psycopg2.OperationalError as e:        
        print('Unable to connect!\n{0}').format(e)
        sys.exit(1)    
    finally:
        if conn:
            conn.close()
            
if __name__ == '__main__':

    SQL = SQL_readData('2016')
    readData = execute_SQL(SQL)

    labels = ['6号艇', '5号艇', '4号艇','3号艇','2号艇','1号艇']
    sizes = [readData.at[5,'count'], readData.at[4,'count'], readData.at[3,'count'], readData.at[2,'count'],readData.at[1,'count'],readData.at[0,'count']]
    colors = ['#FDF2E9', '#FAE5D3', '#F5CBA7', '#F0B27A','#EB984E','#E67E22']

    fig1, ax1 = plt.subplots()
    ax1.pie(sizes, labels=labels, autopct='%1.1f%%', colors=colors, startangle=90)
    ax1.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.

    plt.savefig('graph/foo.png')
    


グラフの装飾も以下の通り可能です。


            
if __name__ == '__main__':

    SQL = SQL_readData('2016')
    readData = execute_SQL(SQL)

    explode = ( 0,0,0,0.2,0,0) ←3号艇を強調

    labels = ['6号艇', '5号艇', '4号艇','3号艇','2号艇','1号艇']
    sizes = [readData.at[5,'count'], readData.at[4,'count'], readData.at[3,'count'], readData.at[2,'count'],readData.at[1,'count'],readData.at[0,'count']]
    colors = ['#FDF2E9', '#FAE5D3', '#F5CBA7', '#F0B27A','#EB984E','#E67E22']

    fig1, ax1 = plt.subplots()
    ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', colors=colors, startangle=90,shadow=True,pctdistance=0.8, labeldistance=1.1)
    ax1.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
    plt.rcParams['font.size'] = 11.0 ← フォントのサイズを変更
    plt.title("1着の号艇の割合", loc='right') ← タイトルを追加
    plt.rcParams['axes.titlesize'] = 14.0 ← タイトルのサイズ指定
    
    plt.savefig('graph/foo.png')
    

CSVファイルをPostgreSQLデータベースに書込む

CSVファイルの入出力にはpandasが便利!

CSVファイルはEXCELから吐き出すこともできるので、データベースへの書込みはこのルートでほとんど賄えます。

CSVファイルの内容


2017-10-10,1.,Almost Like Praying (feat. Artists for Puerto Rico),Lin-Manuel Miranda
2017-10-10,2.,Thunder,Imagine Dragons
2017-10-10,3.,Feel It Still,Portugal. The Man
2017-10-10,4.,Perfect,Ed Sheeran
2017-10-10,5.,Look What You Made Me Do,Taylor Swift

    

今回はデータベースにpostgreSQLを使用して、postgreSQLとのインターフェースにはpsycopg2を使っています。SQLを作る2つの関数とそのSQLを実行する2つの関数で構成し、CSVファイルの読み込みはpandasのread_csv関数を使って1行で達成してます。



import psycopg2,sys
import pandas as pd

def SQL_createTable() :
    
    sql = 'CREATE TABLE itunes'
    sql = sql + '(date DATE,'  
    sql = sql + 'rank SMALLINT,'
    sql = sql + 'song VARCHAR(100),'
    sql = sql + 'artist VARCHAR(30));'
    
    return sql

def SQL_insertTable() :
    
    sql = 'INSERT INTO itunes ('
    sql = sql + 'date,'  
    sql = sql + 'rank,'
    sql = sql + 'song,'
    sql = sql + 'artist)'
    sql = sql + ' VALUES (%s,%s,%s,%s);'
    
    return sql


def initial_DB(SQL) :    
    try :        
        conn = psycopg2.connect(database="ddddd", user="uuuuu", password="xxxxx", host="127.0.0.1", port="0000")    
        cur = conn.cursor()
        cur.execute(SQL)
        conn.commit()        
    except psycopg2.OperationalError as e:        
        print('Unable to connect!\n{0}').format(e)
        sys.exit(1)    
    finally:
        if conn:
            conn.close()
            
def csv2DB(SQL,csvDF) :    
    try :        
        conn = psycopg2.connect(database="ddddd", user="uuuuu", password="xxxxx", host="127.0.0.1", port="0000")    
        for index in range(len(csvDF)) :
            cur = conn.cursor()
            cur.execute(SQL,(
                    csvDF.at[index,'date'],
                    csvDF.at[index,'rank'],
                    csvDF.at[index,'song'],
                    csvDF.at[index,'artist']))
            conn.commit()
    except psycopg2.OperationalError as e:
        print('Unable to connect!\n{0}').format(e)
        sys.exit(1)
    finally:
        if conn:
            conn.close()

if __name__ == '__main__':

    createTable = True
    if createTable :
        SQL = SQL_createTable()
        initial_DB(SQL)
    csvDF = pd.read_csv('csv/file.csv',sep=',',names =('date','rank','song','artist'))    
    SQL = SQL_insertTable()
    csv2DB(SQL,csvDF)
    

    


データベースに蓄積された内容は以下の通り。



postgresql => select * from itunes ;
    date    | rank |                        song                         |       artist
------------+------+-----------------------------------------------------+--------------------
 2017-10-10 |    1 | Almost Like Praying (feat. Artists for Puerto Rico) | Lin-Manuel Miranda
 2017-10-10 |    2 | Thunder                                             | Imagine Dragons
 2017-10-10 |    3 | Feel It Still                                       | Portugal. The Man
 2017-10-10 |    4 | Perfect                                             | Ed Sheeran
 2017-10-10 |    5 | Look What You Made Me Do                            | Taylor Swift
(5 行)


project_1=>
    

iTunes のランキングをサクッとスクラッピング

BeautifulSoupを使ってWeb上のデータ集めも簡単♪

iTunes Chrat の1位から5位までの曲名と歌手名をリストアップする

https://www.apple.com/itunes/charts/songs/


from bs4 import BeautifulSoup
from urllib.request import urlopen

if __name__ == '__main__':

    #URLのHTML全体を読み込む
    url = 'https://www.apple.com/itunes/charts/songs/'
    urlRead = urlopen(url).read()
    html = BeautifulSoup(urlRead, "html.parser")
    
    #欲しいデータを絞り込む
    section = html.find("section", { "class" : "section chart-grid" })
    div = section.find("div", { "class" : "section-content" })
    chart = div.find_all('li')
    
    for n in range(5) :
        s = chart[n].find('strong').get_text() + '位 : '
        s = s + chart[n].find_all('a')[1].get_text() + ' 【 '
        s = s + chart[n].find_all('a')[2].get_text() + ' 】'
        print(s)

    

ランキングの結果

リストアップの結果は以下の通りです。


1.位 : Almost Like Praying (feat. Artists for Puerto Rico) 【 Lin-Manuel Miranda 】
2.位 : Dear Hate (feat. Vince Gill) 【 Maren Morris 】
3.位 : Thunder 【 Imagine Dragons 】
4.位 : Heaven 【 Kane Brown 】
5.位 : Feel It Still 【 Portugal. The Man 】
    


結果をCSVファイルとして出力する

3*5の多重配列のリストをpandasのdataFrameに変換することでpandasの関数(to_csv)を利用してcsv fileを生成することが出来ます。


from bs4 import BeautifulSoup
from urllib.request import urlopen
import pandas as pd

if __name__ == '__main__':

    #URLのHTML全体を読み込む
    url = 'https://www.apple.com/itunes/charts/songs/'
    urlRead = urlopen(url).read()
    html = BeautifulSoup(urlRead, "html.parser")
    
    #欲しいデータを絞り込む
    section = html.find("section", { "class" : "section chart-grid" })
    div = section.find("div", { "class" : "section-content" })
    chart = div.find_all('li')
    
    tempList = []
    for n in range(5) :
        temp = [chart[n].find('strong').get_text(),
                chart[n].find_all('a')[1].get_text(),
                chart[n].find_all('a')[2].get_text()]
        tempList.append(temp)
    
    df = pd.DataFrame(tempList)
    df.to_csv('csv/file.csv',sep=',',header=False,index=False)
    

「csv/file.csv」の中身は以下の通りです


1.,Almost Like Praying (feat. Artists for Puerto Rico),Lin-Manuel Miranda
2.,Thunder,Imagine Dragons
3.,Feel It Still,Portugal. The Man
4.,Perfect,Ed Sheeran
5.,Look What You Made Me Do,Taylor Swift
    

pythonで競艇の予測プログラムをサクッと作る

フローを整理してプログラムの目的を明確にする

ブログを始めるにあたりプログラムの可読性を高めるためにフローを図のように統一しました。これによりプログラムの位置付けが明確になり読みやすくなりました。またデータベースを使うことで作業の区切りをつけて、取り扱うデータを頭の中で整理しやすくなってます。その際メモリー容量とか演算スピードとかは一切考えてません。あくまでデータをフラットに使って情報の可読性を最優先してます。プログラムは以下の通り細分化して参照したいプログラムを探しやすくしましたので興味がありましたら参照して下さい。

競艇の予測プログラムがこんなに簡単に!

先に2016年女子の2964回分のレース結果をデータベースに用意して、次のレース予測を行うプログラムが以下の通りとなります。SQLを作り上げるクラスを準備して、SQLを実行する関数で2016年のレース結果をデータベースから読み出します。この2016年のレース結果から2017年に実施されるレースを予測するのがpredict関数になります。


from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.callbacks import EarlyStopping
import pandas as pd, numpy as np
import psycopg2,sys

class SQL:
    """ SQL script """
    def __init__(self) :
        self.SQL = "SELECT COUNT(*) "
        self.SQL = self.SQL + "FROM SAMPLE_1 ;"
    def pastData(self,date) :
        self.SQL = "SELECT * "
        self.SQL = self.SQL + "FROM SAMPLE_1 "
        self.SQL = self.SQL + "WHERE DATE_PART('YEAR',date) = '"
        self.SQL = self.SQL + date
        self.SQL = self.SQL + "' ORDER BY date ;"
    def predictData(self,date):
        self.SQL = "SELECT * "
        self.SQL = self.SQL + "FROM SAMPLE_1 "
        self.SQL = self.SQL + "WHERE date = '"
        self.SQL = self.SQL + date
        self.SQL = self.SQL + "' ;"

def execute_SQL_pandas(SQL) :
    """ Read race result from DataBase """
    try :
        conn = psycopg2.connect(database="ddddddd", user="uuuuu", password="xxxxx", host="127.0.0.1", port="oooo")    
        results = pd.read_sql(SQL,conn)        
        return results
    except psycopg2.OperationalError as e:        
        print('Unable to connect!\n{0}').format(e)
        sys.exit(1)    
    finally:
        if conn:
            conn.close()

def predict(pastData,predictData) :
    """Predict boat race """
    #過去のレース結果にこれから始まるデータを追加する
    pastData = pastData.append(predictData, ignore_index=True)
    #レーサー毎の勝率を正規化する
    pastData["white_racer"] /= max(pastData['white_racer']) 
    pastData["black_racer"] /= max(pastData['black_racer']) 
    pastData["red_racer"] /= max(pastData['red_racer']) 
    pastData["blue_racer"] /= max(pastData['blue_racer']) 
    pastData["yellow_racer"] /= max(pastData['yellow_racer']) 
    pastData["green_racer"] /= max(pastData['green_racer']) 
    #学習用のデータを一つにまとめる
    inputData = pastData[["white_racer",
                          "black_racer",
                          "red_racer",
                          "blue_racer",
                          "yellow_racer",
                          "green_racer"
                  ]].as_matrix()
    #結果の識別(今回は6種類)
    label_no = 6
    label = {"1":[1,0,0,0,0,0], 
             "2":[0,1,0,0,0,0], 
             "3":[0,0,1,0,0,0], 
             "4":[0,0,0,1,0,0], 
             "5":[0,0,0,0,1,0],
             "6":[0,0,0,0,0,1]}
    #結果をラベルに置き換える
    resultLabel = np.empty((len(pastData),label_no))
    for i, v in enumerate(pastData["result"]):
        resultLabel[i] = label[v]
    #訓練データとテストデータを分ける 
    n = int(len(pastData)/3)*2 + 1
    trainIn, trainLabel = inputData[1:n], resultLabel[1:n]
    testIn,  testLabel  = inputData[n:len(pastData)-12], resultLabel[n:len(pastData)-12] 


    #モデルの構造を定義
    model = Sequential()
    model.add(Dense(512, input_shape=(6,)))
    model.add(Activation('relu'))
    model.add(Dropout(0.1))

    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dropout(0.1))

    model.add(Dense(label_no))
    model.add(Activation('softmax'))

    #モデルを構築
    model.compile(
        loss='categorical_crossentropy',
        optimizer="rmsprop",
        metrics=['accuracy'])

    #データで訓練開始
    model.fit(
        trainIn, trainLabel,
        batch_size=100,
        nb_epoch=20,
        validation_split=0.1,
        callbacks=[EarlyStopping(monitor='val_loss', patience=2)],
        verbose=1)

    #モデルの評価を開始
    score = model.evaluate(testIn, testLabel)
    #レースの予想を開始
    predictInData = inputData[len(pastData)-12:len(pastData)]
    predictions = model.predict(predictInData)
    
    return score,predictions

if __name__ == '__main__':
    
    #過去のレース結果をデータベースから読み出す
    sql = SQL()
    sql.pastData('2016')
    pastData = execute_SQL_pandas(sql.SQL)
    
    #予想するレースのデータをデータベースから読み出す
    sql.predictData('2017-01-14')
    predictData = execute_SQL_pandas(sql.SQL)
    #予想する
    score,predictions = predict(pastData,predictData)
    

    


競艇の予測プログラムの実行結果(正解率:52%)

2016年のレース結果のデータは以下の通りです。各レーサの勝率を元に人口知能に学習させてます。これが2964回のレース分あります。


pastData.ix[0]
Out[62]: 
date                  2016-01-09
race_no                        1
white_racer                  9.5 ←1号艇レーサーの勝率
black_racer                 20.6 ←2号艇レーサーの勝率
red_racer                   17.8 ←3号艇レーサーの勝率
blue_racer                   2.3 ←4号艇レーサーの勝率
yellow_racer                28.3 ←5号艇レーサーの勝率
green_racer                 23.2 ←6号艇レーサーの勝率
result                         5  ←結果(5号艇が1位)
Name: 0, dtype: object
    

プログラムの実行結果は、正解率は52%で3号艇が0.36で一番高く結果は3号艇で大当たり


In [63]:score[1]
Out[63]: 0.52000000042793082 ←正解率52%

In [64]:
predictions[0]         予測結果
Out[64]:              ↓
array([ 0.18952873,  0.29357207,  0.36467475,  0.08164515,  0.04051487,  0.03006445], dtype=float32)  
predictData.ix[0]
Out[65]: 
date                  2017-01-14
race_no                        1
white_racer                  5.1
black_racer                   18
red_racer                   31.6 ←勝率の高さで学習しているのが分かる
blue_racer                   4.3
yellow_racer                 7.2
green_racer                    3
result                         3
Name: 0, dtype: object
    

第2レースの予測結果は、3号艇が0.40で一番高く、結果は3号艇で大当たり


predictions[1]
Out[67]: 
array([ 0.16596539,  0.30057701,  0.40548053,  0.08547471,  0.02496851,  0.01753386], dtype=float32)

predictData.ix[1]
Out[68]: 
date                  2017-01-14
race_no                        2
white_racer                  2.7
black_racer                   24
red_racer                     41
blue_racer                    12
yellow_racer                 5.6
green_racer                    0
result                         3
Name: 1, dtype: object
    

第3レースの予測結果は、1号艇が0.30で一番高く結果は4号艇で外れ。


predictions[2]
Out[69]: 
array([ 0.30444902,  0.1886065 ,  0.06484132,  0.27285177,  0.14985009,  0.01940131], dtype=float32)

predictData.ix[2]
Out[70]: 
date                  2017-01-14
race_no                        3
white_racer                   16
black_racer                   15
red_racer                    3.3
blue_racer                  31.8
yellow_racer                43.6
green_racer                   10
result                         4
Name: 2, dtype: object
    


結論:予測プログラムは意外と使えそう

最後のレース結果は単純に勝率の高さだけでなくスタート位置を考慮していることを伺わせるような予想を立てています。勝率が一番高い5号艇よりも4号艇の方が確率が高くなっているところに可能性を感じます。正解率を100%にすることは絶対に無理ですが、モーターの勝率やボート勝率を含めたり、あるいはスタート位置からの勝率などを計算し直すことで現状の正解率を60%とか70%まで高めることはできそうです。(現在検証中)

このようにpythonプログラムって簡単じゃないですか?pythonをイチから完璧に覚えてベストはプログラムを目指そうとするとプログラムを書き始められるまでかなりの時間を要することになります。ベストじゃなくベターなプログラム(既に実績のあるプログラム)ならこんな感じで今からでもスタートできます。興味のある方はブログを御覧下さい。