1
2

More than 1 year has passed since last update.

【C++】matplotlib-cppでtrend chartを書く方法

Last updated at Posted at 2021-07-18

C++ matplotlib-cppでtrend chartを書く方法

 データの可視化は情報を整理したり、わかりやすく結果を示すときによく使います。グラフの種類は、トレンドチャートやバーチャート、モザイクチャートやヒートマップ、データテーブルなど様々あり、適切な表現で示すことで伝えたいことを100%伝えることができます。今回はC++のmatplotlib-cppを使ったGraphのつくり方をまとめました。

matplotlib-cppとは

ドイツ ハンブルクのソフトウェア企業TenzirのBenno Eversが2014年にGitに公開したC++ Plotライブラリ。Python Matlabとmatplotlibを参考に作られており、使い方はmatplotlibに似ている。

Tenzir Home Page
matplotlib-cpp Github
matplotlib-cpp documentation

環境設定

  • OS : Windows10(2021-)
  • Editor : VS code(version 1.58.0)
  • C++ compiler : MinGW w64bit(gcc version 8.1.0)
  • Python : Python39(v3.9.6)
  • make : v3.81

初期設定

pipでpython moduleをinstallする。
各環境のProxyサーバーを指定する。

pip --proxy=http://**********.com:80 install --trusted-host file.pythonhosted.org numpy
pip --proxy=http://**********.com:80 install --trusted-host file.pythonhosted.org matplotlib

MinGW w64bitをinstallする。

choco install mingw

もしくは
mingw download page

詳細は以下の記事参考。
https://qiita.com/ryo-sato/items/00c17469978e47d91a09

buildするのにmakeを使用するため、以下のサイトからmakeのinstallerをダウンロードする。
"Complete package, except sources"の横の"Setup"を押す。

まずはデータをloadする

getlineで行毎に読み込み、for文で以下のように列毎に分け、データをpair vectorに代入する。
今回のデータセットの1列目に日付情報、2列目以降に数字・数値問わない縦軸となるデータがあることを前提にデータを読み込んでいます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <cmath>
#include <ctime>
#include <math.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <time.h>
#include <numeric>
#include <unistd.h>
#include <cctype>
#include <direct.h>
#include "Python.h"
#include "datetime.h"
#include "matplotlibcpp.h"

namespace plt = matplotlibcpp;

while (getline(ifs_csv_file, str_buf)) {
      std::istringstream i_stream(str_buf);
      std::string i_stream_tmp =  i_stream.str();
      long long row_n = std::count(i_stream_tmp.cbegin(), i_stream_tmp.cend(), ',') ; 
      while (getline(i_stream, str_line_buf, '\n')) {
          for (long long i = 0; i <= row_n; i++) {
              tokun = str_line_buf.substr(0, str_line_buf.find(delimiter));
              vvec.push_back(tokun);
              str_line_buf.erase(0, str_line_buf.find(delimiter) + delimiter.length());   
          }
          //After second Row reading
          if (nn != 0 & !(vvec[1].empty())) { 
              time_t t = string_to_time_t(vvec[1]);
              char longdate[30];
              strftime(longdate, 20, "%Y%m%d%H%M%S", localtime(&t));
              tvec.push_back(t);
              for (long long k = 2; k <= n; k++) {
                  if (!(vvec[k].empty()) && isdigit(vvec[k][0])){
                      double ytmp = stod(vvec[k]);
                      TY[k-1].push_back(std::make_pair(t,ytmp));
                      ty.push_back(std::make_pair(t,ytmp));
                  }
              }
           //Initial Row reading
          }else if (nn == 0){ 
              for (long long k = 0; k <= n; k++) {
                  label_vec.push_back(vvec[k]); 
              }
          }
          nn =+ 1;
          vvec.clear();     
      }
}

時間の軸ラベルを生成する

matplotlibの横軸のラベルベクトルを作成する。
今回は横軸を日付にしたいので、実際の横軸の秒値を一日毎に数えて対応する日付を文字で入れている。

    //横軸時間のベクトルの重複を消し、日付若い順にソートする。
    sort(tvec.begin(), tvec.end());

    //x-axis Min Value
    time_t t_min1 = tvec[0]; 
    char ltmp_min[20];
    strftime(ltmp_min, 20, "%Y/%m/%d 00:00", localtime(&t_min1));
    std::string Ltmp_min = ltmp_min;
    time_t Lttmp_min = string_to_time_t(Ltmp_min);
    time_t t_min = Lttmp_min;

    //x-axis Max Value
    size_t tlen;
    tlen = tvec.size();
    time_t t_max = tvec[tlen-1];

    vector<pair<string,long long>> Ltx = time_t_to_date(t_min,t_max);
    
    //横軸のラベルに使う値と日付文字ベクトルを作成。
    for (auto& i: Ltx) {
      Label_strdate_row.push_back(i.first);
      Label_seconddate_row.push_back(i.second);
    }

