Linux/sarをグラフ化その5(JavaScriptでグラフ化
※ここにCSVを入力・編集、またはファイルをドロップして、reloadボタンを押す。

(-_-)

\ \ 1
2
0x

※ここに数値が表示

Y軸初期値
線の色
一行目をX軸(時間軸)にする X軸の日跨ぎを補完する(23:59->00:01) X軸の間隔を均等にする



そして以下、解説です。


Linux/sarをグラフ化その5(JavaScriptでSVGグラフ化)(2023-07-09)


とにかく、今すぐ、CSVファイルをグラフ化して、
データの全体像の雰囲気だけでもつかみたいという時にご利用ください。

もう、gnuplotもExcelも何もない、あるいはグラフ化している時間が無い。
CSVファイルだけでどうすりゃ良いんだよぉ・・・という時に、
役に立ったり立たなかったりするかもしれません。


いきなりこのページに来た方は、まずCSVファイルをご用意ください。
sarコマンドからCSVファイルを取得する例はこちら


以下は、そのJavascriptコードの中身に用事がある人に向けてメモします。
(スマホでご覧の方はすみません、今回はPC向けのメモです)


上に表示されたグラフ化コードの詳細は、
このページをWebブラウザで右クリックしてソースを直接見て下さい。

<!--- ## CSVをjavascriptでグラフ化その1(ここから) --->
 (~~中略~~)
<!--- ## CSVをjavascriptでグラフ化その1(ここまで) --->

というコメントではさまれた場所2つ(その1、その2)があります。

それを適当なhtmlファイルにコピペすれば、再現できます。
<html><head> (その1) </head><body bgcolor="#eeeeff"> (その2)</body></html> みたいな
「てきとうな.html」ファイルを作ってみて下さい。

1ファイルで完結しています。
他にライブラリやらjsファイルやらは読み込んでいないし、サーバと通信も発生しません。
Webブラウザだけで、いま読んだCSVをその場でグラフ化します。

400行くらいのコードになってしまいました。
ポイントだけ上から順に解説していきます。


<style type="text/css">
  table#gr_table {
    padding:0 0 0 0;
    border:0;
    margin:0 0 0 0;
    background-color:#f1f1f1;
    font-size: 12px;
  }
  (以下略)

スタイルシートでグラフ化する場所の書式を決めます。
一番の目的は padding やら border やらを全部ゼロに設定して隙間を無くすことです。
色(color)や文字サイズ指定は適当です。

var data="2,4,8,16,32,64\n10,20,30,40,50,60";

グラフに最初に入れるダミーのデータです。CSVデータ二行分。
この一行目を時刻(秒)扱いした場合にどうグラフに反映されるか実験してみて下さい。

var RowMax = 0;
var ColMax = 0;
var ValMax = Number.MIN_SAFE_INTEGER;
(以下略)

各種変数。多すぎる。
ちなみに Max を入れる変数は最小値(MIN_SAFE_INTEGER)で、
Minを入れる変数は最大値(MAX_SAFE_INTEGER)で初期化しています。


