Node-Redを利用して基本的なデータ分析をやってみました（続）箱ひげ図


# はじめに
前回、「Node-Redを利用して基本的なデータ分析をやってみました」において、弊社デモ環境から出力したCSVファイル（オフィスのCO2濃度と気温を測定）を読み込み、折れ線グラフ、ヒストグラム、散布図などを作成してみました。
今回はその続編で「箱ひげ図」の表示に挑戦してみました。

　※２０２２年１月頃の寒い時期のデータを使用しています。

# 基本的なデータ分析　今回の表示目標

　　CO2濃度の箱ひげ図　
　　　1日の時間帯を以下の４つに分けての表示が今回の表示目標となります。
　　　早朝　00-05　00:00-05:59
　　　午前　06-11　06:00-11:59
　　　午後　12-17　12:00-17:59
　　　夜間　18-23　18:00-23:59

　vegaノードを使用して、縦軸に時間帯、横軸にCO2濃度をとり、箱ひげ図を表示します
　完成形は以下となりました
![基本的なデータ分析　箱ひげ図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742464/a4deedf1-e483-1709-a776-6612cb651a17.png)



　＜少し考えてみました＞
　　〇時間帯別に見ると以下がわかりました。
　　　早朝　00-05　00:00-5:59
　　　　　　中央値は603、第一四分位が599、第三四分位が606
　　　　　　　中央値は他の時間帯と比較して高め
　　　　　　　ばらつきがかなり小さい
　　　午前　06-11　06:00-11:59
　　　　　　中央値は552、第一四分位が506、第三四分位が580
　　　　　　　中央値は午後、夜間と同程度
　　　　　　　ばらつきが大きい
　　　午後　12-17　12:00-17:59
　　　　　　中央値は545、第一四分位が532、第三四分位が569
　　　　　　　中央値は午前、夜間と同程度
　　　　　　　ばらつきが小さい
　　　夜間　18-23　18:00-23:59
　　　　　　中央値は559、第一四分位が535、第三四分位が609
　　　　　　　中央値は午前、午後と同程度
　　　　　　　ばらつきが大きい

　　＜ご参考＞前回の「Co2濃度の折れ線グラフ」
　　　早朝は600ppm前後で一定値、朝９時前後で450ppmくらいまで下降、
　　　お昼にかけて550ppmくらいまで上昇、午後は少し下降し、夕方にかけて上昇。
　　　夕方から夜間は650ppmくらいまで上昇　　といった様子が読み取れます。
![基本的なデータ分析　CO2グラフ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742464/989d89db-3b7a-44a0-f364-1d6984caff97.png)

# Node-Redで構築　フローの全体図
　当方環境はNode-Red　ver2.2.2をWindows上で動かしております。
　フローの全体は以下となります。ノードは７つでシンプルですが、Functionノード２つにJavaScriptのロジックを入れております。

![基本的なデータ分析　箱ひげ図 フロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742464/44927e0f-9c42-8e94-6468-18c2005f986f.png)



# Node-Redで構築　CSVファイル読込

前回　「Node-Redを利用して基本的なデータ分析をやってみました」と同じ内容です。
https://qiita.com/tota_nissho/items/f6d6d94eb23cd4d9240d

　読み込むCSVファイルはヘッダがなく、以下のような形式です。
　１列目がタイムスタンプ、２列目がCO2濃度（整数）、３列目が温度となっております。
　＝＝＝
　2022/01/04 00:01:19,527,21.1625
　・・・
　2022/01/04 23:59:19,605,22.2250
　＝＝＝

　まず、injectノード（file）ではfilenameをセットします
　次に、read fileノード　出力は文字列、文字コードはデフォルトです
　最後にCSVノード　区切り文字はコンマ、数値を変換するにチェックを入れます

　これでCSVデータをNode-Red上に持ってくる設定ができました。　


# Node-Redで構築　箱ひげ図　ファイルを時間帯別に分割
　
　まず、Functionノード(文字列変換CO2変形)で、データを分割します。
　「１列目がタイムスタンプ、２列目がCO2濃度、３列目が温度」のデータから、
　以下の４つのファイルを作成し、CO2濃度順にＳＯＲＴします。
　　・早朝　00-05　00:00-05:59　のデータ
　　・午前　06-11　06:00-11:59　のデータ
　　・午後　12-17　12:00-17:59　のデータ
　　・夜間　18-23　18:00-23:59　のデータ
　
　ChangeロールでjSONataなどで設定したかったのですが、方法がわからず、
　JavaScriptで実装することといたしました。もう少しシンプルにしたかったのですが、少々冗長なロジックとなり、恐縮です。

　
　ご参考：Functionノード(文字列変換CO2変形)のロジックとなります

