LoginSignup
5
2

More than 3 years have passed since last update.

MATLABで競艇の解析を始めよう(ピットレポート解析)

Posted at

MATLABでボートレースの解析をする10個目くらいの記事です。

 8回目9回目の続きです!あまり予想するにあたって参考にならないと評判の「ピットレポート」を解析してみます。

 COTOHAが面白そうだったので、COTOHA でやってみましょう。COTOHA は、自然言語処理の使いやすい API を提供してくれるサービスです!
https://api.ce-cotoha.com/

ピットレポートって何なのさ。

 大きいレースがあるときだけ公式サイトに載ってる情報なんですけど、走る前のインタビューです。例えばこういうやつ。
 https://www.boatrace.jp/owpc/pc/race/pitreport?rno=12&jcd=22&hd=20191114
pitrep.jpg

 このレースだと、結果は 124653 の順なんですけど、この言葉から情報は取れるかな。

さっそくピットレポートの読み込みからはじめよう!

 と張り切ると、大体詰まる。。

ダウンロードできるデータがないので、力技で取ろう。

 ここを探してもないよね。。。
 https://www.boatrace.jp/owpc/pc/extra/data/download.html

 とりあえずウェブページにはあるので、レースがある日の URL を総当りで取ってみましょう。
前回のプログラムの続きで、2019年のデータでやってみます!

MATLAB と Text Analytics Toolbox があればいいね。

%% 前回までの結果を読む。
load TR_all
load fanbook

%% ピットレポートを取ってくる。
PlaceList = {'桐生','戸田','江戸川','平和島','多摩川',...
    '浜名湖','蒲郡','常滑','津',...
    '三国','琵琶湖','住之江','尼崎',...
    '鳴門','丸亀','児島','宮島','徳山','下関',...
    '若松','芦屋','福岡','唐津','大村'};
Place = categorical(PlaceList,PlaceList);

