LoginSignup
0
0

More than 3 years have passed since last update.

1次元配列の分布をターミナルで可視化して簡単にデバッグする

Posted at

初書:2020/11/09
php:7.4.10

前置き

タイトルで何言ってるか微妙に伝えづらかったので、説明。
何をしたいのかというと、例えば乱数生成器を作成したとする。その関数がどれくらい偏っているかを簡単に調べたい。
ただグラフを書こうとするとターミナルで実行しても表示できない(知識がないだけかもしれないが)ので、簡
単に可視化する方法がないか考え、棒と色で可視化させようとしたもの。

目標の出力

前置きでも分かりづらいので、画像で(色がmarkdownだと表示できなかった)
スクリーンショット 2020-11-10 13.23.28.png
ちなみにこれは正規分布乱数を出力している。
こんな感じで、出力の偏りを色で簡単に見れるというもの。

ちなみにターミナル用に作成しているので、いわばデバッグ用である。

コード

先にコードを置いておく。今回はgithubも作成してみたので、ダウンロードして速攻使えるようにしてある。
distribution_show/distribution_show.php at main · yuu-1st/distribution_show
(初めてgithubを使ったので変な使い方してるかも)

で、改めてコード。少し長い。

/**
 * ターミナル上で、一次元分布を可視化します。(need PHP 7.3.0)
 *
 * @param array $arr 分布配列。キーは全て整数値である必要があります。
 * @param integer $interval 出力間隔。値は切り捨てで圧縮されます。
 * @param int $maxwidth 出力時の横幅。指定文字数を超えると改行されます。
 * @return void
 */
function distribution_show(array $arr, int $interval = 1, int $maxwidth = PHP_INT_MAX) : void
{
    ksort($arr); 
    $min = array_key_first($arr);
    $max = array_key_last($arr);
    $value_max = 0;
    $array = [];
    for ($i = $min; $i <= $max; $i++) {
        $key = (int)floor(($i - $min) / $interval);
        $array[$key] ??= 0;
        $array[$key] += isset($arr[$i]) ? $arr[$i] : 0;
        $value_max = $array[$key] > $value_max ? $array[$key] : $value_max;
    }
    $console = "";
    $co = 0;
    $coplus = 0;
    for ($i = 0; $i < count($array); $i++) {
        if ($array[$i] > 0) {
            $color = (int)floor((255 - 232) * ($array[$i] * 1.0 / $value_max) + 232);
            printf("\e[38;5;%dm%s\e[m", $color, "|");
        } else {
            printf(" ");
        }
        if ($coplus % 10 == 0) {
            $key = $min + $i * $interval;
            $console .= "↑" . $key;
            $coplus += mb_strlen($key) + 1;
        } elseif ($co == $coplus) {
            $console .= " ";
            $coplus++;
        }
        $co++;
        if ($co % $maxwidth === 0) {
            printf("\n%s\n", $console);
            $console = "";
        }
    }
    printf("\n%s\n", $console);
}

ターミナルで色を出力する

今回のメインとなるところ。グラフで表示するには最低でも二次元平面が必要になるので、一次元で表示するには色しかない。
調べてみると、ターミナルでも色の出力は可能な様子。

参考サイト:
ターミナルのechoやprintfに256色で色をつける 完全版 - vorfee's Tech Blog
bash:tip_colors_and_formatting - FLOZz' MISC

つまり、出力時に\e[38;5;255m文字列なんとか\e[mを含め、255を指定の色コードに変えれば色を変更することが可能。
ただ残念なことにRGBでの出力は出来ず、256色しかないので、0~255を1ずつ変えて〜は出来ない。
後者の参考サイトに色の一覧が載っているので、有難く拝見させてもらうと、どうやら232-256の間では白黒でグラデーションができるらしい。白黒で十分なので、この領域を使用することにする。
(なお、コード内の指定値は232~255になっているのだが、これは256を選択すると色が変になったので、念のために除外した)

ちなみにphpの場合はechoでもprintfでも上記の出力を行うと色が変化する。今回はターミナルでの出力なので、それらしいprintfを採用した。

配列を操作する

正直これが出来たらあとは作業になる。配列をごちゃごちゃしてprintfで出力するだけなのだから。

詳しい内容はコードを読んでもらうとして、配列操作に使った関数をいくつかメモ。

ksort

PHP: ksort - Manual
配列のキーを昇順に並び替える。
元々作成していた配列が、値0のキーを作成しておき、それに1ずつ足していく、という方法ではなく、その値が出来たらキーを作成して1ずつ足す、という方法であったため、キーが数値順に並んでいなかった。そのため、ここで一度並び替えを行っている。

$array = ["25" => 13, "19" => 43, "8" => 24];
ksort($array);
var_dump($array); // array(3) {[8]=>int(24) [19]=>int(43) [25]=>int(13)}

array_key_first / array_key_last

PHP: array_key_first - Manual
PHP: array_key_last - Manual

配列の先頭のキーもしくは最後のキーを取得する。
ちなみにこの関数はphp7.3で追加されたので、php7.2以前は使えない1
今回は、一つ前でキーソートを行っているので、実質キーの最小値と最大値を取得している。


とまぁとりあえず前処理を行い、あとは一つ目のforループで配列の再生成(間隔圧縮のため)と値の最大値を取得し、二つ目のforループで出力を行っていく。

使ってみる

今回は正規乱数を作成したかったので、以下のサイトを参考に、分布を表示してみる
平均値から正規分布乱数を生成する方法(PHP) | colori

$arr = [];
for ($i = 0; $i < 100000; $i++) {
    $a = (int)round(normal(500, 170)); // 参考サイトにあるnormalと同じ
    $arr[$a] ??= 0;
    $arr[$a]++;
}
distribution_show($arr, 5, 150);

normal関数で乱数を取得し、整数値化。その値のキーをインクリメントすることで、カウントしていく。
そのあと、先ほど作成した関数を呼び出し、第一引数に配列、第二引数には一つの棒がどの範囲を示すか、第三引数は改行する文字数。

スクリーンショット 2020-11-10 13.18.51.png

今回は基準を500にしているので、500を中心に色が濃い事が分かる。

一応第二引数の詳しい説明をしておくと、今回の画像だと左端が-168を指しているので、この棒一つで-168~-164の5数値がまとめているという事。1ずつだとかなり横長になったので追加した。

まとめ

せっかく作ったけど、実際にこの関数が役に立つことはほとんどないので、誰か使ってあげてください。


  1. 一応7.2以前でも、独自で実現する方法はあるようなので、7.2以下で使う場合は置き換えれば使用できる。 

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