GA4の集計結果からレポートを自動生成するとき、空の応答を 0 で埋めると「取得できていない」と「取得した結果が0件」を区別できなくなります。PVに一定割合を掛けた推計まで同じ表へ入れると、どの数値で判断してよいかも分かりません。
商品ページの閲覧数と購入リンクのクリック数を集計する例で、値の状態、単位、テストの範囲を整理します。TypeScriptのコードは説明用の最小例です。末尾の実装事例からそのまま抜粋したものではありません。
未取得を数値に変換しない
まず、次の3つを別の状態として扱います。
-
取得値の0件:指定した集計条件で返された数値が0
-
未取得:内訳を問い合わせていない、取得に失敗したなど、必要な値がない
-
推計:PVの8%など、仮定を置いて計算した値
図1:取得値の0件、未取得、推計の区別。説明用の概念図で、実際のGA4データや管理画面ではありません。
たとえば eventName ごとの eventCount だけを取得した応答からは、商品別クリック数は分かりません。商品を識別するディメンションがないためです。ページのPVを別途取得できても、閲覧からクリックへの移行率が一定だとは限りません。
状態を判別できる型にすると、未取得を表示する処理を明示できます。
type CountResult =
| { kind: "reported"; value: number }
| { kind: "missing"; reason: string }
| { kind: "estimated"; value: number; basis: string };
function formatCount(result: CountResult): string {
switch (result.kind) {
case "reported":
return `${result.value} 回`;
case "missing":
return `未取得(${result.reason})`;
case "estimated":
return `推計 ${result.value} 回(${result.basis})`;
}
}
reported は取得結果が数値としてある状態です。計測が正常だと保証する状態ではありません。API応答に対象イベントの行がない場合も、即座に実際の操作が0回だったとは断定せず、集計条件や計測設定を確認します。
この最小例は数値の妥当性検査を省いています。実際に使う場合は、API応答を型へ変換する境界で有限数・非負などを検証し、不正値や取得エラーを黙って0へ変換しないようにします。推計を出すなら、仮定と対象期間を添え、取得値とは区別して表示します。
ユーザーあたりの回数と割合を区別する
次は架空の例です。同じ期間の購入リンククリックが10回、アクティブユーザーが2人なら、10 ÷ 2 = 5回/人です。1人が何度も押せるので、これを500%のコンバージョン率と表示すると意味が変わってしまいます。
GA4の指標定義でも、eventCount はイベント件数、eventCountPerUser はイベント件数をアクティブユーザー数で割った値です。対象イベントだけを合算する場合も、分子を何の件数にしたかを明記します。
クリックした人の割合を知りたいなら、対象イベントを起こしたユニークユーザー数が必要です。分母のユーザー集合と期間もそろえます。購入リンクのクリックを、購入完了や売上として扱うこともできません。
分母が0の場合は「算出不可」と表示します。0回/人とすると、ユーザーがいて誰も操作しなかった状態と区別できません。
表示のテストと実接続の検証を分ける
先ほどの型なら、APIに接続せずに表示上の区別をテストできます。次は同じファイルに追記する最小例です。
import assert from "node:assert/strict";
assert.equal(
formatCount({ kind: "reported", value: 0 }),
"0 回",
);
assert.equal(
formatCount({ kind: "missing", reason: "商品別は未取得" }),
"未取得(商品別は未取得)",
);
assert.equal(
formatCount({ kind: "estimated", value: 80, basis: "PVの8%" }),
"推計 80 回(PVの8%)",
);
説明用コードの実行結果
2026年10月6日、本文のTypeScriptコード2個を同じ article.mts に保存し、Node v24.3.0で node article.mts を実行しました。3件のassertが成功し、終了コードは0でした。成功時に標準出力がないのは、このassert例の正常な動作です。
表示を確認するため、同じコードへ console.log と補助のassertを追記した fixtures.mts も実行しました。「0 回」「未取得(商品別は未取得)」「推計 80 回(PVの8%)」に加え、10回÷2人=5回/人と、分母0を「算出不可」とする補助例を確認しています。
図2:実際に保存した実行ログの画像化です。端末画面のスクリーンショットではありません。Nodeの型除去による実行で、TypeScriptの型検査は行っていません。説明用の固定データのみを使い、Google認証・GA4 APIへの実接続・本番データ取得は行っていません。
取得処理も、APIクライアントを引数で渡せる構造にすると固定応答へ差し替えられます。商品別の情報を返していないのに内訳を生成しないことや、応答が空のときに架空の流入元を補わないことを、外部サービスに依存せず確認できます。
ただし、固定応答のテストが成功しても、認証やGA4プロパティへの権限は確認できません。実測用の関数を呼んで「本番実測データ」という見出しが出ても、APIクライアントを差し替えていれば本番データを取得した証拠にはなりません。
実接続では認証・権限・問い合わせ結果を別に確認します。さらに、画面で行った操作が意図したイベント名やパラメータで記録されるかも確認対象です。テスト結果には、表示処理、固定応答による集計、実接続のどこを確認したかを残します。
集計条件と未実行の施策を表示する
「直近30日」もコードで定義をそろえます。DateRangeの仕様では両端の日付を含むため、30daysAgo から today は31日分です。昨日までの30日なら 30daysAgo から yesterday を指定します。相対日付はGA4プロパティのタイムゾーンで解釈されます。
分析から提案する改善策には「未実行」と付け、観測と仮説を分けます。たとえば「商品ページのPVが多い」は取得値で確認し、「関連記事へのリンクを増やすと回遊が増える」は変更前後で検証する仮説として扱います。
参考実装と確認のポイント
この整理のきっかけになった実装変更では、カードPV×8%によるクリック推計、固定割合による店舗別配分、空の流入元をDirect 100%で補う処理を削除しました。回帰テストは固定のAPI応答を使っています。本番GA4への接続・実データ取得の成功を示す事例ではありません。
自分のレポートへ適用するときは、まず空値を補う処理と割合の計算式を探してみてください。値がない理由、分子と分母の単位、テストが保証する範囲を明示すると、読み手が数値を誤解しにくくなります。

