2014年5月29日木曜日

C#でWebからデータを取ってくる

C#でwebからデータを取ってくるってことはあまり考えたことがなかったのですが、ちょっとプロ野球の得点分布を知りたいなと思った時に、いちいちデータを手打ちで集めるのも面倒だから自動化出来ないかなぁと考えて色々さがしてみたところ、C#でもhtmlのDOMを自在に操れるHtmlAgilityPackなるものがあるらしいのでちょっとテスト。

使い方としてはまず using System.Net でネットワーク関連のusingをもって来ておいて、HttpWebRequestとHttpWebResponseでデータを読み込みます。この時にもっと単純に取ってくるWebClientというクラスもあるのですが、それよりもrequestとresponseでストリーミングで読み込んだほうがUI(フォーム表示用)スレッドと読み込みスレッドを分離できるので、読み込み時にフォームが固まることを防げるのでよいらしいです。

WebClientよりHttpWebRequestを使った方がいい理由
http://d.hatena.ne.jp/kabakiyo/20120224/1330066622

あとはその読み込んだstringをHtmlAgilityPackにほり込んで、SelectNodesでjQuery的に検索してやれば勝手にほしいとこだけを検索して出してくれるという流れです。とってもシンプルでいいですね。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;

namespace web
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        string baseurl = "http://baseball.yahoo.co.jp/npb/game/";

        private void button1_Click(object sender, EventArgs e)
        {
            DateTime dt_now = DateTime.Now;
            string date = dt_now.ToShortDateString().Replace("/", "");

            // 今日の試合を読み込むためにurlを加工
            string url = baseurl + date + "01" + "/stats";
            //ex).url = http://baseball.yahoo.co.jp/npb/game/2014052801/stats

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            req.Method = "GET";
            HttpWebResponse res = (HttpWebResponse)req.GetResponse();
            Stream s = res.GetResponseStream();
            StreamReader sr = new StreamReader(s);
            string context = sr.ReadToEnd();
            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            doc.LoadHtml(context);

            HtmlAgilityPack.HtmlNodeCollection _nodes = doc.DocumentNode.SelectNodes("/html[1]/body[1]/table[1]/tr");
            if (_nodes == null)
                break;
            foreach (var node in _nodes)
            {
                textBox1.Text += node.InnerText + "\r\n";
            }
        }
    }
}

2014年5月23日金曜日

久しぶりに

めっちゃ前に作ってた野球シミュレータの改良をやりました。

たまにはいじらないと自分で書いたコードが暗号に見えるようになりますね。すでに意味不明部分多数で手遅れ感満載ですが。

ほんとうはjavascriptかなにかweb上で動くもので作れればいろんな人に使ってもらえる可能性も増えて、いろいろと改良する点も意見としてもらえると思うのですがいかんせんC#しか使えないということと今現在新しい言語をいじくる余裕があまりないというので停滞中です。一応javascriptの本とか買ったんですけどね。ほとんど手付かず状態です。

とはいっても、C#でうまく設計さえできれば他の言語でかくのも結構楽にできるんではないかと(勝手に)予想してるので当分の目標はC#のWindowsWPFアプリケーションとして仕上げたいなぁともくろんでます。今のところ新しいやつでは

  • DHありなしの対戦
  • CSVで登録する選手を任意に決定できる
  • 先発ローテーション(最終的には抑えの登板とかも)実装
  • 天候、パークファクターの導入(まあこれは別にいいっちゃいいかな)

といった当たりをできるようにすることを目標にしてます。さっき見返してたら地味にもうこれ作り始めてから半年以上経ってるんですよねこれ。完成するのいつかな…

2014年5月21日水曜日

セイバーメトリクスな数字

打率、ホームラン、打点、勝ち星、得点圏打率というよく見る数字に比べて、出塁率や長打率、あるいはOPS(ここらへんは割りと浸透してるのかな?),XR,Fip,Dip,IsoD,IsoP,LOB%……とかいったセイバーメトリクス的な数字は市民権をまだまだ得れていない感じがあります。人によってはセイバーメトリクスの数字は実際の試合を見ていない数学オタクがでっち上げたもんでそんなのには意味が無いとかいう人もいたり。