function reloadData() {

CSVデータを読み込む関数です。

 HasXline = document.getElementById("ch_xline").checked ? 1 : 0;
 HasX_24h = document.getElementById("ch_xrev").checked ? 1 : 0;
 HasX_even= document.getElementById("ch_xeven").checked ? 1 : 0;

グラフ下にあるチェックボックス三つのOn/Offを読み取ってます。

 var e_min = document.getElementById("y_min");
 var v_min = e_min.value.replaceAll(/[^0-9\.\-]/g, "") 

replaceAll で正規表現を使って「数字以外の文字列」を全て「」で置き換え(つまり削除)しています。

 if(parseFloat(v_min) > parseFloat(v_max)){ var w = v_min; v_min = v_max; v_max = w; }

データを読み込みつつ、いまのデータ内での最大値と最小値を取得するのですが、
大小が逆転している場合にはこうやって、正しい順番に補正します。

最大~最小値からグラフの幅が決まって、
グラフの幅によって、グラフのどの部分に線や点を描くかが決まります。


 var d_rows = data.split("\n");

CSVデータを改行(\n)で分けて配列化。
続く for 文で一行ずつ処理していきます。

 for(var r=0; HasErr.length== 0 && r<d_rows.length && r<line_colors.length; r++){
 (中略)
   for(var c=0; HasErr.length==0 && c<d_cols.length; c++){
   (以下略)

各行、各カンマ区切りを読み込みます。
この時点ではまだグラフは描いておらず、入力されたCSVの書式や値の整合性チェックだけです。

       // Note: include ":"
       if(HasErr.length == 0){
         dbg_text += (c<=0?"":",") + s;
         var f;
         if(tt.length > 1){
           f = 0;
           var x60=1;
           for(var t=tt.length-1; t>=0; t--){
             f += parseFloat(tt[t]) * x60;
             x60 *= 60;
           }

CSVデータの一行目の文字列に ":" が含まれている場合には、
時刻とみなして、時を分に、分を秒に変換するために数値を 60 倍しています。

続く処理でも時刻計算をやっていて、
そもそもこのCSVが総時間で何秒分のデータなのかを取得しようとしています。

 if(HasErr.length > 0){
   document.getElementById("dbg_text").innerHTML =  dbg_text + HasErr;
   return HasErr;
 }

CSVデータ解析中にエラーがあった場合は、エラーメッセージを返して終了です。
エラーが無い場合、この後の処理で・・・

   array_gr[i] = new Array(n);
   array_dt[i] = new Array(n);
   array_dp[i] = new Array(n);

グラフに必要な各点を取得する処理へと続いて、
reloadData 関数は終了します。



function makeGraph(err) {
  var real_h = 200.0;
  var real_w = 220.0;

グラフを描画します。
ちなみにグラフの大きさ 200、220ピクセルは適当な数字なので変えて下さい。
続くgr_hと gr_w は・・・内部処理はパーセントで計算して、グラフ描画時はそれをピクセルに戻す、という処理になります。

 var sv_base = "<rect fill='white' x=0 y=0 width=" + gr_w + " height=" + gr_h+ " />" +
 "<path fill='none' stroke='#aaaabf' stroke-width=4 d='M " +
   gr_left +","+ gr_top +" "+
   gr_left +","+ gr_btm +" "+
   gr_right +","+ gr_btm +" "+
   "' />";

HTMLの rect タグ、path タグをガリガリと書いています。
このあたりからグラフ本体になる部分です。

 sv_base +="<path fill='none' stroke='#ddddee' stroke-width=1 d='";
 if(HasXline > 0 && HasX_even == 0){
   var last_x = Number.MIN_SAFE_INTEGER;
   for(var c=0; c<ColMax; c++){
   (以下略)

pathタグの "d=" 部分を書き連ねます。
CSVの一行目を時刻とみなした場合に、グラフ上の各時刻点に縦の線を引いています。

     var f = array_dt[0][c] + h24_over;

時刻が 23:59 から 00:00 みたいな日跨ぎだった場合に、
補完するための h24_over (=24 * 3600 秒で一日分)を、なんかやってます。

 for(var r=array_gr.length-1; r>=(HasXline>0 ? 1:0); r--){
   if(line_index>=0 && line_index!=r){ continue; }
   var co = "#111111";
   if(line_colors[r] != null && line_colors[r].length > 0){ co = line_colors[r]; }
   lines += "<path fill='none' stroke='" + co + "' stroke-width=1 d='M ";
   for(var c=0; c<array_gr[r].length; c++){

CSVデータ最後の行から最初の行へむけて順番にグラフ化、線を重ねています。
つまり、線が重なった時は最初の方のデータが優先されて表示されます。

 e.innerHTML =  sv_base + lines;

生成した rectタグと path タグをHTMLファイルに反映して、終了です。



document.addEventListener("DOMContentLoaded",function(){

定番の処理です。
HTMLファイルの読み込み完了時にこの処理が呼ばれます。

 var es = document.getElementById("svg_gr");
 es.addEventListener('click', e => {

グラフをクリックしたら、線が表示・非表示されるのを繰り返します。
・・・そうなるように、makeGraphしなおしてます。

 es.addEventListener('mouseover', e => {
   var r = es.getBoundingClientRect();
   var x = Math.round(e.clientX - r.left);

グラフの上をマウスカーソルが動いた時、
そのグラフ上での座標を取得して、
グラフ上の線と重なったか判定して、
線に対応するCSVデータの数値を表示する、ようにしています。

 var e_drop = document.getElementById("txa_in");
 e_drop.addEventListener('dragover', e => {
   e.preventDefault();

デフォルトの動作を無効にしつつ、
CSV入力用の領域にドラッグ&ドロップでファイルを読み込ませるための
dragover から drop までの設定を行っています。


以上で DOMContentLoaded が終わり、そして最後の関数です。



function readDropFile(ev) {

ドラッグ&ドロップ時に呼ばれて、ファイルからCSVを読み取ります。
無事に読み取れた場合は・・・

 reader.onload = function (evt) {
   data = reader.result;
   document.getElementById("txa_in").value = data;
   rePaint();
 }

ここで上のCSVデータをテキストエリア(txa_in)に反映しつつ、
rePaint でCSVデータを再計算しています。




HTML側の解説は省略します!
参考になれば幸いです。