以下は変数設定
```
var file_old = msg.payload;
var v = []; var w = [];var x = [];var y = [];var z = [];
var values01 = [];var values02 = [];var values03 = [];var values04 = [];
var ix=0;
```

次に、読み込んだCSVファイルを４つの時間帯に分割します。
```
for (ix = 0; ix < file_old.length; ix ++) {
    if ('00' <= file_old[ix].col1.substr( 11, 2 )  && file_old[ix].col1.substr( 11, 2 ) <= '05') {
      v[ix] = '00-05'; x[ix] = Number(file_old[ix].col2); values01.push({  Time: v[ix], Co2: x[ix]  }) 
    }  else if ('06' <= file_old[ix].col1.substr( 11, 2 )  && file_old[ix].col1.substr( 11, 2 ) <= '11') {
      v[ix] = '06-11'; x[ix] = Number(file_old[ix].col2); values02.push({  Time: v[ix], Co2: x[ix]  }) 
    }  else if ('12' <= file_old[ix].col1.substr( 11, 2 )  && file_old[ix].col1.substr( 11, 2 ) <= '17') {
      v[ix] = '12-17'; x[ix] = Number(file_old[ix].col2); values03.push({  Time: v[ix], Co2: x[ix]  }) 
    } else if ('18' <= file_old[ix].col1.substr( 11, 2 )  && file_old[ix].col1.substr( 11, 2 ) <= '23') {
      v[ix] = '18-23'; x[ix] = Number(file_old[ix].col2); values04.push({  Time: v[ix], Co2: x[ix]  }) 
    }     
    }     

```

最後に分割した４ファイルをCO2の濃度順にSORTします
```
msg.dataco2_01 = values01;
msg.dataco2_01.sort(function (e, f) {
  if(e.Time < f.Time) { return -1 } 
    else if(e.Time > f.Time) { return 1;  }
  if(e.Co2 < f.Co2) { return -1 } 
    else if(e.Co2 > f.Co2) { return 1;  }// Co2 を比較
      return 0;});  // いずれにも当てはまらなければ 0 を返す

//以下　msg.dataco2_02からmsg.dataco2_04も同様に処理

return msg;

```

　　


# Node-Redで構築　箱ひげ図　Vegaの設定

templateノードでVegaの設定をします。
```
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "title": "CO2 時間帯別箱ひげ図",
  "width": 800,
  "height": 300,
  "data": {
    "values": []
  },
  "encoding": {"y": {"field": "Species", "type": "nominal", "title": null}},
  "layer": [
    {
      "mark": {"type": "rule"},
      "encoding": {
        "x": {"field": "lower", "type": "quantitative","scale": {"zero": false}, "title": null},
        "x2": {"field": "upper"}
      }
    },
    {
      "mark": {"type": "bar", "size": 30},
      "encoding": {
        "x": {"field": "q1", "type": "quantitative"},
        "x2": {"field": "q3"},
        "color": {"field": "Species", "type": "nominal", "legend": null}
      }
    },
    {
      "mark": {"type": "tick", "color": "white", "size": 30},
      "encoding": {
        "x": {"field": "median", "type": "quantitative"}
      }
    },
    {
      "transform": [{"flatten": ["outliers"]}],
      "mark": {"type": "point", "style": "boxplot-outliers"},
      "encoding": {
        "x": {"field": "outliers", "type": "quantitative"}
      }
    }
  ]
}

```
上記の設定手順は以下となります。
・以下をCopyしtemplateノードに貼り付けします。
　　https://vega.github.io/vega-lite/examples/boxplot_preaggregated.html
・以下５か所を変更
　　・　titleの変更
　　・  "width": 800,  "height": 300,　を追加
　　・  "values": []　への変更　　中身のデータを消去
　　・　"mark": {"type": "bar", "size": 14}　を　
　　　　"mark": {"type": "bar", "size": 30}　に変更 （２か所）



　
# Node-Redで構築　箱ひげ図　数値編集とVegaノード

