⚖️

rechartsで微妙な差分を図示する

2024/12/25に公開

はじめに

小型ペットの体重記録アプリを開発中、体重の変化を可視化するために recharts で折れ線グラフを実装しました。
体重が22g前後で、折れ線グラフのY軸を0〜30くらいにすると、小型ペットには比較的大きな変化である1、2gの変化がグラフ上でほとんど確認できませんでした。

そこで、微妙な体重変化を可視化するために以下の方法をとりました。

最終形

以下のように現在の体重データ周辺だけを表示するように。

工夫したこと

Y軸の範囲

まずは、データ周辺だけ表示するよう、domain で範囲指定しました。
のちにY軸の目盛りを設定すると、このプラスマイナスの範囲は正確に反映されなくなりますが、一旦プラスマイナス2とします。

<YAxis
  yAxisId={1}
  domain={["dataMin - 2", "dataMax + 2"]} // ここ
  tick={{
    fontSize: 12,
    fill: "#001858",
  }}
/>

X軸の表示

Y軸が0から始まっていないので、詐欺グラフっぽくならないよう(そういう用途ではないので関係ないかもですが)はじめは <XAxis /> ごと削除してX軸を非表示にしていました。
しかし <XAxis /> をなくすとデータ点クリックでも日付が表示されなく不便なので、X軸の直線だけを非表示にして日付は表示することにしました。

<XAxis
    dataKey="date"
    tick={{
      fontSize: 10,
      fill: "#2f3138",
    }}
    axisLine={false} // ここ
/>

Y軸の目盛り

<YAxis /> でY軸の目盛りに数字を表示できますが、デフォルトでは小数点刻みの微妙な値になってしまいました。

5刻みくらいで表示したかったので、「平均値 - 5」「平均値」「平均値 + 5」の値を表示するよう、計算した値を ticks パラメータにセットしました。
もっと幅広くデータが変化する場合は目盛りが足りないかもしれませんが、今回の用途ではこの範囲で十分でした。

また、avgWeightState の値がセットされる前にグラフが表示されると、値がセットされた後に表示が更新されないため、avgWeightState が確定してからグラフを表示するようにしました。

const [avgWeightState, setAvgWeightState] = useState<number>();

// データの平均値計算
useEffect(() => {
    if (!data) return;
    let totalWeight = 0;
    let count = 0;
    data.map((d: Weight) => {
      totalWeight += d.weight;
      if (d.weight) count++; // weightの値がある要素数をカウント
    });
    // truncではなくroundにすることでデータに近い値になる
    setAvgWeightState(Math.round(totalWeight / count / 5) * 5);
}, [data]);

...

{avgWeightState && ( // 値が確定するまで表示しない
    <YAxis
      yAxisId={1}
      domain={["dataMin - 2", "dataMax + 2"]}
      ticks={[avgWeightState - 5, avgWeightState, avgWeightState + 5]} // 5刻みの値を表示
      tick={{
        fontSize: 12,
        fill: "#001858",
      }}
    />
)

おわりに

値の変化を自力で実装することなく、簡単に実現でき感謝です。

Discussion