LoginSignup
0
1

More than 1 year has passed since last update.

Yellowfinで睡眠情報の帯グラフを作成する

Posted at

image.png

はじめに

今回も要望がありd3.jsのライブラリや記事を探していたら、近いことをされている方がいたのでその方の記事のX軸とY軸を切り替えたものを今回記事にしました。
睡眠時間のデータを時間ごとの帯グラフの上に描画するイメージです。

ソースはこちら

詳しくはその方の記事の解説を見てもらえるとわかりますが、このグラフは直近7日分の日付を動的に取得しているので場合によって静的にするか-1日だけの表示にするか変更が必要です。また、データの分割は同じ方のこの記事が参考になります。
オリジナル版との違いはXY軸の変換とデータの配列の3つめに種類を与えることで色分けを可能にしているところです。
Yellowfinのレポートの値を使うとしたらこの記事が参考になると思います。このソースの場合はdoDrawingの中のdata変数にデータが入っています。

time.js
//generateChart は必須の関数であり、Javascriptグラフの作成において呼び出しされます
generateChart = function (options) {

    // これはお使いのブラウザのデバッガのブレークポイントのトリガとなるため、Javascriptをデバッグすることができます
    debugger;

    // グラフを描画する部分です
    var $chartDrawDiv = $(options.divSelector);

    // Javascriptに関係なく任意のポートレットでスクロールバーを停止するには、このCSSクラスを使用します
    $chartDrawDiv.addClass('jsChartNoOverflow');

    // これにより、datasetから高さと幅が取得されます。グラフの作成時にこれらを使用して、
    // ダッシュボード、カンバス、ストーリーボードに正しく合わせます
    //var height = options.dataset.chart_information.height;
    //var width = options.dataset.chart_information.width;
    var height = 400;
    var width = 800;

    // 必要に応じて、RAWデータJSONをグラフに必要な形式に変換します。
    var processedData = processData(options.dataset.data);

    // グラフの実績の描画を $chartDiv に行います。
    doDrawing(processedData, $chartDrawDiv, height, width, options.errorCallback);
},

    processData = function (dataset) {

        // データが格納されるdatasetはカラム(列)ベースの形式であり、各カラム(列)にはデータの配列が含まれます
        // eg. dataset.column_name[0].raw_data, dataset.column_name[0].formatted_data

    },

    doDrawing = function (data, $chartDiv, height, width, errorFunction) {

        // require を使用して必要なJavascriptライブラリを読み込みます

        // 付属するライブラリとその場所:
        // js/chartingLibraries/c3/c3
        // js/chartingLibraries/chartjs/Chart
        // js/chartingLibraries/d3_3.5.17/d3_3.5.17
        require(['js/chartingLibraries/d3/d3'], function (d3) {

            try {
                let divided = [
                    [
                        new Date("2022-01-10 03:00:00"),
                        new Date("2022-01-10 05:00:00"),
                        10
                    ],
                    [
                        new Date("2022-01-10 05:30:00"),
                        new Date("2022-01-10 07:00:00"),
                        11
                    ],
                    [
                        new Date("2022-01-11 01:00:00"),
                        new Date("2022-01-11 05:00:00"),
                        12
                    ]
                ];
                var $canvas = $('<svg id="svg"></svg>');
                $chartDiv.append($canvas);
                var margin = {
                    left: 30,
                    right: 30,
                    bottom: 50,
                    top: 30,
                };

                let svg = d3.select("#svg")
                    .attr("width", width)
                    .attr("height", height);


                // グラフ描画コードはこちら
                /* y 方向のスケールを作成する */
                let nDates = 7;    // 一週間分のデータを表示する
                let y = d3.scaleLinear()
                    .domain([0, nDates])
                    .range([margin.top, height - margin.bottom]);

                /* x 方向のスケールを作成する */
                let x = d3.scaleLinear()
                    .domain([0, 24])    // 00:00:00 ~ 24:00:00
                    .range([margin.left, width - margin.right]);

                /* 日付文字列のリストを取得する */
                let tod = new Date();
                let dateStrings = [];
                let tmpDate = null;
                for (let i = 0; i < nDates; i++) {
                    tmpDate = new Date();
                    tmpDate.setDate(tod.getDate() - i);
                    let ymd = tmpDate.toLocaleString().split(" ")[0];
                    let md = ymd.substring(5);    // 年を取り除く
                    dateStrings.push(md);
                }
                dateStrings.reverse();

                /* x 軸と目盛りを作成する */

                let xAxis = svg.append("g")
                    .attr("class", "x_axis")
                    .attr(
                        "transform",
                        "translate(" +
                        [
                            0,
                            height - margin.bottom
                        ].join(",") + ")"
                    )
                    .call(
                        d3.axisBottom(x)
                            .ticks(24)
                            .tickSize(-height + margin.top + margin.bottom)
                            .tickFormat(function (d) {
                                return d + ":00";
                            })

                    );

                /* y 軸と目盛りを作成する */

                let yAxis = svg.append("g")
                    .attr("class", "y_axis")
                    .attr(
                        "transform",
                        "translate(" +
                        [
                            margin.left,
                            0
                        ].join(",") + ")"
                    )
                    .call(
                        d3.axisLeft(y)
                            .ticks(nDates)
                            .tickSize(-width + margin.left + margin.right)
                            .tickFormat(function (d, i) {
                                return dateStrings[i];
                            })
                    );

                /* バーを描画する */

                let innerWidth = width - margin.left - margin.right;    // 内側の表示部分 (横幅)
                let innerHeight = height - margin.bottom - margin.top;    // 内側の表示部分 (高さ)
                let barWidth = innerWidth / 24;    // バーの横幅
                let barheight = innerHeight / nDates;
                let bars = svg.selectAll("rect")
                    .data(divided)
                    .enter()
                    .append("rect")
                    .attr("x", function (d, i) {
                        let start = d[0];
                        return x(toHour(start));

                    })
                    .attr("y", function (d, i) {
                        let start = d[0];
                        let startYmd = start.toLocaleString().split(" ")[0];
                        let startMd = startYmd.substring(5);    // 2018/02/15 → 02-15
                        return y(dateStrings.indexOf(startMd));
                    })
                    .attr("height", barheight)
                    .attr("width", function (d, i) {
                        let milliSecInDay = 86400000;    // Date - Date でミリ秒が返るため
                        let start = d[0];
                        let finish = d[1];
                        return innerWidth * (finish - start) / milliSecInDay;
                    })
                    .attr("fill", function (d, i) {
                        let colornum = d[2];
                        let color = "blue";
                        if (colornum == 10) {
                            color = "red";
                        } else if (colornum == 11) {
                            color = "green";
                        }
                        return color;

                    })
                    .style("opacity", 0.6);

                /* その日の始めから何秒経過したかを返す */
                function toSec(d) {
                    let elapsed = d.getHours() * 60 * 60 +
                        d.getMinutes() * 60 +
                        d.getSeconds();
                    return elapsed;
                }

                /* その日の始めから何時間経過したかを返す */
                function toHour(d) {
                    let sec = toSec(d);    // 経過秒
                    let secInDay = 86400;    // 24 * 60 * 60
                    return sec / secInDay * 24;
                }
                /* 目盛りの日付文字列の位置を調整する */

                yAxis.selectAll("g.tick")
                    .selectAll("text")
                    .attr(
                        "transform",
                        "translate(" +
                        [
                            0,
                            barheight / 2
                        ].join(",") + ")"
                    );

            } catch (err) {
                errorFunction(err);
            }
        });
    }

まとめ

先人様のソースを見ることでかなり理解を深められました。これで工場の稼働時間などの可視化なども可能になりそうですね。

参考にさせていただきました

D3.js -- 睡眠時間をグラフにプロットする
https://zdassen.hatenablog.com/entry/2018/02/21/012842
D3.js -- x軸、y軸の目盛りを描画する & グリッドを描画する
https://zdassen.hatenablog.com/entry/2018/02/15/131910
JavaScript -- 日付変更線をまたぐデータを翌日の00:00:00で分割する
https://zdassen.hatenablog.com/entry/2018/02/13/234150

0
1
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
1