0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FileMaker Pro で度数分布表、ヒストグラムを作成する

Last updated at Posted at 2025-03-21

使用環境

Claris FileMaker Pro Version 21.1.1.41 (macOS)

使用データ

MLB 公式 BaseballSavant STATCAST Player Batting, Qualifiers, 2024
2024年シーズンの規定到達の打者129名の基本データ

ダウンロードした stats.csv を名前変更(テーブル名、ここでは player_batting と仮定)した上で、新規テーブルにインポート。
1行目は、フィールド名としてインポートする。

プラン

インポートして作った player_batting テーブルにある batting_avg フィールド(各選手の打率)は分布するということを調べる。

① 以下を計算して、データを数値要約する

  • 総和
  • 平均値
  • 最大値
  • 最小値
  • 中央値
  • 範囲
  • 分散
  • 標準偏差
  • 母集団分散
  • 母集団標準偏差

② 棒グラフで代用して、ヒストグラムを描く
③ 度数分布表を描く

スクリーンショット 2025-03-21 9.16.14.png

ルール

  • FileMaker Pro 特有の計算フィールドおよび集計フィールドは使わない
  • スクリプトステップ Loop は使わない

ソリューションファイルの準備

FileMaker Pro で新規作成を行うと初期テーブルが作成されますが、これは名前変更 session としてインポートには使用しません。
ダウンロードしたデータは新規テーブル、テーブル名 player_batting としてインポートします。

インポートしたデータの最初のフィールドはlast_name, first_name となっており、半角スペースが含まれています。このフィールドを扱うときは、変更した方がいいでしょう。

 この段階で、テーブルオカレンスは、sessionplayer_batting の2つですが、意味的な分類で、色分けしておくことをお勧めします。
session を1つ、player_batting を2つ複製し、名前変更します。以下のように命名します。

ベーステーブル テーブルオカレンス名
session _SESSION
player_batting _PLAYERBATTING
player_batting __playerbatting_PLAYERBATTING

_PLAYERBATTING__playerbatting_PLAYERBATTING はデカルト積で自己リレーションシップを作成します。ここでは、フィールド player_idyear 同士を「x」記号で繋げています。

レイアウト作成(仮)

以下のようにレイアウトを作成します。

レイアウト名 テーブルオカレンス名 配置するフィールド
_SESSION _SESSION 現時点でなし
_PLAYERBATTING _PLAYERBATTING なし
__playerbatting_PLAYERBATTING __playerbatting_PLAYERBATTING batting_avg

OnWindowFirstOpen 用のスクリプト作成

とりあえず、空のスクリプトを以下の名称で作成します。

スクリプト名
OnFirstWindowOpen
PlayerBattingStats

OnFirstWindowOpen スクリプトをスクリプトトリガ OnFirstWindowOpen に設定します。

ファイル > ファイルオプション... > スクリプトトリガ > ☑️ OnFirstWindowOpen「OnFirstWindowOpen」

OnFirstWindowOpen スクリプトは、起動時に、レイアウトを切り替え、計算を実行させるために使用します。以下のスクリプトステップを設定します。

OnFirstWindowOpen
レイアウト切り替え [ 「_PLAYERBATTING」 (_PLAYERBATTING) ; アニメーション: なし ]
スクリプト実行 [ 指定: 一覧から ; 「PlayerBattingStats」 ; 引数:    ]

グローバル変数のレイアウトへの配置

今回は、フィールドにはデータを保存しないで、グローバル変数に計算結果を表示する方法を採ります。
_PLAYERBATTING レイアウトに以下のグローバル変数を配置します。
グローバル変数は、レイアウトモードで、挿入 > マージ変数 で配置できます。

見出し グローバル変数名
レコード数 $$LENGTH
総和 $$SUM
平均値 $$MEAN
最大値 $$MAX
最小値 $$MIN
中央値 $$MEDIAN
範囲 $$RANGE
分散 $$VAR
標準偏差 $$SD
母集団分散 $$VARP
母集団標準偏差 $$SDP

母集団は、129名の選手しかいないので、分散、標準偏差を出す必要はありませんが、流用しやすいように加えています。