１．Functionノード(数値変換)は以下となります。

・以下のValue値の部分と同様になるように数値を編集します。
　　https://vega.github.io/vega-lite/examples/boxplot_preaggregated.html
```
    "values": [{
      "Species": "Adelie",
      "lower": 2850,
      "q1": 3350,
      "median": 3700,
      "q3": 4000,
      "upper": 4775,
      "outliers": []
    }
```

以下は変数設定
```
//変数設定
var file_old_01 = msg.dataco2_01;
var x = [];var y = [];var z = [];var values = [];
var ix1=0;var ix2=0;var ix3=0;var ix0=0;
```

次に、時間帯で４つに分割したファイルのそれぞれについて、種別（時間帯）と最小値(lower)、第一四分位（q1）、中央値(median)、第三四分位（q3）、最大値(upper)を抽出し、セットします。
```
for (ix0 = 0; ix0 < 4; ix0 ++) {
//種別（時間帯）と最小値(lower)
    if(ix0==0){file_old_01 = msg.dataco2_01; y[0] = '00-05';}
    else if(ix0==1){file_old_01 = msg.dataco2_02; y[0] = '06-11';}
    else if(ix0==2){file_old_01 = msg.dataco2_03; y[0] = '12-17';}
    else if(ix0==3){file_old_01 = msg.dataco2_04; y[0] = '18-23';}
     y[1] = Number(file_old_01[0].Co2) ; 
// 第一四分位（q1）    
     ix1 = Number(file_old_01.length-1) / 4;
     if (ix1 == Math.floor(ix1)){     
        y[2] = Number(file_old_01[ix1].Co2) ;
     }else{
         ix1 = Math.floor(ix1)
         y[2] = Number(file_old_01[ix1].Co2) + (Number(file_old_01[ix1+1].Co2) - Number(file_old_01[ix1].Co2)) / 2
     }     
//中央値(median)
     ix2 = Number(file_old_01.length-1) / 2;
     if (ix2 == Math.floor(ix2)){     
        y[3] = Number(file_old_01[ix2].Co2) 
     }else{
          ix2 = Math.floor(ix2)
          y[3] = Number(file_old_01[ix2].Co2) + (Number(file_old_01[ix2+1].Co2) - Number(file_old_01[ix2].Co2))/ 2
      }
//第三四分位（q3）
     ix3 = Number(file_old_01.length-1) * 3 / 4;
     if (ix3 == Math.floor(ix3)){     
        y[4] = Number(file_old_01[ix3].Co2) ; 
     }else{
         ix3 = Math.floor(ix3)
        y[4] = Number(file_old_01[ix3].Co2) + (Number(file_old_01[ix3+1].Co2) - Number(file_old_01[ix3].Co2)) / 2
     }
//最大値(upper)
     y[5] = Number(file_old_01[file_old_01.length-1].Co2) ;

    values.push ({ "Species": y[0], "lower": y[1], "q1": y[2], "median": y[3], "q3": y[4], "upper": y[5], "outliers": []})
}
// 出力設定:
msg.payload.data.values = values;
return msg;
```

２．最後にVegaノードをセットし、Group,Size,Nameをセットします




# おわりに、続編に向けて

　　　今回は「箱ひげ図」にチャレンジしてみました。
　　　Vegaノードでは各種可視化がまだまだ可能なようですので、引き続き試していきたいと考えています。
　　　


# 参考資料

- [Node-REDダッシュボードを用いたデータ可視化](https://qiita.com/HiroyasuNishiyama/items/2433d275c667bc75ed8c)
- [Vega](https://vega.github.io/vega/)
- [Vega-lite](https://vega.github.io/vega-lite/)
- [JSONata](https://docs.jsonata.org/overview.html)
- [node-red-node-ui-vegaを使った各種センサーのグラフ表示](https://qiita.com/kz1017/items/a9690f1e7cb06b0e3f24)
- [二酸化炭素濃度モニタリングリモートデモシステムの構築](https://qiita.com/hrsakuma/items/b29de6c1b482ee15f03b)
- [Node-Redを利用して基本的なデータ分析をやってみました](https://qiita.com/tota_nissho/items/f6d6d94eb23cd4d9240d)