TR_comment = table;
f = uifigure;
d = uiprogressdlg(f,'Title','読込中','Message','ちょっと待ちなさいね。');
for ix = 1:height(TR_2019)
    % URLはこんな感じで、レース番号 rno, 開催場所 jcd, 日付 hd を入れれるようにプログラムを書こう。
    % https://www.boatrace.jp/owpc/pc/race/pitreport?rno=01&jcd=07&hd=20200214
    jcd = num2str(find(ismember(PlaceList,TR_2019.Place{ix})),'%02d');
    rno = num2str(TR_2019.R(ix),'%02d');
    hd = TR_2019.YYYYMMDD{ix};
    url = ['https://www.boatrace.jp/owpc/pc/race/pitreport?rno=',rno,'&jcd=',jcd,'&hd=',hd];
    code = webread(url);

    if contains(code,'is-alignL')
        T = htmlTree(code);
        td = findElement(T,'td[class="is-alignL is-p0-10"]');
        comment = extractHTMLText(td);
        comment = comment(TR_2019.frame(ix,:));  % 着順で並び替え
        TR_comment = [TR_comment;[TR_2019(ix,:),table(comment','VariableNames',{'Comment'})]];
    end
    d.Value = ix/height(TR_2019);
end

save TR_comment TR_comment

超時間がかかるので、実行したら一旦帰る。

 2019年の54493レースの総当りなので、適当に書いたウェイトバーが全然進まない。。
waitamin.jpg

しばらくしたら TR_comment.mat ができてるはず。

 できてたら読み込んで、続きをやりましょう。

load TR_comment    % 2054レース x 6人分 = 12324個のデータがありました。

 試しに適当なレースのコメントを見てみる。

>> TR_comment.Comment(end,:)'

ans = 

  6×1  string 配列

    "今日が一番いい感じでした。全体にいいですね。だいぶ上積みが出来てきました。スタートは早いと思ったら早いし勘はズレていないです。3コースは好きですね。(コメント自信度・・★★☆)"
    "昨日は調整をして出足も伸びも、だいぶ良くなっていますね。でも、昨日とは感触が違うので一から調整をします。スタートは決まっているけど、自分の勘とはズレているので修正をしたい。いいスタートを決めたいです。(コメント自信度・・★★★)"
    "しっかりした足はありますね。起こしでちょっと気になるところがあるので修正をしたいです。出たとこ勝負になりそうだけど、スタートはしっかりコンマ10の全速を行きたいですね。(コメント自信度・・★★☆)"
    "足はいい方だと思います。合えば伸びも回り足も、ちょっとずついいですね。試したいペラの調整をしています。波を越えられる足になっているし、どこか開いたところを捲り差したいですね。(コメント自信度・・★★☆)"
    "乗れるとは思っていませんでした。エンジンは普通くらいになったけど、ペラは叩き変えます。展開を突ける足にしたいですね。6コースは嫌いじゃないです。昨日はいいスタートを行けたので今日も行けるようにしたい。(コメント自信度・・★★☆)"
    "バランスが取れて全体にいい足をしていますね。満足をしています。気象条件にしっかり合わせたい。昨日はスタートで遅れてしまったし、3日間の反省をふまえて、しっかりレースをしたいです。(コメント自信度・・★★☆)"

 大丈夫そう!

COTOHA API を使ってみよう!

 と思ったんですが、データが多い!
 COTOHAの無料版は1日に1000件までなのに、どうやら12324個もデータがあるので間引きましょう。

データを間引こう。

 場所で分ければいいか。。
 どの会場に何個くらいデータがあるかな。

histogram(categorical(TR_comment.Place))

comment_histogram.png

 ピッタリ 100レース x 6人 = 600データで区切りがいいから福岡にしよう。

TR_Fukuoka = TR_comment(TR_comment.Place == "福岡",:);

MATLAB x COTOHA の設定をしよう。

 登録は aoimidori さんの設定を見て、
 https://qiita.com/aoimidori/items/644ac0e726d60a99cc4a

 プログラムは eigs さんの記事を丸パクリすればいいね。
 https://qiita.com/eigs/items/39196afdb5f28bf3ba0f

%% COTOHA設定
clientid = 'input_your_Client_ID';
clientsecret = 'input_your_Client_secret';

url = 'https://api.ce-cotoha.com/v1/oauth/accesstokens';
options = weboptions('RequestMethod','post', 'MediaType','application/json');
Body = struct('grantType', 'client_credentials', ...
    'clientId', clientid, ...
    'clientSecret', clientsecret);
tokens = webwrite(url, Body, options);

超ラク。

センチメント解析をしよう。

 MATLAB の Text Analytics Toolbox にも、vaderSentimentScores とか、いくつか関数があるんですけど、今のところ日本語に対応してなさそうなので COTOHA でやりましょう。

こんな感じでAPIを600回 ( =レース数 x 6人) コールします。

%% センチメント解析
clear response
for n = 1:height(TR_Fukuoka)
    for ix = 1:6
        baseurl = 'https://api.ce-cotoha.com/api/dev/';
        Header = {'Content-Type', 'application/json;charset=UTF-8';
            'Authorization', ['Bearer ' tokens.access_token]};
        Body = struct('sentence', TR_comment.Comment(n,ix), ...
            'sent_len', '1');
        options = weboptions('RequestMethod','post', ...
            'MediaType','application/json','HeaderFields', Header);

        response(n,ix) = webwrite([baseurl 'nlp/v1/sentiment'], Body, options);
    end
end

各順位の人はどのような感情か。

COTOHA から Positive/Negative とか感情分析の結果が出るので、着順ごとに集計してみよう。

%% 順位ごとの感情
T1 = struct2table(response(:,1));
T2 = struct2table(response(:,2));
T3 = struct2table(response(:,3));
T4 = struct2table(response(:,4));
T5 = struct2table(response(:,5));
T6 = struct2table(response(:,6));

figure(1)
subplot(2,3,1),histogram(categorical({T1.result.sentiment})),title('1着')
subplot(2,3,2),histogram(categorical({T2.result.sentiment})),title('2着')
subplot(2,3,3),histogram(categorical({T3.result.sentiment})),title('3着')
subplot(2,3,4),histogram(categorical({T4.result.sentiment})),title('4着')
subplot(2,3,5),histogram(categorical({T5.result.sentiment})),title('5着')
subplot(2,3,6),histogram(categorical({T6.result.sentiment})),title('6着')

 ↓↓結果。

sentiment_rank.png

 みんな大体ポジティブ!(=ポジティブでも負ける)
 1着と6着との傾向がほぼ同じだから、感情ではあんまり分からんね。

スコアで傾向が見えるかな。

 どのくらいポジティブかっていうスコアがあるので、その平均を並べてみよう。
ポジティブだけ集めてスコアを平均します。

score = [...
mean([T1(ismember({T1.result.sentiment},'Positive'),:).result.score]),...
mean([T2(ismember({T2.result.sentiment},'Positive'),:).result.score]),...
mean([T3(ismember({T3.result.sentiment},'Positive'),:).result.score]),...
mean([T4(ismember({T4.result.sentiment},'Positive'),:).result.score]),...
mean([T5(ismember({T5.result.sentiment},'Positive'),:).result.score]),...
mean([T6(ismember({T6.result.sentiment},'Positive'),:).result.score])];
bar(score)

sentiment_score.png

差はあるけど、1着と6着が同じなんだよな・・・
(なぜポジティブが6着なんだ。)

分散はあんまり差が出ませんでした。

1着しか言わない言葉はあるだろうか。

 COTOHAの結果の中に、emotional_phrase っていうのがあったので、1着しか言わない言葉を集めよう。
 せっかくなので、関数にして6回コールして、setdiff で差分を取りましょう。

emo1 = emo_phr(T1);
emo2 = emo_phr(T2);
emo3 = emo_phr(T3);
emo4 = emo_phr(T4);
emo5 = emo_phr(T5);
emo6 = emo_phr(T6);

ph1 = setdiff(emo1,[emo2,emo3,emo4,emo5,emo6])';  % 1位しか言ってないこと。

function emo = emo_phr(T)  % エモい言葉あつめ
emo = [];
for n = 1:height(T)
    for m = 1:length(T.result(n).emotional_phrase)
        emo{end+1} = T.result(n).emotional_phrase(m).form;
    end
end
end

↓↓1着しか言わない言葉の結果。

>> ph1

ph1 =

  91×1  cell 配列

    {'あまり乗り心地はいい'     }
    {'あまり良くなかったですね'  }
    {'いいとか悪い'            }
    {'いい感じです'            }
    {'このままいけそうです'     }
    {'こぼれた'               }
    {'しんどい'               }
    {'そんなにいい'            }
    {'ちょっと足りない'         }
    {'どうか分からない'         }
    {'どうもうまく合わせられない'}
    {'どこかいい'             }
    {'なさそうですよ'          }
    {'はっきり分かっていない'    }
    {'まだいい'               }
    {'まだいい感じはないですね'  }
    {'まだ重たい'             }
    {'もう変わらない'          }
    {'もう少し良くしたい'       }
    {'もったいなかったです'     }
    {'よくなっている'          }
    {'よくなってきていて'       }
    {'よくなる'               }
    {'ギリギリ'               }
    {'バランスがとれていい'     }
    {'バランスがとれています'    }
    {'バランスがとれているね'    }
    {'不安定ですね'            }
    {'中堅よりちょっといい'     }
    {'乗りづらいです'          }
    {'乗りづらかったです'       }
    {'乗りやすかったです'       }
    {'乗りやすくしてしまうと'    }
    {'乗りやすくなった'         }
    {'乗りやすくなっています'    }
    {'何とかいい'             }
    {'信用し切れていないです'    }
    {'信頼して'               }
    {'入念な'                 }
    {'全然ダメでした'          }
    {'全然足らなくて'          }
    {'冷えたので'             }
    {'冷静さ'                 }
    {'劣勢で'                 }
    {'劣勢でした'             }
    {'可能性もある'            }
    {'問題はないですよ'         }
    {'回りづらかったですね'     }
    {'変わらないです'          }
    {'失敗した'               }
    {'失敗しています'          }
    {'失敗してしまって'         }
    {'失敗しない'             }
    {'失敗です'               }
    {'好感触だった'            }
    {'嫌いじゃなかった'         }
    {'少しいい'               }
    {'差がありそうだし'         }
    {'差がありましたね'         }
    {'引き続きバランス良く'     }
    {'弱いな'                 }
    {'弱いね'                 }
    {'影響はありますね'         }
    {'必要ですね'             }
    {'怪しさ'                 }
    {'悔しいですね'            }
    {'悪くないですね'          }
    {'悪くはないかな'          }
    {'悪くはないですよ'         }
    {'慌てて'                 }
    {'懸命な'                 }
    {'文句ない'               }
    {'普通でしょう'            }
    {'普通なので'             }
    {'根本的'                 }
    {'波があると分からないです'  }
    {'渾身'                  }
    {'痛いですね'             }
    {'笑顔'                  }
    {'自信があった'            }
    {'良くして'               }
    {'良くなかったな'          }
    {'良くなってくれていればいい'}
    {'謙遜した'               }
    {'負けてなかった'          }
    {'軽くて'                 }
    {'達成'                  }
    {'違和感がありました'       }
    {'重視して'               }
    {'重視ですね'             }
    {'間違えてしまったよ'       }

 反対の言葉があったりするけど、「自信があった」「文句ない」「バランスがとれて」とかがあればいいのかな。逆に6着はどうだろう。

>> ph6 = setdiff(emo6,[emo1,emo2,emo3,emo4,emo5])'

ph6 =

  74×1  cell 配列

    {'あまりいい'           }
    {'あまり良くなってる'     }
    {'いいかも'             }
    {'いまひとつ'           }
    {'このまま行ってみて'     }
    {'ごまかせる'           }
    {'そんなに変わらない'     }
    {'ただ乗りやすい'        }
    {'どこか気になる'        }
    {'はっきりとは分からない'  }
    {'まだ分からない'        }
    {'もう少し欲しい'        }
    {'もう少し欲しいですね'   }
    {'よく分からなかったです'  }
    {'よさそう'             }
    {'スムーズさ'           }
    {'ズレていました'        }
    {'トップクラス'          }
    {'パワーがある'          }
    {'丁寧な'               }
    {'中堅くらいですね'       }
    {'乗りやすさ重視'        }
    {'人並'                }
    {'全く分からなかった'     }
    {'全然ダメだね'          }
    {'分からなくなりました'   }
    {'力強い'               }
    {'劣勢だった'           }
    {'効果はない'           }
    {'同じくらいであれば'     }
    {'失敗して'             }
    {'安心しました'          }
    {'少しだけ気になる'       }
    {'平行線'               }
    {'弱すぎて'             }
    {'弱すぎる'             }
    {'強めだった'           }
    {'強気'                }
    {'影響もありましたね'     }
    {'微妙に'               }
    {'必要なので'           }
    {'思惑通り'             }
    {'悪くはないです'        }
    {'持っていけた'          }
    {'普通くらいかな'        }
    {'普通よりちょっといい'   }
    {'楽しみはありますよ'     }
    {'残れた'               }
    {'波があったら乗れないです'}
    {'波があると乗りづらい'   }
    {'深くなっても大丈夫です'  }
    {'滑る'                }
    {'無理'                }
    {'物足りないですね'       }
    {'特殊で乗りづらかったです'}
    {'相当高い'             }
    {'素直なので'           }
    {'良かったかな'          }
    {'良くなった'           }
    {'良くなっていない'       }
    {'良くなっている'        }
    {'良くなってます'        }
    {'良くなれば'           }
    {'著しくない'           }
    {'行き過ぎてしまいました'  }
    {'見劣りする'           }
    {'見劣りすると'          }
    {'負けない'             }
    {'追いつかれた'          }
    {'速いですね'           }
    {'違和感がなければ'       }
    {'違和感もない'          }
    {'遠かったです'          }
    {'限界'                }

 「ズレていました」「劣勢だった」「滑る」「無理」「著しくない」「限界」とか言ってる人がいたら6着候補だな。こっちは分かりやすい。

 ついでに、4,5,6着しか言わないこと。

>> ph456 = setdiff([emo4,emo5,emo6],[emo1,emo2,emo3])'

ph456 =

  257×1  cell 配列

    {'あおられてしまった'        }
    {'あまりいい'              }
    {'あまりいい感じ'           }
    {'あまり分かっていない'      }
    {'あまり深くなる'           }
    {'あまり良くない'           }
    {'あまり良くなってる'        }
    {'あまり速い'              }
    {'あんまりいい'             }
    {'いいかも'                }
    {'いいのかな'              }
    {'いまひとつ'              }
    {'うまくいかなかった'        }
    {'このまま行く'             }
    {'このまま行ってみて'        }
    {'ごまかせる'              }
    {'さっぱり'                }
    {'すぐにズレてしまう'        }
    {'ずっとおかしかった'        }
    {'ずっと変わらない'          }
    {'そんなに変わらない'        }
    {'そんなに嫌いな'           }
    {'ただ乗りやすい'           }
    {'だいぶ良くなっていると'     }
    {'ちょっといいかな'          }
    {'ちょっと分かっていない'     }
    {'どうしていい'             }
    {'どこか気になる'           }
    {'なにかおかしい'           }
    {'なんとか残せた'           }
    {'はっきりとは分からない'     }
    {'ひどい'                  }
    {'まだマシ'                }
    {'まだ分からない'           }
    {'まだ微妙ですね'           }
    {'まだ持て余している'        }
    {'まだ普通なので'           }
    {'もう少し欲しい'           }
    {'もう少し欲しいですね'      }
    {'もう少し良く'             }
    {'もう少し良くなる'          }
    {'もっと乗りやすくして行きたい'}
    {'ものすごく'              }
    {'よかったです'             }
    {'よくて'                  }
    {'よくなれば'              }
    {'よく分からないです'        }
    {'よく分からなかったです'     }
    {'よさそう'                }
    {'イマイチでしたね'          }
    {'キツイです'              }
    {'グッとくる'              }
    {'スムーズさ'              }
    {'スローで'                }
    {'ズレていました'           }
    {'トップクラス'             }
    {'バランスがとれて普通ですかね'}
    {'バランスはとれている'      }
    {'バランスもとれて'          }
    {'バランス良く'             }
    {'パワーがある'             }
    {'パワーはあります'          }
    {'パワーアップ'             }
    {'ヤバい'                  }
    {'ラッキーだった'           }
    {'ワースト機'              }
    {'一番良い'                }
    {'丁寧な'                  }
    {'上位級'                  }
    {'上向き'                  }
    {'上手く'                  }
    {'下手でしたね'             }
    {'不可もない'              }
    {'不安はない'              }
    {'不完全燃焼'              }
    {'中堅くらい'              }
    {'中堅くらいですね'          }
    {'乗りづらい'              }
    {'乗りづらいですね'          }
    {'乗りやすいのかな'          }
    {'乗りやすくて'             }
    {'乗りやすくなっていますね'   }
    {'乗りやすさ重視'           }
    {'乗り心地がいいですね'      }
    {'人並'                   }
    {'何とかしのげる'           }
    {'余計'                   }
    {'偏っている'              }
    {'全く分からなかった'        }
    {'全然ダメだね'             }
    {'冷えたし'                }
    {'分かった'                }
    {'分からないですね'          }
    {'分からないね'             }
    {'分からなくなりました'      }
    {'力強い'                  }
    {'劣る'                   }
    {'劣勢'                   }
    {'劣勢かもしれない'          }
    {'劣勢だった'              }
    {'劣勢です'                }
    {'劣勢な'                  }
    {'効果はない'              }
    {'十分いい'                }
    {'可もなく不可もなく'        }
    {'同じ'                   }
    {'同じくらいであれば'        }
    {'向上している'             }
    {'問題はなかったですね'      }
    {'変化はある'              }
    {'多い'                   }
    {'大丈夫そうだった'          }
    {'大丈夫だ'                }
    {'失敗して'                }
    {'失敗してしまいました'      }
    {'好きなので'              }
    {'好成績'                  }
    {'威張れない'              }
    {'威張れるほどでもない'      }
    {'威張れるほどよくないです'   }
    {'嫌いじゃない'             }
    {'嫌いです'                }
    {'嬉しかったです'           }
    {'安定'                   }
    {'安心しました'             }
    {'少しだけ気になる'          }
    {'少しでも良くなる'          }
    {'少しズレていた'           }
    {'少しマシ'                }
    {'差がありますね'           }
    {'平行線'                  }
    {'引きずっていた'           }
    {'弱いかな'                }
    {'弱いです'                }
    {'弱すぎて'                }
    {'弱すぎる'                }
    {'強い'                   }
    {'強かったですね'           }
    {'強めだった'              }
    {'強気'                   }
    {'強烈'                   }
    {'影響があって'             }
    {'影響もあって'             }
    {'影響もあり'              }
    {'影響もありましたね'        }
    {'徐々に良くなってきています' }
    {'微妙に'                  }
    {'必要か'                  }
    {'必要がありますね'          }
    {'必要だ'                  }
    {'必要なので'              }
    {'思い切り'                }
    {'思惑通り'                }
    {'怪しかった'              }
    {'悪いのかな'              }
    {'悪かったり'              }
    {'悪かったりする'           }
    {'悪くなさそうですよ'        }
    {'悪くはない'              }
    {'悪くはないです'           }
    {'悪くもない'              }
    {'情けない'                }
    {'慌しく'                  }
    {'手ごたえがあった'          }
    {'持ちこたえる'             }
    {'持っていけた'             }
    {'持っていける'             }
    {'持て余している'           }
    {'早かった'                }
    {'映えないですね'           }
    {'普通くらいかな'           }
    {'普通だね'                }
    {'普通で'                  }
    {'普通よりちょっといい'      }
    {'最低限'                  }
    {'望んでいない'             }
    {'本当に寒くて'             }
    {'楽しみたいです'           }
    {'楽しみはあります'          }
    {'楽しみはありますよ'        }
    {'欲しいですね'             }
    {'残れた'                  }
    {'気づかなかった'           }
    {'気になっていた'           }
    {'気になりました'           }
    {'気になります'             }
    {'気になりますね'           }
    {'波があったら乗れないです'   }
    {'波があると乗りづらい'      }
    {'深くなっても大丈夫です'     }
    {'滑っている'              }
    {'滑る'                   }
    {'無念'                   }
    {'無理'                   }
    {'物足りないですね'          }
    {'特に悪い'                }
    {'特殊で乗りづらかったです'   }
    {'特殊な'                  }
    {'特筆出来る'              }
    {'狙えていた'              }
    {'猛烈な'                  }
    {'甘いですね'              }
    {'甘くて'                  }
    {'甘くて不安ですね'          }
    {'目立つ'                  }
    {'相当高い'                }
    {'納得できる'              }
    {'素晴らしかったですね'      }
    {'素直で'                  }
    {'素直なので'              }
    {'細かい'                  }
    {'結構いいですよ'           }
    {'緊張'                   }
    {'自信がある'              }
    {'良い状態であれば'          }
    {'良かったかな'             }
    {'良かったね'              }
    {'良かったり'              }
    {'良くしていきたい'          }
    {'良くしていきたいです'      }
    {'良くないですね'           }
    {'良くなった'              }
    {'良くなっていない'          }
    {'良くなっていますよ'        }
    {'良くなっている'           }
    {'良くなってます'           }
    {'良くなりそう'             }
    {'良くなりました'           }
    {'良くなれば'              }
    {'良さそうでした'           }
    {'良さそうな'              }
    {'著しくない'              }
    {'行き過ぎてしまいました'     }
    {'見劣りする'              }
    {'見劣りすると'             }
    {'負けない'                }
    {'足りない'                }
    {'足りなかったです'          }
    {'迷'                     }
    {'追いつかれた'             }
    {'追い付く'                }
    {'速いですね'              }
    {'違和感があります'          }
    {'違和感がある'             }
    {'違和感がない'             }
    {'違和感がなければ'          }
    {'違和感もある'             }
    {'違和感もない'             }
    {'遠かったです'             }
    {'重たいですね'             }
    {'重たかったね'             }
    {'重たくなった'             }
    {'限界'                   }
    {'難しくて'                }
    {'頑張って'                }
    {'骨っぽい'                }
    {'高い'                   }

「劣勢」はたくさん出てくる。「見劣り」「滑る」「無念」「全然ダメ」「情けない」とかあれば4着以下だな。
「骨っぽい」ってなんだ。。

今日のアクセス数の上限になったのでここまで。

 ということで、「何だこの滑るボートは!全然ダメじゃないか!情けない骨っぽい限界だ!無理!」くらい言ってたら舟券から外してもいいと思います。(そんな人いない。)

 あとはもう少しデータを増やしたり、同じ人の良い時との類似度算出をしてもいいかもしれないですね。いずれにせよ 1日1000アクセスまでなので、また明日にしましょう。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2