FileMaker Pro の関数による計算

計算はすべて、PlayerBattingStats スクリプト内の計算式で行います。まず、最初と最後を示します。

PlayerBattingStats
ウインドウの固定
全レコードを表示
レコード/検索条件/ページへ移動 [ 最初の ]
# 
# ここから計算







# ここまで
# 
レイアウト切り替え [ 元のレイアウト ; アニメーション: なし ]

計算式はすべて、変数を設定スクリプトステップ内で行います。
 この計算をする際に、最初に作った自己リレーションシップが効きます。

あるレイアウトで計算した場合、選択されている現在のレコードを対象に計算するのが FileMaker の基本です。

それでは、1レコード分しか計算できませんので、現在のレコードの関連レコードを対象に計算を行う必要があります。

デカルト積は、一つのレコードに対してすべてのレコードが繋がるので、この場合、_PLAYERBATTING レイアウトでどのレコードが選択されていようと、__playerbatting_PLAYERBATTING のすべてのレコードが関連レコードとなり、129名分のデータを計算対象にできます。

まず、FileMaker Pro に用意されている関数で計算できるものを計算してしまいます。

レコード数と総和
変数を設定 [ $$LENGTH ; 値: Count ( __playerbatting_PLAYERBATTING::batting_avg ) ]
変数を設定 [ $$SUM ; 値: Sum ( __playerbatting_PLAYERBATTING::batting_avg ) ]
平均値、最大値、最小値
変数を設定 [ $$MEAN ; 値: Average ( __playerbatting_PLAYERBATTING::batting_avg ) ]
変数を設定 [ $$MAX ; 値: Max ( __playerbatting_PLAYERBATTING::batting_avg ) ]
変数を設定 [ $$MIN ; 値: Min ( __playerbatting_PLAYERBATTING::batting_avg ) ]
分散、標準偏差、母集団分散、母集団標準偏差
変数を設定 [ $$VAR ; 値: Variance ( __playerbatting_PLAYERBATTING::batting_avg ) ]
変数を設定 [ $$SD ; 値: StDev ( __playerbatting_PLAYERBATTING::batting_avg ) ]
変数を設定 [ $$VARP ; 値: VarianceP ( __playerbatting_PLAYERBATTING::batting_avg ) ]
変数を設定 [ $$SDP ; 値: StDevP ( __playerbatting_PLAYERBATTING::batting_avg ) ]

度数分布表、ヒストグラムの階級の作成に必要なデータの準備

まず、打率データの最大値から最小値までの範囲を計算しておきます。

範囲
変数を設定 [ $$RANGE ; 値: $$MAX - $$MIN ]

次に、スタージェスの公式を使って、この範囲の階級数を決めます。

k = log_2N +1
階級数
変数を設定 [ $NUMBER_OF_LEVELS ; 値: Round ( 1 + Lg ( $$LENGTH ) ; 1 ) ]

N はレコード数で、計算結果を四捨五入しています。
また、これはレイアウトには必要ないので、ローカル変数を使っています。

ここで、データビューアかカスタムダイアログを使って、Round ( 1 + Lg ( $$LENGTH ) ; 1 ) の結果を把握しておきます。一度、OnFirstWindowOpen を実行すれば、$$LENGTH には値が入っています。
結果は、8 となるはずです。

階級の幅と階級の数で階級を作成

範囲と、階級の数が決まったので、階級を作成します。
まず、間隔を決めます。これもローカル変数です。

各階級の間隔
変数を設定 [ $LEVEL_RANGE_STEP ; 値: Ceiling ( $$RANGE * 100 / $NUMBER_OF_LEVELS ) / 100 ]

範囲を階級数で割っているだけです。打率らしく見せるために、範囲を100倍して割って、それを切り上げた後、100で割って戻しています。

次に、階級の始点(正確には、始点を含まない「超」ということになります)を計算します。これもローカル変数です。

