STATCAST で遊ぶ vol.2

## 使用している環境
- macOS Sequoia 15.2
- WIndows 11 Pro 24H2
　一応、両環境で同じものを動作させてチェックしています。
- R 4.4.2
- RStudio 2024.12.0+467

## RStudio で STATCAST を CSV で使ってみる

　R の baseballr パッケージで、2025年1月26日現在、STATCAST の search 関連関数が既知の問題により動作していないので、CSV を直接、ダウンロードしてみることにします。

　STATSCAST の CSV ダウンロードは、いくつかの方法があるのですが、今回は、2024年シーズンのバッティングの主要データ（すべてのカラムではないという意味です）をダウンロードしてみます。

 [STATCAST 2024 PLAYER BATTING STATS](https://baseballsavant.mlb.com/leaderboard/custom?year=2024&type=batter&filter=&min=q&selections=pa%2Ck_percent%2Cbb_percent%2Cwoba%2Cxwoba%2Csweet_spot_percent%2Cbarrel_batted_rate%2Chard_hit_percent%2Cavg_best_speed%2Cavg_hyper_speed%2Cwhiff_percent%2Cswing_percent&chart=false&x=pa&y=pa&r=no&chartType=beeswarm&sort=xwoba&sortDir=desc)

 　リンクのページに行くと、Leaderboard（リーダーボード、任意のカラムの順位一覧表）が表示されるので、上段のフィルタ欄で「CUSTOM COLUMNS(12)」のドロップダウンメニューを開き、「Default」、「Basic Stats」、「Statcast」の３つを選択し、クローズボタンを押します。
  ![スクリーンショット 2025-01-26 15.47.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/946ef366-c134-3fbb-3e2f-8294c45c1747.png)

　正しく選択していれば、CUSTOM COLUMNS が (39) に変わっているはずです。
カラム数を確認したら「Download CSV」ボタンで、ダウンロードします。

　これで、2024年のSTATCAST規定到達の選手のバッティングデータを取得できました。
 STATCAST規定以外の選手のデータが欲しいときは、「MINIMUM PA」のフィルタを変更します。
 PA とは Plate Appearance（打席）のことです。

　ダウンロードしたファイルは、「stats.csv」という名前になっていると思いますが、何をダウンロードしたのか、自分で命名ルールを決めて、名前を変更しておきましょう。
　ここでは、以下に変更しました。
 
 `2024_batting_d-bs-s.csv`
 
  意味としては、2024年のPLAYER BATTING データのカラム Default、Basic Stats、Statcast ということです。

  名前を変えたら、RStudio の working directory（作業ディレクトリ）に放り込みます。
  RStudio の「Files」で「2024_batting_d-bs-s.csv」が見えるか確認できたら。

```
player_batting <- read.csv("./2024_batting_d-bs-s.csv")
```

オブジェクト player_batting に、ダウンロードしたデータが「**データフレーム**」格納されました。
この player_batting から列を抽出して、解析に使用します。

```
batting_avg <- as.numeric(player_batting$batting_avg)
```

これは、変数 batting_avg へ オブジェクト player_batting の 列 batting_avg を numeric 型へ変換して格納するという意味になります。

内容を確認します。

```
batting_avg
```

```
  [1] 0.332 0.279 0.214 0.211 0.214 0.244 0.242 0.248 0.237
 [10] 0.219 0.252 0.283 0.262 0.265 0.234 0.259 0.267 0.229
 [19] 0.248 0.196 0.278 0.272 0.229 0.253 0.246 0.260 0.257
 [28] 0.243 0.259 0.256 0.258 0.295 0.256 0.273 0.224 0.248
 [37] 0.279 0.247 0.249 0.238 0.245 0.246 0.292 0.247 0.225
 [46] 0.251 0.246 0.238 0.243 0.308 0.257 0.292 0.271 0.235
 [55] 0.231 0.281 0.275 0.280 0.281 0.250 0.310 0.231 0.299
 [64] 0.267 0.272 0.252 0.266 0.295 0.285 0.273 0.323 0.237
 [73] 0.232 0.253 0.258 0.224 0.269 0.239 0.251 0.314 0.241
 [82] 0.259 0.271 0.293 0.220 0.256 0.248 0.219 0.242 0.250
 [91] 0.322 0.242 0.289 0.259 0.272 0.292 0.249 0.260 0.224
[100] 0.275 0.247 0.285 0.282 0.246 0.233 0.245 0.250 0.218
[109] 0.220 0.266 0.262 0.240 0.302 0.282 0.281 0.288 0.254
[118] 0.243 0.241 0.269 0.275 0.254 0.280 0.233 0.269 0.243
[127] 0.273 0.260 0.278
```

　各行の[] で囲まれた数字は、次に表示される値が何番目の値なのかを表現しています。
 当然、ウインドウの大きさに左右されるので、この数字は、環境ごとに異なります。

 ここまで、書いて、最初に打率を選んでしまったことを、少々、後悔しています。
 ホームランの方が分布の段階が少なく分かりやすかったかもしれません。

## 一つの変数の数値要約

 　気を取り直して、このまま続けましょう。

　この打率データの統計の数値要約を見てみましょう。数値要約は、対象データを代表する値とざっくり理解しておきましょう。

```
summary(batting_avg)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.1960  0.2420  0.2560  0.2581  0.2750  0.3320 
```

各意味は、
- Min. 最小値
- 1st Qu. 第一四分位数
- Median 中央値
- Mean 平均
- 3rd Qu. 第三四分位数
- Max. 最大値

　2024年 MLB のSTATCAST規定到達の打者の平均打率は、.258、中央値は、.256 だということがわかりました。
 「ある現象」、この場合は打率ですが、大小、様々な値で起こること、これを「**分布する**」という風に表現します。
 　この場合、「**MLBの打者の打率は分布する**」と表現します。

## 平均

　summary()関数を使えば、平均は求めることが出来ますが、確認のため、手作業で求めてみましょう。
 平均は、n個のデータの値を合計し、n で割ることで求めます。

 ```math
\overline{X} =  \frac{x_1 + x_2 ... x_n}{n}
```

 R で合計を求めるの sum() 関数を使い、データの個数は length() 関数で求められますので、

 ```
sum(batting_avg) / length(batting_avg)
```
```
[1] 0.2581473
```

 実は R には、平均を求める mean() 関数もあります。

```
mean(batting_avg)
```
```
[1] 0.2581473
```

　同じ値が得られました。

 ## 中央値

 　平均以外の代表値として用いられるものに中央値があります。中央値は、データセットを昇順、または、降順でソートして、上から数えて、丁度、真ん中にくる値です。batting_avg のデータ個数は 129 個です。

```
length(batting_avg)
```
```
[1] 129
```

　丁度、真ん中ですから、65番目の値ということになります。データ個数が偶数の場合は、真ん中の２つのデータの平均をとるのが通常です。

 　平均と中央値、どちらがいいかはデータの分布の仕方によって変わります。ほとんどのデータはある範囲に収まっているのに、極端に違う値が含まれる。例えばSTATCAST規定に到達しているのにも関わらず打率.000 の選手がいたり、一人だけ.400割打者がいるようなケースでは、中央値の方が優れている場合があります。
  summary() の結果は、大差ないので、こういう場合は平均で良いでしょう。

## データのばらつきを調べる〜分散と標準偏差

　分散は、データセットの散らばり具合を表す値です。分散は、以下の式で求めます。

 ```math
s = \frac{(x_1 - \overline{x})^2 + (x_2 - \overline{x})^2 + ... + (x_n - \overline{x})^2}{n}
```

 (各データ - データセットの平均)の二乗を合計し、データ個数で割ります。これは厳密には標本（sample）の分散です。

　データサイエンスでは標本の分散の代わりに、不偏分散を使用する場合があります。これは、抽出した標本の偏りを取りぞのいて、母集団（population）の分散を推定することを言います。式は以下のようにデータ個数から1を引いて求めることで不偏分散を求めることが出来ます。
 
  ```math
s = \frac{(x_1 - \overline{x})^2 + (x_2 - \overline{x})^2 + ... + (x_n - \overline{x})^2}{n - 1}
```

　しかし、今、やってるのは、母集団＝標本なので、標本分散で問題ないはずです。R を含め、言語や Excel、FileMaker 等のアプリケーションで用意されている関数の場合、標本分散ではなく不偏分散の方が通常なので、注意して利用しましょう。Excel には両方用意されています。

 　では、手作業で、標本分散（この場合、=母集団の分散）を R で求めてみますが、後で、答え合わせをするために、不偏分散も求めます。

```
batting_avg_avg <- mean(batting_avg) # 打率の平均を求める
batting_avg_avg
```
```
 [1] 0.2581473
```
```
batting_avg_dev <- batting_avg - batting_avg_avg # 各データの平均からの偏差を求める
batting_avg_dev
```
```
 [1]  0.0738527132  0.0208527132 -0.0441472868
 〜　中略 〜
[127]  0.0148527132  0.0018527132  0.0198527132
```
```
batting_avg_dev2 <- batting_avg_dev ** 2 # 求めた偏差の二乗する
batting_avg_dev2
```
```
 [1] 5.454223e-03 4.348356e-04 1.948983e-03 2.222867e-03
 〜　中略 〜
[125] 1.177814e-04 2.294403e-04 2.206031e-04 3.432546e-06
[129] 3.941302e-04
```
```
batting_avg_sumdev2 <- sum(batting_avg_dev2) # 偏差の二乗を合計する（二乗和）
batting_avg_sumdev2
```
```
[1] 0.0810222
```
```
batting_avg_varp <- batting_avg_sumdev2 / length(batting_avg) # 標本分散
batting_avg_varp
```
```
[1] 0.0006280791
```
```
batting_avg_var <- batting_avg_sumdev2 / (length(batting_avg) - 1) # 不偏分散
batting_avg_var
```
```
[1] 0.0006329859
```

　答え合わせのために、R の 関数で、不偏分散を求めてみましょう。

```
var(batting_avg)
```
```
[1] 0.0006329859
```

　求めた不偏分散 batting_avg_var と一致しました。

 　不偏分散から、母集団の分散を求めてみましょう。

```
batting_avg_var * (length(batting_avg) - 1)
```
```
[1] 0.0810222
```

　これは、先ほど計算した batting_avg_sumdev2 （偏差の二乗の合計）と一致してますから、もう正解なのはわかってますけど、続行します。

```
(batting_avg_var * (length(batting_avg) - 1)) / length(batting_avg)
```
```
[1] 0.0006280791
```

　`batting_avg_varp` と一致しました。因みに var は variance（分散）を略したもので、p は、population（母集団）を略しています。

 　続いて、標準偏差を求めます。分散が求まっていれば、標準偏差は簡単に求められます。

　標本分散から求める場合は、
　
  ```math
  s = \sqrt{\frac{(x_1 - \overline{x})^2 + (x_2 - \overline{x})^2 + ... + (x_n - \overline{x})^2}{n}}
  ```

　不偏分散から求める場合は、

```math
  s = \sqrt{\frac{(x_1 - \overline{x})^2 + (x_2 - \overline{x})^2 + ... + (x_n - \overline{x})^2}{n - 1}}
  ```

  このような式になりますが、√の中身は、もう計算していますので、単純に、平方根を取れば、標準偏差が求められます。
　標本分散からの標準偏差、不偏分散からの標準偏差、答え合わせまで一気にやってみましょう。

```
batting_avg_stdevp <- sqrt(batting_avg_varp) # 標本分散からの標準偏差
batting_avg_stdevp
```
```
[1] 0.02506151
```
```
batting_avg_stdev <- sqrt(batting_avg_var) # 不偏分散からの標準偏差
batting_avg_stdev
```
```
[1] 0.02515921
```
```
sd(batting_avg) # sd() 関数で不偏分散からの標準偏差を求める
```
```
[1] 0.02515921
```

　標準偏差とは、あるデータセットでは、標準的に平均からの散らばり方を程度で示しています。

 ## 分布をデータビジュアライゼーションしてみる第一歩

 　計算ばかりしていましたが、ちょっとまとめてみましょう。

```
mean(batting_avg) # 平均
```
```
[1] 0.2581473
```
```
min(batting_avg) # 最小値
```
```
[1] 0.196
```
```
max(batting_avg) # 最大値
```
```
[1] 0.332
```
```
batting_avg_varp # 標本分散
```
```
[1] 0.0006280791
```
```
batting_avg_stdevp # 標本分散からの標準偏差
```
```
[1] 0.02506151
```
```
batting_avg_var # 不偏分散
```
```
[1] 0.0006329859
```
```
batting_avg_stdev # 不偏分散からの標準偏差
```
```
[1] 0.02515921
```

<table>
  <caption>2024年 MLB 打率(STATCAST規定以上)</caption>
    <tr>
      <td>平均</td><td>0.2581473</td>
    </tr>
    <tr>
      <td>最小値</td><td>0.196</td>
    </tr>
    <tr>
      <td>最大値</td><td>0.332</td>
    </tr>
    <tr>
      <td>標本分散</td><td>0.0006280791</td>
    </tr>
    <tr>
      <td>標本分散からの標準偏差</td><td>0.02506151</td>
    </tr>
    <tr>
      <td>不偏分散</td><td>0.0006329859</td>
    </tr>
    <tr>
      <td>不偏分散からの標準偏差</td><td>0.02515921</td>
    </tr>
</table>

　2024年の MLB のSTATCAST規定以上の平均打率は、.258 で、最低打率は .196、最高打率 は .332。分散と標準偏差は表の通り。
 　このデータをグラフにしてみましょう。
　表のX軸の範囲は、最低・最高打率を含める範囲ということで、.190〜.340 としておきましょう。

 ```
 > curve(dnorm(x, mean = mean(batting_avg), sd = batting_avg_stdevp), from = 0.19, to = 0.34)
```

![5af7db3b-9af4-4aa3-a0e6-a9a729ea22da.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/ff483aff-6075-f8a1-bb4d-0ff26408511a.png)

sd = の部分で、標準偏差を使っていますが、ここは、当然ですが、分散の平方根を使っても同じです。

```
curve(dnorm(x, mean = mean(batting_avg), sd = sqrt(batting_avg_varp)), from = 0.19, to = 0.34)
```

　打率の分布が、なんとなくイメージできるようになりました。

 ## まとめ

 　今回は、CSV データをダウンロードして、数値データとして扱うということを、多少強引でしたが、やってみました。