DEV Community

Hajime
Hajime

Posted on

同時視聴者数1万と記事3本を、同じ物差しに載せる

複数の指標を1本のスコアにまとめる仕事は、見た目ほど単純ではありません。

「配信の同時視聴者数」と「メディアに出た記事の本数」を足したい、という場面を考えてみてください。前者は数万単位で動き、後者は0本から数本の間に収まります。そのまま足せば、前者がすべてを決めてしまう。重みで調整しようにも、そもそも単位が揃っていません。

ゲームタイトルの熱量を、配信・検索・記事・コミュニティなど10種類以上の公開データから合成しているダッシュボードがあります。その設計の考え方を材料に、単位の違う指標をどう1つの物差しに載せるかを整理します(公開情報は2026年9月時点)。

GameAxiz

→ 10種類以上の公開データを合成したゲーム熱量スコアの GameAxiz

1. まず「その日の中での位置」に直す

最初の一手は、各指標を同じ範囲に写すことです。このダッシュボードでは、系列ごとにその日の全タイトル中の最大値を基準にして0〜100へ直しています。

// 系列ごとに、その日の最大値を 100 とする
function toRelative(value: number, dayMax: number): number {
  if (dayMax <= 0 || value <= 0) return 0;
  return Math.min(100, (value / dayMax) * 100);
}
Enter fullscreen mode Exit fullscreen mode

この時点でスコアの意味が決まります。結果は絶対量ではなく、その日の顔ぶれの中での相対的な位置です。サイト側もこの点を明記していて、0〜100は「絶対的な人気を表すものではない」と断っています。

相対化の代償は、日をまたいだ比較が素直にできなくなることです。昨日の70と今日の70は、比べている相手が違います。変化を追うなら、スコアそのものより「1週間前との差」のように差分として扱うほうが扱いやすくなります。

2. 線形のままだと、下位が全部0に潰れる

上の関数には問題があります。視聴者数やレビュー数のような量は、上位と下位で桁が3つも4つも違います。

最大値が100万のとき、1,000の値は線形だと0.1点です。ランキングの大半が0点付近に貼り付き、中位の差がまったく見えなくなります。

こういう桁違いの差がつく量は対数に写すのが定石です。

function toRelativeLog(value: number, dayMax: number): number {
  if (dayMax <= 0 || value <= 0) return 0;
  return (Math.log10(1 + value) / Math.log10(1 + dayMax)) * 100;
}
Enter fullscreen mode Exit fullscreen mode

1 + を入れているのは、値が1以下のときに負の対数が出ないようにするためです。これで最大100万に対して1,000は約50点になり、中位のタイトル同士の違いが目に見える幅で出てきます。

3. ただし、小さい整数を対数にすると逆に壊れる

では全部対数にすればいいかというと、そうはいきません。

記事の本数や大会の数のような小さな整数を対数に写すと、今度は1本あるだけで大きな点が付きます。最大が5本の日に1本は、対数だと4割近い点数になります。「1本出た」と「5本出た」の差が、実感よりずっと小さく見えてしまう。

そこで指標ごとに尺度を宣言します。

type Scale = "log" | "linear";
type Signal = { key: string; scale: Scale; weight: number };

function normalize(v: number, max: number, scale: Scale): number {
  return scale === "log" ? toRelativeLog(v, max) : toRelative(v, max);
}
Enter fullscreen mode Exit fullscreen mode

判断の基準は「その量の分布が何桁にまたがるか」です。

量の性質 例 尺度
桁違いに開く 同時視聴者数、再生数、レビュー数 対数
小さな整数に収まる 記事本数、大会の件数 線形

4. ストックとフローを混ぜない

もう1つの落とし穴は、性質の違う量を同じ鍋に入れることです。

Steamのレビュー数やコミュニティの参加者数は、長い時間をかけて積み上がった量(ストック)です。一方、今日の配信視聴者数や今週の記事本数はいま起きていること(フロー)です。

ストックを熱量と同列に混ぜると、発売から何年も経った大型タイトルが、何も起きていない日でも上位に居座ります。「いま熱いもの」を見たいのに、「これまで大きかったもの」が答えとして返ってくるわけです。

このダッシュボードでは、ストックを「規模」として別枠にまとめ、総合スコアへは小さく効かせる形を取っています。規模を無視はしないが、日々の動きを覆い隠さない、というバランスです。

const flow = weighted(flowSignals);   // 配信・検索・記事など
const stock = weighted(stockSignals); // レビュー数・コミュニティ規模など
const total = mix(flow, stock);       // stock 側の比重は小さくとる
Enter fullscreen mode Exit fullscreen mode

なお、この「規模」をそのまま売上と読むのは誤りです。サイトでも、販売規模はSteamのレビュー数を基にした推定であり、家庭用ゲーム機の売上は含まないと明記されています。指標の名前が読み手の期待とずれるときは、画面の上で但し書きを添えるのが誠実だと思います。

5. 地域ごとに別々に合成する

最後に、合成を1回で済ませない理由です。

国内で話題でも海外では静か、あるいはその逆、というタイトルは珍しくありません。両方をまとめて平均すると、偏りの大きいタイトルほど中くらいの点数に丸められ、特徴が消えます。

そこで重みのセットを地域ごとに持ち、国内スコアと海外スコアを別々に算出します。公開されている説明では、国内側は日本語版Wikipediaの閲覧数と国内メディアの記事本数が中心、海外側はTwitchの視聴者数と英語版Wikipediaが中心です。

スコアの内訳

詳細ページでは、総合・国内・海外の3つを並べ、どの指標が押し上げているかも表示しています。合成した数字を出すなら、分解した姿も一緒に出す。単一のスコアに対する疑問のほとんどは、内訳が見えるだけで解消します。

まとめ

  • 単位の違う指標は、まず系列ごとに同じ範囲(0〜100)へ写す。結果は相対値になる
  • 桁違いに開く量は対数、小さな整数は線形。指標ごとに尺度を宣言する
  • 積み上がった量(ストック)は別枠にし、日々の動き(フロー)を隠さない比重で混ぜる
  • 偏りそのものに意味があるなら、平均せずに軸を分けて合成する
  • 合成スコアには内訳を添える

どれも個別には地味な判断ですが、1つでも抜けると「見た目はそれらしいのに、誰の実感とも合わない数字」ができあがります。

本ページはプロモーションが含まれています

Top comments (0)