階級の始点
変数を設定 [ $LEVEL_RANGE_FROM ; 値: Ceiling ( $$MIN * 100 ) / 100 - $LEVEL_RANGE_STEP ] 

 最小値を含めばいいわけですから、間隔に準じて、最小値を100倍にして切り上げ後、100で割り、間隔分減算します。

次に、階級の終点(こちらは含む「以下」ということになります)を計算します。これもローカル変数です。

階級の終点
変数を設定 [ $LEVEL_RANGE_TO ; 値: Ceiling ( $$MAX * 100 ) / 100 + $LEVEL_RANGE_STEP ]

 最大値を含むには、切り上げ計算後、間隔分加算しておけば含まれることになります。

階級の作成

階級の間隔、始点(含まない)、終点(含む)が決まったので、各ラベルを作成し、値一覧(Value List)にします。計算式は、別に書きます。これはグローバル変数です。

階級の作成
変数を設定 [ $$CLASSIFIED ; 値: ]
$$CLASSIFIED の計算式
While (
	[
		~from	= $LEVEL_RANGE_FROM ;
		~to		= $LEVEL_RANGE_TO ;
		~step	= $LEVEL_RANGE_STEP ;
		~item	= ""
	 ] ;
	~from < ~to ;
	[
		~item	= GetAsText ( ~item ) & Left ( GetAsText ( ~from ) & "000" ; 4 ) & ¶ ;
		~from	= ~from + ~step
	] ;
	Left ( ~item ; Length ( ~item ) - 1 )
)

計算した、始点、終点、間隔を、変数 ~from~to~step に代入します。ラベル用の ~itemを用意しておきます。

 繰り返し処理を行なって、.000 形式で文字列を作成し、値一覧は改行区切りリストですから1個作成毎に改行 を追加します。

 最後に、終端の改行を取り除いています。ここでは必須ではありませんが、値一覧同士を結合するケースもあるので、習慣として値一覧を作成したときは、最後の は取り除くということに統一しておいた方がいいと思います。

度数のカウント

次に度数をカウントします。これは、各打率データがどの階級に含まれるかをカウントするということです。
グローバル変数 $$FREQUENCY にカウントした各階級の度数を値一覧として代入します。

度数
変数を設定 [ $$FREQUENCY ; 値: ]

計算式です。

$$FREQUENCY の計算式
While (
	[
		~record_number	= 1 ;
		~length			= $$LENGTH ;
		~classified			= $$CLASSIFIED ;
		~current			= "" ;
		$COUNTER[1]		= 0 ;
		$COUNTER[2]		= 0 ;
		$COUNTER[3]		= 0 ;
		$COUNTER[4]		= 0 ;
		$COUNTER[5]		= 0 ;
		$COUNTER[6]		= 0 ;
		$COUNTER[7]		= 0 ;
		$COUNTER[8]		= 0 ;
		~index			= ""
	 ] ;

	~record_number ≤ ~length ;

	[
		~current		= GetAsNumber ( GetNthRecord ( __playerbatting_PLAYERBATTING::batting_avg ; ~record_number ) ) ;
		~index		= Case (
			~current > ( GetAsNumber ( GetValue ( ~classified ; 1 ) ) ) and
			~current ≤  ( GetAsNumber ( GetValue ( ~classified ; 2 ) ) ) ;
				1;

			~current > ( GetAsNumber ( GetValue ( ~classified ; 2 ) ) ) and
			~current ≤  ( GetAsNumber ( GetValue ( ~classified ; 3 ) ) ) ;
				2;

			~current > ( GetAsNumber ( GetValue ( ~classified ; 3 ) ) ) and
			~current ≤  ( GetAsNumber ( GetValue ( ~classified ; 4 ) ) ) ;
				3;

			~current > ( GetAsNumber ( GetValue ( ~classified ; 4 ) ) ) and
			~current ≤  ( GetAsNumber ( GetValue ( ~classified ; 5 ) ) ) ;
				4;

			~current > ( GetAsNumber ( GetValue ( ~classified ; 5 ) ) ) and
			~current ≤  ( GetAsNumber ( GetValue ( ~classified ; 6 ) ) ) ;
				5;

			~current > ( GetAsNumber ( GetValue ( ~classified ; 6 ) ) ) and
			~current ≤  ( GetAsNumber ( GetValue ( ~classified ; 7 ) ) ) ;
				6;

			~current > ( GetAsNumber ( GetValue ( ~classified ; 7 ) ) ) and
			~current ≤ ( GetAsNumber ( GetValue ( ~classified ; 8 ) ) ) ;
				7;

				8
			) ;
			$COUNTER[~index]	= $COUNTER[~index] + 1 ;
			~record_number	= ~record_number + 1
	] ;

	$COUNTER[1] & ¶ &
	$COUNTER[2] & ¶ &
	$COUNTER[3] & ¶ &
	$COUNTER[4] & ¶ &
	$COUNTER[5] & ¶ &
	$COUNTER[6] & ¶ &
	$COUNTER[7] & ¶ &
	$COUNTER[8]
)

 階級数 8 とわかった上での計算式です。