じゃあそもそも、打率ってそんなに自然な数字なの?という話がでてきます。何気なく使っている打率ですが、あれの計算方法も全ての安打を無差別に足し算するという作業をしていますから、当然恣意的な指標です。しかもフォアボールはカウントされませんから、フォアボールを選ぶことは打率を上げると言う観点からいうと全く無意味です。ですから打率だけで打者の能力を図ろうとするときには、そういった消えた要素があることを考えていないと、間違った結論を導く可能性があります。

たとえば、ノースリーから毎回打ちに行ってフォアボールは選ばないし打っても毎回単打の3割バッターと、打てばほとんどホームラン、フォアボールも選びまくる3割バッター、この二人が同じになってしまうのが打率です。ここまで酷いことは無いと思いますが、そのような可能性があることは頭の何処かに考えて置く必要があります。

打点とか勝ち星とかいったものに関してはもっと不条理で、ヒットは打つものの毎回運悪くランナー無しで回ってくる弱小チームの四番よりも、周りが打ちまくるせいで毎回ランナーがいる状態で回ってくる打者の方が明らかに有利ですし、勝ち星なんてのは自分が打たれまくっても打線がバカバカ打てば増えるし、去年のいつかのDeNAの高崎投手みたいに、自責点0失点2で尚且つ自分がヒットを打って1点とって8回まで投げたとしても打線が打てずに負けてしまえば負けになるというある意味論理的でない無茶苦茶な数字です。

このように、指標の妥当性という面からセイバーメトリクスな数字を批判するのは無理があります。結局は、打率とOPSなにがちがうのかっていうのは、見慣れているのかそうではないのか、ただその違いに尽きると思います。

例えば今一般によく用いられる指標としてセーブとかHP(ホールドポイント)がありますが、あれはつい最近まで日本球界には存在しませんでした。しかし先発完投型の試合構成であったのが、先発→中継ぎ→抑えという分業制へと移行していく段階で、途中で登板する投手の地位向上が図られ、中継ぎを評価する指標として導入されたのがセーブといった概念です。では、セーブが無い時代の抑えで活躍した選手に価値がないかというとそんなことは無いですよね。今の時代の人と同じようにチームの勝利に貢献しているはずですから、同じように評価されるべきです。しかし、その選手がいた当時にはそんな概念がありませんから、おそらくは先発投手に比べてかなり選手としての価値が低いとみなされていたでしょう。

このセーブやホールドポイントと同じように、今一般に普及している指標だけが万能で十分である、とは必ずしも言い切れないはずです。そのためいろいろな側面から選手を見てやる方法として、セイバーメトリクスがあるんだと思っています。打者指標のIsoDを見れば、どれだけフォアボールを選ぶ傾向があるバッターであるかが、投手指標の一つであるFipを見れば、守備の影響から独立してどの程度その投手が抑えることが出来るのかといったことをある程度客観的に判断できます。

従来指標が絶対だ!という立場を頑なに守ろうとすると、結果として現実を見ることができなくなる恐ろしさがあります。

この記事は http://www.fangraphs.com/blogs/a-response-to-bob-ryan/を読んで思ったことをつらつら書いた(というか多分に影響を受けてい書いた)のですが、あらためて物事を認知する難しさや主観の危うさを感じれた気がします。

さきほどの内容も、セイバーメトリクスの指標を使う時にそれが絶対だという立場にたてば結局おなじことになるわけで、そこは常に注意して行かないといけないところです。

I think telling an accurate story is more important than telling a comfortable one, and the reality is that the numbers that are commonly used generate stories that are factually incorrect. ........
We’re not replacing the “Holy Trinity” of baseball statistics because we can’t enjoy the game. We’re pointing out that these statistics breed false narratives, and we value the truth.
A Response to Bob Ryan http://www.fangraphs.com/blogs/a-response-to-bob-ryan/

2014年5月15日木曜日

ランナー二塁でのチームバッティング

今日の巨人対ヤクルト戦、5回ノーアウトランナー二塁、バッター比屋根の場面。このような場面で間違いなく実況が言うのが、「バッターは最低でもファーストもしくはセカンドゴロを打てるように流し打ちを心がけて欲しいですね」というコメントです。今日も解説の江川がそのようなことを話していました。

確かにライト方向へのゴロならば2塁ランナーはサードへ進めますし、もしサードやショートへの強烈な当たりであればランナーは動けず流れが悪くなるというのも一理あります。

