FileMaker Data API を使う 2025年版 vol.17

 [本記事まで対応しているソースコード](https://github.com/GetMyselfBack/filemaker-data-api-examples/tree/main)
 ※ vol.15 までのバグ修正あり

 現在の構造
```
.
├── css
│   ├── bootstrap-common.css
│   └── fmda-form.css
├── csv
│   └── standings.csv
├── find-records-test.php
├── find-records-test2.php
├── find-records-test3.php
├── html
│   └── edit-record.html
├── index.php
├── js
│   ├── fmda-form-check.js
│   └── fmda-select-page.js
├── json
│   ├── html-contents.json
│   ├── standings-al-east-2024.json
│   └── standings-all-2024.json
└── php
    ├── bs-builder-class.php
    ├── bs-frame-class.php
    ├── filemaker-data-api-class.php
    ├── php-functions.php
    ├── preferences.php
    └── temporary-functions.php
```

## FileMaker Pro の計算フィールド

　さて、実際にリクエストしたいわけですが、現在のフォームでは、以下のフィールドが空欄です。
 - 勝率（任意）
 - ピタゴラス勝率（任意）
 - X-W/L（任意）
 - 勝率差分（任意）

 これを FileMaker Pro の場合、計算フィールドでやりたくなってしまうでしょうが、計算フィールドは便利なようで、非常に厄介な存在でもあります。
 FileMaker Pro 特有の計算フィールドは非常に便利なのですが、関連テーブルが絡むとパフォーマンスに影響が大きく、また、単一テーブルでの使用であっても、多用すると、テーブル構造が分かりにくくなったり、アジャイル開発に適した FileMaker Pro であるはずなのに、テーブル改変で、足を引っ張る存在にもなり得ます。
 そういったデメリットから、なるべく使わないようにしたい存在でもあります。

 空欄になっている部分は、いずれも簡単な計算かつ単一テーブルなので、計算フィールドを使っても問題にはありませんが、習慣として、計算フィールドは原則、使わない方針でやっていきたいと思います。

## 勝率

　勝率(winning_percentage)の計算は、以下の式になります。

```math

 winning\% = \frac{wins}{wins\ +\ losses}
```
　JavaScript を使って、勝利数（wins）と敗戦数（losses）を取得して、計算すれば計算フィールドのような挙動になります。

 ### fmda-form-check.js に追加
```
{
  // 勝率の計算
  'use strict';
  // 勝利数、敗戦数のタグを取得
  const wins    = document.getElementById("wins");
  const losses  = document.getElementById("losses");
  console.log(wins);
  console.log(losses);
  
  // 値が変更されたときに呼び出される関数
  wins.addEventListener("input", calcWinningPercentage());
  losses.addEventListener("input", calcWinningPercentage());

  // 勝率を計算
  function calcWinningPercentage( )
  {
    const winningPercentage = Number(wins.value) / (Number(wins.value) + Number(losses.value));
    document.getElementById("winning_percentage").value = winningPercentage.toFixed(3).slice(-4);
    console.log(winningPercentage);
  }
}

```

## ピタゴラス勝率

 ピタゴラス勝率（Pythagorean Expectation）はビル・ジェームズが考案したチームの得点と失点から期待できる勝率を算出するというものです。
 そして、非常にチームの勝率と相関があるもので、様々な改良版も開発されています。
 ビル・ジェームズ版は以下の式になります。
```math
Pythagorean\ Expectation\ Win\% = \frac{runs\ scored^2}{runs\ scored^2\ +\ runs\ allowed^2}
```
 これは、映画『マネーボール』でも登場しますね。原作では、ビル・ジェームズについても大きく取り上げられています。
 
 現在、打者指標で OPS（出塁率+長打率）が重視されるのも元を辿ればピタゴラス勝率で、出塁と長打が得点算出に有効であることから、その能力を表す OPS が重視されるわけです。

 MLB では、[BASEBALL-REFERENCE.COM](https://www.baseball-reference.com) の改良版である以下の式が採用されています。

```math
Pythagorean\ Expectation\ Win\% = \frac{runs\ scored^{1.83}}{runs\ scored^{1.83}\ +\ runs\ allowed^{1.83}}
```

 これも、得点（runs_scored）と失点（runs_allowed）を取得して計算してしまいます。

### fmda-form-check.js に追加
```
{
  // ピタゴラス勝率の計算
  'use strict';
  // 累乗の指数指定
  //const power = 2;    // Bill James
  const power = 1.83;   // BASEBALL-REFERENCE.COM

  // 得点、失点のタグを取得
  const runsScored  = document.getElementById("runs_scored");
  const runsAllowed = document.getElementById("runs_allowed");

  // 値が変更されたときに呼び出される関数
  runsScored.addEventListener("input", calcPythagoreanExpectation(power));
  runsAllowed.addEventListener("input", calcPythagoreanExpectation(power));

  // ピタゴラス勝率を計算
  function calcPythagoreanExpectation(power)
  {
    const pythagoreanExpectation = (Number(runsScored.value) ** power) / ((Number(runsScored.value) ** power) + (Number(runsAllowed.value) ** power));
    document.getElementById("pythagorean_expectation").value = pythagoreanExpectation.toFixed(3).slice(-4);
    console.log(pythagoreanExpectation);
  }
}

```

## X-W/L
 X-W/L（Expected Win-Loss）とは、ピタゴラス勝率から算出した勝敗数の期待値です。
 MLB は現在、全開催で162試合ですから、これにピタゴラス勝率を掛ければ期待勝利数を計算できますね。
 全開催という意味は、MLB では、ポストシーズンのスポットがすべて決まった場合、雨天中止などで残っている消化試合を開催しない場合があるので、必ずしも全チームが162試合を消化するわけではないという意味で、敢えて全試合と書いています。

 これは、ピタゴラス勝率（pythagorean_expectation）を取得すれば計算できますね。
### fmda-form-check.js に追加
```
{
  // 期待勝敗数(x-W/L)の計算
  'use strict';

  // ピタゴラス勝率のタグを取得
  const pythagoreanExpectation  = document.getElementById("pythagorean_expectation");

  // 値が変更されたときに呼び出される関数
  pythagoreanExpectation.addEventListener("input", calcExpectedWinLoss());

  // x-W/L を計算
  function calcExpectedWinLoss( )
  {
    const expectedWins    = Math.round(162 * (Number(pythagoreanExpectation.value)));
    console
    const expectedLosses  = 162 - expectedWins;
    const expectedWinLoss = expectedWins + '-' + expectedLosses;
    document.getElementById("expected_win_loss").value = expectedWinLoss;
    console.log(expectedWinLoss);
  }
}
```

## 勝率差分

　最後に勝率とピタゴラス勝率との差です。
 プラスなら、ピタゴラス勝率以上に勝てた、マイナスならピタゴラス勝率ほど勝てなかったという意味になりますね。

 式は、以下です。
 ```math
Difference =  Winning\ Percentage - Pythagorean\ Expectation
```

 勝率（winning_percentage)とピタゴラス勝率（pythagorean_expectation）を取得して計算します。

### fmda-form-check.js に追加
```
{
  // 勝率差分の計算
  'use strict';
  
  // 勝率、ピタゴラス勝率のタグを取得
  const winningPercentage       = document.getElementById("winning_percentage");
  const pythagoreanExpectation  = document.getElementById("pythagorean_expectation");

  // 値が変更されたときに呼び出される関数
  winningPercentage.addEventListener("input", calcWinningPercentageDiﬀerence());
  pythagoreanExpectation.addEventListener("input", calcWinningPercentageDiﬀerence());

  // 勝率差分の計算
  function calcWinningPercentageDiﬀerence( )
  {
    const winningPercentageDiﬀerence = Number(winningPercentage.value) - Number(pythagoreanExpectation.value);
    document.getElementById("winning_percentage_diﬀerence").value = winningPercentageDiﬀerence.toFixed(3);
    console.log(winningPercentageDiﬀerence);
  }
}
```

 これで全フィールドが埋まりました。

 次回は、Edit Record のリクエストをして、このフォームの値を FileMaker ソリューションの更新をしたいと思います。