レコードをレコード番号(変数 ~record_number)で順番に参照していきます。最後のレコード番号は対象レコード数ですので、変数 ~length に、グローバル変数 $$LENGTH を代入します。カウントする際の評価のため、先ほど作った階級の値一覧が入ったグローバル変数 $$CLASSIFIED を変数 ~classified に代入して使っています。

ロジックは、GetNthRecord 関数で参照する関連レコード __playerbatting_PLAYERBATTING の現在のレコード番号 ~record_number のフィールド batting_avg の値が、$$CLASSIFIED のどの階級に含まれるかを評価して、下から順番に振った階級番号を変数 ~index にセットします。
セットされら番号を数値インデックスとして、ローカル配列(繰り返し変数) $COUNTER[] でカウントしていきます。
FileMaker Pro の配列は、1 から始まるので、1 から 8 までの ~index によって、$COUNTER[] がインクリメントされるということです。

最後のレコードが評価されると、各インデックスごとの $COUNTER[] の値が決まっているので、これを順番通りに値一覧としています。

ここで一旦、保存します。

グラフの作成

 FileMaker のグラフは、レイアウトオブジェクトとしてのグラフと JavaScript アドオンの簡易グラフがあります。今回は、レイアウトオブジェクトを使います。 JavaScript アドオンを使用したい場合は、グローバル変数ではなく、計算結果保存用のテーブルを作って設定することになります。

グラフを配置したら以下のように設定します。

グラフ
項目 内容
タイトル "2024年 MLB 規定到達選手の打率のヒストグラム"
タイプ 縦棒グラフ
X 軸 (水平): タイトル "階級(打率)"
X 軸 (水平): データ $$CLASSIFIED
Y 軸 (垂直): タイトル "度数(人数)"
X 軸 (水平): データ $$FREQUENCY
Y 軸: 目盛 線形
Y 軸: 補助目盛(小)を表示 5
Y 軸: 最小値を設定 0
Y 軸: 最大値を設定 55
データソース
項目 内容
グラフデータ 現在のレコード(区切りデータ)

 OnFirstWindowOpen スクリプトを実行すると、縦棒グラフによるヒストグラムが表示されるはずです。

中央値の計算

FileMaker には中央値を求める関数はありませんので、自分で算出します。PlayerBattingStats の続きになります。

ソートする
レイアウト切り替え [ 「__playerbatting_PLAYERBATTING」 (__playerbatting_PLAYERBATTING) ; アニメーション: なし ]
レコードのソート [ 記憶する ; ダイアログあり: オフ ]

ソートの記憶内容は、__playerbatting_PLAYERBATTING::batting_avg を降順ソートです。

続いて、中央値を計算し、グローバル変数 $$MEDIANに代入します。

$$MEDIAN の設定
変数を設定 [ $$MEDIAN ; 値: ]

計算式は以下の通りです。

