グラフを描画するプログラムをscratchから書かねばならなくなった時に、軸の目盛り(tick)の間隔をいい感じに設定する方法。

By | 2020年5月14日

はじめに

人間の人生において、少なくとも一度はグラフを描画するプログラムをJFreeChartやChart.jsの力を借りずにscratchから書かねばならない場面に出くわすものです。

この記事ではそんな時におそらく役立つグラフの軸の目盛りの間隔をいい感じに設定する方法について書きます。

スポンサーリンク

やりたいこと

グラフといえば普通は2次元、たまに3次元、もっと気の利いたグラフだと$n$次元のものが思い浮かぶと思いますが、単に軸の数が増えるだけです。また、向きについても片方だけ考慮すれば良さそうですので、1次元のもの(数直線)かつ、データがすべて負でない値をとる場合を考えます。

それで、以下のデータを数直線上に図からはみ出さないようにプロットすることを考えます。

しかし、データを点として単純にプロットするだけですと、数直線を見ただけではデータが分布している範囲がわかりにくいと思います。

そこで、原点以外に$k (k \lt n)$個の目盛りをつけることにします。

2190
1947
1628
2902
581
74
515
4401
1671
3690

上記の$k$個の目盛りをいい感じにつける方法を考えます。

単純な方法

上記のデータ列を例にとって考えます。

$k$も適当な値を置きます。$k = 5$とかにしてみます。原点を含めると6個の目盛りをつけることになります。

上記のデータ列のデータの最大値は4401ですので、5個目の目盛りを4401とすると、隣接する目盛りの間隔は880.2になります。したがって、目盛り付きの数直線は下図のようになります。

目盛りとしては正確なのかもしれませんが、正確すぎるせいかグラフ全体の見た目的にbusyな印象を受けますね。

わかりやすい目盛り

「目盛りの間隔が880.2である。」というのは数字にあまり強くない人にとっては直感的はわかりにくいと思う(※個人の感想です。)ので、もう少しなんとかならないか考えてみます。

例えば、目盛りの間隔を800にすれば、目盛り付きの数直線は下図のようになります。


スポンサーリンク

ちょっと見やすくなったような気がします。

実は最初の図よりも2番目の図の方が画像上における目盛りの幅を意図的に詰めてありますが、それでも2番目の図の方が見やすいように感じますし、「4401」の点を図上の目盛りとしては一番右側に位置する4000の目盛りのさらに右側にプロットすることになったとしてもそれほど違和感を感じないと思います。

解くべき問題

定式化

ここまでの考察で、グラフの目盛りをいい感じに設定するためには、

ある正の実数値$x$が与えられたときに、$x$を10進数で表示したときの最上位の桁以外の桁を0としたときの値$y$を求める。

ことを考えればよさそうです。

上記の記述はどことなく文学的で微妙だということであれば、


スポンサーリンク

ある正の実数値$x$が与えられたときに、

\begin{align}
m\cdot 10^n &\le x \lt (m+1)\cdot 10^n \label{eq:tick}
\end{align}

となるような整数$(m,n), 1\le m \le 9$の組を求め、さらに$y = m\cdot 10^n$を求める。

というように書くことができそうです。

解いてみた(`・ω・´)

(\ref{eq:tick})式の$m$及び$n$は$n = \lfloor \log_{10} x\rfloor$, $m = \left\lfloor\displaystyle\frac{x}{n}\right\rfloor$と計算できますので、$y$は以下のように表すことができます。

\begin{align}
y &= \left\lfloor \frac{x}{10^{\lfloor \log_{10} x\rfloor}} \right\rfloor \cdot 10^{\lfloor \log_{10} x\rfloor} \label{eq:ticky}
\end{align}

なお、$\lfloor x \rfloor$はみんな大好き床関数です。

プログラム例

PHPとかPython3とか

(\ref{eq:ticky})式の$y$を求めるプログラムはPHPだと以下のように書けます。

また、Python3だと以下のように書けます。

実行してみた

スポンサーリンク

PHPのプログラムを実行すると以下のような結果が得られます。

[panda@pandanote.info ~]$ php floor_function.php 12322
10000
[panda@pandanote.info ~]$ php floor_function.php 123
100
[panda@pandanote.info ~]$ php floor_function.php 600
600
[panda@pandanote.info ~]$ php floor_function.php 880.2
800
[panda@pandanote.info ~]$ php floor_function.php 999
900
[panda@pandanote.info ~]$ php floor_function.php 1
1

Python3でも実行してみました。PHP版とは実行時のOSが異なるのは気にしない方向でお願いいたします。

C:\Users\Pandanote>python3 floor_function.py 12322
10000
C:\Users\Pandanote>python3 floor_function.py 123
100
C:\Users\Pandanote>python3 floor_function.py 600
600
C:\Users\Pandanote>python3 floor_function.py 880.2
800
C:\Users\Pandanote>python3 floor_function.py 999
900
C:\Users\Pandanote>python3 floor_function.py 1
1

ちょっとした応用

グラフの目盛りの間隔を決めるという目的で(\ref{eq:tick})式の$m$及び$n$を求める場合に、「目盛りの数は増えてもいいから$m = 1,2,5$のいずれかになるようにしたい。」というニーズもあると思います。

そのようなニーズに応える場合には、以下のように場合分けするとよさそうです。なお、目盛りの数は最大で$\left\lceil\displaystyle\frac{5}{2}k-\displaystyle\frac{1}{2}\right\rceil$本になる可能性があります。

  • $m \ge 5$の場合には$m = 5$とみなす。
  • $m = 2,3,4$の場合には$m = 2$とみなす。
  • $m = 1$の場合にはそのまま$m = 1$とする。
スポンサーリンク

まとめ

この記事の数直線はSVGで作成していますが、今どき(この記事を最初に書いた時点(2020年5月)の時点の情報です。)のブラウザならだいたい対応してますし、ディスプレイの解像度とか気にしなくていいので、割とおすすめです。

…と雑にSVGをおすすめしたところで、この記事は以上です。