最後にmatplotlib形式でGraphを書く。

    std::vector<double> ycounts(Lx_row.size()), y_sum(Lx_row.size()), y_ave(Lx_row.size());
    for (long long j = 0; j < lvec.size(); j++) {
        ycounts.resize(Lx_row.size());
        y_sum.resize(Lx_row.size());
        y_ave.resize(Lx_row.size());       
        sort(TY[j].begin(), TY[j].end());
 
        for (auto& i: TY[j]) {
            time_hour = (long long)i.first;
            t.push_back(i.first);
            y.push_back(i.second);
            y_sum[(i.first-Lt_row[0])/86414] = y_sum[(i.first-Lt_row[0])/86414] + i.second;
            ycounts[(i.first-Lt_row[0])/86414] = ycounts[(i.first-Lt_row[0])/86414] + 1;           
        }
        for (long long k = 0; k <  Lx_row.size(); k++) {
            y_ave[k] = y_sum[k]/ycounts[k];
        }

        //matplotlib pltでグラフ作成
        plt::figure_size(600, 300);
        plt::plot(Lt, y_ave, "r-o"); 
        plt::grid("true");
        plt::ylabel("");
        plt::xlabel("");
        std::map<string,string> m;
        m["rotation"] = "vertical";
        m["fontsize"] = "small";
        m["fontstyle"] = "normal";
        m["fontstretch"] = "normal";
        plt::xticks(Lt_row,Lx_row,m);
        plt::axis("tight");//on,off,equal,scaled,tight,auto,image,square
        plt::title(lvec[k]);
        _mkdir("./image");
        std::string out_image_name = "./image/image_" + to_string(k);
        plt::tight_layout();
        plt::save(out_image_name);
        plt::close();
        t.clear();
        y.clear();
        y_sum.clear();
        y_ave.clear();
        ycounts.clear();
        k = k + 1;
 
    }

makeでコンパイル

main.cppと同じフォルダにMakefileを以下のように作成し、makeする。

CC            = g++
CFLAGS        = -IC:/matplotlib-cpp \
	-IC:/Python39/include \
	-IC:/Python39/Lib/site-packages/numpy/core/include \
	-IC:/Python39/Lib/site-packages/numpy/core/include/numpy
LDFLAGS       = -LC:/Python39/libs
LIBS          = -lpython39
OBJS          = main.cpp
PROGRAM       = output

all: $(PROGRAM)

$(PROGRAM): $(OBJS)
	$(CC) -o $(PROGRAM) $(OBJS) $(CFLAGS) $(LDFLAGS) $(LIBS)

するとoutput.exeができる。
できたプログラムでトレンドチャートを作るとこんなのが書ける。
乱数で作った以下のテストデータでトレンドチャートを書いてみよう。

datetime Para1 Para2
2021/5/1 0:00 0.01 0.85
2021/5/1 1:00 0.54 0.70
2021/5/1 2:00 0.09 0.00
2021/5/1 3:00 0.74 0.06
2021/5/1 4:00 0.50 0.03
2021/5/1 5:00 0.95 0.49
2021/5/1 6:00 0.62 0.45
2021/5/1 7:00 0.55 0.98
2021/5/1 8:00 0.42 0.34
2021/5/1 8:59 0.12 0.97

出力はこんな感じ。よく見るmatplotlibと同じformatのグラフができる。
image_0.png

まとめ

いかがでしたか。今回はC プロットライブラリ matplotlib-cppでのグラフ作成を紹介しました。
もともとpython matplotlibを使っていた人ならそれなりに理解しやすいのではないかと思います。
実際グラフを書くときは細かい調整をしたりするので、Pythonで作ったほうがいいですね。。。
大量にグラフを作る必要があるときは、C言語なので処理が速く、まぁ一応メリットはあるかなという感じです。
以上、ありがとうございました。

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