$$MEDIAN の計算式
Let ([
	~is_odd	= Mod ( $$LENGTH ; 2 )
	] ;

	Case (
		~is_odd = 1 ;
			GetNthRecord ( __playerbatting_PLAYERBATTING::batting_avg ; Ceiling ( $$LENGTH / 2 ) ) ;
		~is_odd = 0 ;
			Let ([
				~temp	= GetNthRecord ( __playerbatting_PLAYERBATTING::batting_avg ; Ceiling ( $$LENGTH / 2 ) ) ;
				~temp	= ( ~temp + GetNthRecord ( __playerbatting_PLAYERBATTING::batting_avg ; Truncate ( $$LENGTH / 2 ; 0 ) ) ) / 2
				] ;
				~temp
			)
		)
)

レコード数が奇数であるかを調べるため、レコード数の2の剰余をとり、奇数なら 1、偶数なら 0 がセットされます。
それを条件として、Case 関数を使って、処理を振り分けます。

奇数の場合は、2で割った値の切り上げで算出したレコード番号の打率を取得します。

偶数の場合、レコード数を2で割った値の切り捨てと切り上げて算出した2つのレコード番号を使って、打率を取得し平均を出します。

度数分布表を作成する

 FileMaker Pro で、クロス集計等の表を作りたいときは、Web ビューアを使用して、HTML でテーブルを作成します。

まず、session テーブルに表のデータ部分を除いた、HTML のコードを保存するグローバルフィールドを作成し、_SESSION レイアウトに配置します。

session テーブルに追加するグローバルフィールド
名前 タイプ
html_begin テキスト
html_end テキスト

今回は、Bootstrap の Card コンポーネントにテーブルを配置してみました。各グローバルフィールドの内容は以下の通りです。

html_begin フィールドの内容
data:text/html,
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2024 シーズン 打率</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
  </head>
  <body>

    <div class="container">
      <div class="row mt-3">
        <div class="col">
          <div class="card">
            <div class="card-header">
              2024 シーズン 打率 度数分布表
            </div>

            <div class="card-body">
              <table class="table table-dark table-striped">
                <thead>
                  <tr>
                    <th scope="row">度数</th>
                    <th scope="row">人数</th>
                  </tr>
                </thead>
              
                <tbody>
                

FileMaker 内で、HTML ソースコードを持つ場合は、データ HTML として扱います。
ソースコードの冒頭に data:text/html, が必要になります。

html_end フィールドの内容
                </tbody>
             </table>
           </div>
         </div>
       </div>
     </div>
   </div>
   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
 </body>
</html>

データ部分は、Web ビューアの計算式で作成します。
_PLAYERBATTING レイアウトでレイアウトモードに移り、Web ビューアを配置します。
 以下の計算式を、Web アドレスに設定します。

Web ビューアの Web アドレスの計算式
_SESSION::g_html_begin &

While (
   [
   	~table_row_begin		= "<tr><th>" ;
   	~table_data_begin	= "</th><td>" ;
   	~table_row_end		= "</td></tr>" ;
   	~table_data			= "" ;
   	~value_count			= ValueCount ( $$CLASSIFIED )	 ;
   	~current_value		= 1
   ] ;

   ~current_value < ~value_count ;

   [
   	~table_data		= ~table_data &
   		~table_row_begin &
   		GetValue ( $$CLASSIFIED ; ~current_value ) & " - " & GetValue ( $$CLASSIFIED ; ~current_value + 1 ) &
   		~table_data_begin &
   		GetAsText ( GetValue ( $$FREQUENCY ; ~current_value ) ) &
   		~table_row_end ;
   	~current_value	= ~current_value + 1
   ] ;

   ~table_data
) &
_SESSION::g_html_end

階級と度数の値一覧から、一つづつデータを取り出し、テーブルの行を作っています。
これを階級数分だけ繰り返します。

この処理を、先ほど作ったグローバルフィールドに設定した開始と終了の HTML で挟みます。

 起動時にスクリプトは実行されるので、起動すれば計算、グラフ描画、表作成すべて完了しますが、スクリプト実行用のボタンを付けても良いでしょう。

まとめ

 私の体感ですが、計算フィールド、集計フィールド、Loop スクリプトステップは、パフォーマンスが目に見えて落ちる場合があります。
今回は、統計学の初歩の初歩を題材にしましたが、何かの参考になれば幸いです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?