【C++】C++からPythonのmatplotlibを使う

2024-04-16C++,matplotlib,python

はじめに

C++を使ってデータ処理をしていると、Pythonの豊富なライブラリを手軽に使える環境がうらやましいと思うことがあります。その中でも簡単にさまざまな種類のグラフを表示できるmatplotlibはひときわ便利な存在です。

実は、C++でもmatplotlib-cppというライブラリを使うことで、Pythonのmatplotlibを呼び出し、直接グラフを表示できます。

今回は、このmatplotlib-cppの使い方について調べました。そのメモです。

入手

matplotlib-cppのGitHubからmatplotlibcpp.hをダウンロードします。もしくはリポジトリをcloneします。

git clone https://github.com/lava/matplotlib-cpp.git

使い方

matplotlib-cppを使うには、事前にPythonとmatplotlib, NumPyのインストールが必要です。これらをあらかじめインストールしておきます。

matplotlib-cpp本体はヘッダファイルだけなので、C++のソースコード内でインクルードするだけで使うことができます。

Linuxの場合

ビルド時にPythonのインクルードファイルとライブラリへのパスを指定します。GCCなら以下のようになります。

% python3 --version
Python 3.11.4

% g++ ソースファイル -std=c++11 -I/usr/include/python3.11 -l/usr/lib/python3.11

Pythonのインクルードファイル、ライブラリへのパスは、インストールされているバージョンに合わせます。

Windowsの場合

Windowsの場合、いくつか躓くところがありますので注意が必要です。

ソースコードの修正

入手してきたmatplotlibcpp.hは、そのままでは使うことができません。コンパイルしようとすると、select_npy_type<long long>, select_npy_type<unsigned long long>が重複定義されているとのメッセージが出てくるはずです。

matplotlibcpp.hのコードを読むと、select_npy_type<long long>を定義している付近に、

// Sanity checks; comment them out or change the numpy type below if you're compiling on
// a platform where they don't apply

とあるので、以下のように定義部分をコメントアウトして置きます。

//template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
//template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };

これでエラーが出なくなります。

ビルド

Windowsでは以下のパスが必要です。あらかじめこれらのパスを調べておきます。

  1. Pythonのインクルードファイル
  2. NumPyのインクルードファイル
  3. Pythonのライブラリ

Windowsの場合、Pythonのデフォルトのインストール先はC:\Users\ユーザー名\AppData\Local\Programs\Pythonと長いので環境変数INCLUDELIBに登録しておくのが良いです。

また、WindowsではGCCでビルドできません。MSVC(cl.exe)かClangを使う必要があります。

  • MSVC(cl.exe)

    cl /EHsc ソースファイル /I \path\to\python\include /I \path\to\python\pkgs\numpy-base-なんちゃら\Lib\site-packages\numpy\core\include /link /LIBPATH \path\to\python\libs
  • Clang

    clang++ ソースファイル -I\path\to\python\include -I\path\to\python\pkgs\numpy-base-なんちゃら\Lib\site-packages\numpy\core\include -l\path\to\python\libs\python311.lib

CMake

CMakeを使う場合は、以下のようにCMakeLists.txtを書きます。PythonやNumPyのパスを自動的に検索してくれるので、CMakeの使える環境であれば、こちらの方がラクです。

cmake_minimum_required(VERSION 3.14)
project(プロジェクト名 CXX)

add_executable(実行ファイル名 ソースファイル)
target_compile_features(実行ファイル名 PRIVATE cxx_std_11)

find_package(Python COMPONENTS Development NumPy REQUIRED)
target_include_directories(実行ファイル名 PRIVATE ${Python_INCLUDE_DIRS} ${Python_NumPy_INCLUDE_DIRS})
target_link_libraries(実行ファイル名 Python::Python Python::NumPy)

find_package(Python COMPONENTS Development NumPy)を使うと、PythonとNumPyのパスを自動的に探し出してくれます。最後のREQUIREDは必須という意味です。Pythonのパスを探すためにはCMakeのバージョンが3.12以降、NumPyのパスを探すためにはCMakeのバージョン3.14以降が必要です。

find_package(Python)についての詳細はCMakeの公式ドキュメントを参照してください。

Makefileを作る場合、デフォルトではDEBUGビルドで生成しますので、(とくにWindowsの場合)ビルドタイプをReleaseにしておく必要があります。

cmake -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles" ..

使用例

Pythonのmatplotlibと同じような書き方で使うことができます。

#define _USE_MATH_DEFINES
#include <cmath>
#include <map>
#include <string>
#include <vector>

#include "matplotlibcpp.h"

namespace plt = matplotlibcpp;

int main()
{
    size_t n = 100;
    double sigma = 0.5, mean = 5.0, tx;
    std::vector<double> x(n), y(n), z(n);
    std::map<std::string, std::string> style;

    for(int i = 0; i < n; i++){
        x[i] = i / 10.0;
        tx = x[i] - mean;
        y[i] = 10 / std::sqrt(2 * M_PI * sigma * sigma) * std::exp(-tx * tx / (2 * sigma * sigma));
        z[i] = std::sin(2 * M_PI * x[i]) - 2;
    }

    // initializer_listによる設定
    plt::plot({1, -1.3, 0.1, 0.5, -0.5, 0.8, -0.3, 1, 0, -1, 0.6});

    // ラムダ式を使った設定
    plt::plot(x, [](double t){
        return std::log(t) + 4;
    }, "b-");

    // named_plotによる凡例の設定と線(赤点線)の設定
    plt::named_plot("gaussian", x, y, "r--");

    style["label"] = "y = sin(2 * pi * x) - 2";
    style["color"] = "black";
    style["marker"] = "+";
    style["markeredgecolor"] = "green";
    style["linestyle"] = "-.";

    // mapを使った凡例の設定と線の色の設定
    plt::plot(x, z, style);

    // 表示範囲の設定
    plt::xlim(0, 12);
    plt::ylim(-4, 10);

    // 軸ラベルの設定
    plt::xlabel("x");
    plt::ylabel("y");

    // グラフタイトルの設定
    plt::title("sample graph");

    // 凡例の表示
    plt::legend();

    plt::show();

    return 0;
}

出力結果

図1

そのほかいろいろなグラフを書くことができます。どのようなグラフを書くことができるかは、GitHubのリポジトリにあるサンプルコードが参考になります。

おわりに

以上でC++でPythonのmatplotlibを使うことができるライブラリ、matplotlib-cppについて使い方をまとめてみました。Windowsでは使い方で少し注意が必要な点がありますが、本家matplotlibと似たような書き方で簡単にグラフが表示できるので、今後も使ってみたいライブラリです。

C++,matplotlib

Posted by izadori