しかし、当然それを見越してピッチャーも左バッターならばアウトコースへのストレート、右打者へならインコースへのチェンジアップなどを軸に流し打ちをさせない投球をしてくるでしょう。そのような流し打ちをしてヒットを打つことが難しい場面において、監督は無理に流し打ちを意識させる必要があるのでしょうか。

まず、ノーアウトランナー二塁と言う状況では平均的にどの程度の点数を得ることが出来るのかを確認します。以下の表は2013年のNPB全試合においてノーアウトランナー二塁の状況が発生した後得られた点数の平均値を表しています。

situation アウトカウント 得点期待値
2 0 1.022
これを見るとノーアウトランナー二塁は平均的に見れば1点取ったことと同義であると言えます。では次にこの状況から起こりうる状況変化を考えます。

ノーアウトランナー二塁で起こりうるものとして、四死球、シングルヒット、ツーベース、スリーベース、フライもしくは2,3塁へのゴロアウト、1,2塁へのゴロがありますが、今回は簡単のためスリーベースとホームランは考えないこととします。

バッターの結果による次の状況の得点期待値が次の表です

situation アウトカウント 得点期待値 差分
2 1 0.713 -0.309
3 1 0.873 -0.149
1-2 0 1.41 0.388
1-3 0 1.736 0.714
これを見ると思ったより1アウトランナー3塁の得点期待値がよろしくないです。ノーアウト2塁時よりも得点期待値はだいたい0.15点ほど減少します。当然1アウトランナー2塁のほうが低いのですが、くらべても0.2点以下の差しかありません。

一方もし打者がヒット、もしくはフォアボールを選べば得点期待値はぐっと上がります。フォアボールでも0.4点、ヒットなら0.7点分の価値です。ここには載せていませんが、ノーアウトランナー1塁での得点期待値が0.44であることを考えると、このシチュエーションでヒットを一本追加することは、ノーアウトでランナー無しの時に単打もしくはフォアボールを選ぶときに得られる得点期待値よりも1.7倍ほどの価値があることがわかります。

しかも、ランナー二塁で強打が成功しツーベースを放てば、1点獲得した上にその後の得点期待値は1.022のままですし、ホームランを打つ可能性も当然含まれますから、強打策は更に大きな得点寄与となることが予想できます。

以上のことから、長打が見込まれるバッターや、ライト方向への打球を打とうとすると打率や長打率の下がってしまう選手は、普通に打たせるべきだということが言えるでしょう。

今日の試合の場面ではバッターは比屋根ですから、長打は少ないし次の川端バレンティンで点を取れば良いと考えて右打ちを指示することは、それほど間違った選択ではないですし、むしろセオリー通りです。

しかし続く打者が川端、バレンティン、雄平ということは、裏を返せば打撃能力が高く大量得点が見込める場面であるとも言えます。また比屋根は右打者で、尚且つ今シーズン3割近い打率を誇り、打球方向は主に左に寄っています(http://baseballdata.jp/playerB/1100052.htmlによると33打席中ライトへ飛んだ打球はわずか4球です)から、比屋根はそこまで右打ちが得意であるようには見受けられません。したがって、右打ちを指示した時の打率が下がってしまう可能性は十分考えられます。

加えてヤクルトは現在救援陣の防御率が極端に悪く3点差程度であれば試合後半にひっくり返されてしまうというリクスがあります。以上のことを鑑みると、確実に1点を取るよりも強打で大量点を取ってトドメを指すという指示もありではないのか、と思ったりもします。

2014年5月9日金曜日

本を買いました

一冊280円という安さにつられてついつい買ってしまいました。技術系の本を読んでると、一冊1000円ぐらいまで安く感じてしまうのは病気だと思います。

今回もまたAmazonで。立ち読みしなくても欲しいなと思う本は殆どAmazonかもしくはhontoで買うようになりました。

特にhontoは定期的に200円~500円offのクーポンを配ってくれるので貧乏学生にはとても助かります。まわしもんではないですが結構オススメです。欠点は発送がちょっと遅いことですかね。

与謝野晶子とか読んだこと一切なかったのでちょっとチャレンジですね。青空文庫でざっと見た感じ古文の授業を受けてないに等しい僕には厳しい気もしますが頑張ります。

2014年5月8日木曜日

異なるリーグに所属する選手同士の対戦予測

今回は異なるリーグに所属する選手同士が、あるリーグで対戦した時にどの程度の成績を残すのかということを考えてみたいと思います。今まで考えていたのはおなじリーグ内部での話でしたから、最終的に少しリーグ平均のOddsでいじれば良かったのですが、投手打者そして今現在所属するリーグが異なっている場合ちょっとややこしくなります。

具体的には打者が打高のリーグに所属投手が投高のリーグに所属そして現在のリーグの反発係数が2つのリーグに比べて高いというような場合です。 ボールが飛びやすかったり、球場が狭かったりと言った平均的に打者が有利なリーグに所属している場合は、打者および投手の安打発生Oddsは高くなる傾向にあります。逆の場合も同様で、したがって異なるリーグに所属している選手同士を対戦させようとする時は、それぞれが所属するリーグの影響をまず取り除き、その上でodds ratioを考えるのが妥当でしょう。

Oddsを用いる成績予測の考えの根底には、選手の突出度をOdds同士の比であるOdds Ratioによって比較するという概念があります。この作業は、数学や物理でいうところの規格化手法です。規格化とは、ある値を基準として、自分の大きさをそれの何倍かで表すという考えです。これを考えれば、打者、投手、今考えているリーグをLb,Lp,Lthisとすると $${Odds Ratio}_b=\frac{Odds_b}{Odds_{Lb}}$$ $${Odds Ratio}_p=\frac{Odds_p}{Odds_{Lp}}$$

という風に表せます。これを掛け算することで、打者と投手を合わせた突出度を計算できます。そしてそれに今打者と投手が所属するリーグのOddsを掛け算することで、今いるリーグの平均と比べることができ、合理的にOdds Ratioを算出出来ます。 $$Odds Ratio_{AvsBonLthis}= Odds Ratio_b\cdot Odds Ratio_p\cdot Odds_{Lthis} $$

  • $Odds Ratio_{AvsVLthis}$ : AとBがリーグLで対戦した時のオッズ
  • $Odds Ratio_b$ : Batterのオッズ比、リーグでの卓越度
  • $Odds Ratio_p$ : Pitcherのオッズ比、リーグでの卓越度
  • $Odds_{Lthis}$ : PitcherとBatterが所属するリーグのオッズ

ex).平均打率0.300のリーグで0.350打つバッターと平均打率0.270のリーグで被打率0.280のピッチャーが超低反発球が導入された平均打率0.220のリーグに所属する場合の期待される打率 $$ \begin{aligned} Odds Ratio_{Lthis} &=\frac{0.350/(1-0.350)}{0.30/(1-0.30)}\cdot \frac{0.28/1-0/28)}{0.27/1-0.27}\cdot 0.220/(1-0.220)\\ &= 0.372\\ &\therefore P = \frac{0.372}{1-0.372} = 0.271 \end{aligned} $$

しかしこの考え方にも欠点があります。それは、比べるリーグのレベルが明らかに異なる場合です。

例えば少年野球チームで平均以上にヒットを打つバッターと、プロ野球選手だけど1軍ではボコボコに打たれまくった投手が対戦するときなどにはこの方法を用いてはいけません。Oddsの考え方の基礎にあるのは、その選手が平均値からどれだけ突出した選手なのか、ということに焦点を合わせて結果を推定するというものです。

そのため、比べる2選手の所属するリーグの選手の能力が基本的にはほとんどおなじであるということが大前提となっています。したがって、同一のリーグでも大きく年代が異なる選手同士の対戦成績予測や、NBPとMLBといった明らかに能力の平均値が異なるリーグの選手同士の成績予測にはこの方法を用いるのは適切ではありません。

2014年5月7日水曜日

複数の可能性がある確率でのOddsRatioの使い方

前回に引き続いて、OddsRatioのお話

OddsRatioが勝率の違うチーム同士の対戦成績や、ある打者と投手が対戦した時の打率や奪三振がどの程度の確率で起こるのかを計算する際にとても有効であることについて書きました。

今回考えて見たいのは、確率の分岐先が複数存在する場合です。

2014年5月6日火曜日

野球シミューレーションを作る時に大事なこと

FanGraph今月のまとめ!みたいな記事に乗ってた、シミュレータを作る上で学んだ10のこととかいう最近ネットでありがちなタイトルの記事がなかなかおもしろいことを書いてあったので、ちょっとメモ