Blogger の記事作成を Markdown で行う - グラフ編

アイキャッチ画像

概要

前回の記事 で、Blogger の記事を、 Markdown から生成する方法を記載したが、 今回は、グラフの管理方法を記載する。 ただ、方法については、まだまだ模索中であり、 この記事も、随時更新して行くことになりそうだ。

記事によっては、グラフを含めたいことがあるので、 ライブラリを探してみた結果、 Plotly ライブラリが良さそうなので、採用することにした。 このライブラリは、JavaScript にも Python にもパッケージがある。 ただ、Markdown に直接記載できて、 ローカルで結果を直ぐに確認できるような方法は、見当たらない。

最初は、JavaScript のパッケージを利用しようとしたが、 常時読み込むようにすると、サイズが数メガ増えるので、 Blogger では、Plotly を利用しないことにした。 代わりに、Markdown の plotly コードブロックに、 図を生成する Python ソースファイル名を記載し、 Pandoc フィルタで、その Python ソースを実行して図を生成する方式を採用した。

この方式の利点は、図の確認が容易で、使い回しが簡単なことである。

Python 環境を構築する。

ローカルに Python 環境を構築する。 Conda 環境(ここでは Miniconda3)は、導入済みとする。

Python 環境を追加する。

Windows の場合、現時点で kaleido v0.2.1 をインストールすると、 to_image() メソッド呼び出したり、 IntelliJ のターミナルで実行や、ソースコードの実行を行うと show() メソッドが ハングアップするので、v0.1.0.post1 をインストールしている。 WSL の Ubuntu 24.04 上では、0.2.1 でも問題は発生しなかった。

conda create --name plotly -y python=3.12
conda activate plotly
python -m pip install -U pip setuptools wheel
pip install pandas kaleido==0.1.0.post1 pillow plotly panflute

Plotly 用の Pandoc フィルタを作成する。

ファイルの文字コードは、UTF-8 とすること。

[plotly-filter.py]

import re

import panflute as pf

svg = b""


def action(elem, doc):
    if isinstance(elem, pf.CodeBlock) and 'plotly' in elem.classes:
        # グラフ描画用の Python ソースをファイルから読み込む。
        with open(elem.text.strip(), 'r', encoding='utf-8') as f:
            src = f.read()
        # .show メソッドの行を、SVG タグ変換処理に置換する。
        src = re.sub(r"(.*)\.show\(.*", "global svg\nsvg = \\1.to_image(format='svg')", src)
        # SVG タグに変換する処理を実行する。
        exec(src)
        # Plotly コードブロックを、生成した SVG タグに差し替える。
        div = pf.Div(pf.RawBlock(svg.decode()), classes=['ichili-plotly-svg'])
        return div


def main(doc=None):
    return pf.run_filter(action, doc=doc)

show() メソッドの置換は、以下のようにしている。

fig.show()
↓
global svg
svg = fig.to_image(format='svg')

図を生成する Python ファイルを作成する。

show() メソッドは必ず記述する。 ただし、関数内には記述しないこと。 ファイルの文字コードは、UTF-8 とすること。

[plot1.py]

import plotly.express as px

df = px.data.gapminder().query("country=='Canada'")
fig = px.line(df, x="year", y="lifeExp", title='Life expectancy in Canada')
fig.show()

Markdown に Plotly の図を定義する。

Plotly コードブロックの記述は、以下のようにする。 Python のソースファイル名を記述する。

```plotly
plot1.py
```

Markdown を HTML に変換する。

conda activate plotly
pandoc --no-highlight --wrap=preserve --preserve-tabs --mathjax --lua-filter=mermaid-filter.lua --filter=plotly-filter.py 入力.md -o 出力.html

出力した HTML では、Plotly コードブロックに、 SVG タグが生成されているはず。

<div class="ichili-plotly-svg">
    <svg class="main-svg"> ・・・ 中略 ・・・
    </svg>
</div>

実際の表示は以下のようになる。

19601970198019902000707274767880Life expectancy in CanadayearlifeExp

IntelliJ の External Tools に登録する場合。

フィルタは、プロジェクトの src/filters に配置している。

項目
Name: to html
Group: External Tools
Program: cmd
Arguments: /c "conda activate plotly & pandoc --no-highlight --wrap=preserve --preserve-tabs --mathjax --lua-filter=$ProjectFileDir$\src\filters\mermaid-filter.lua --filter=$ProjectFileDir$\src\filters\plotly-filter.py $FileName$ -o $FileNameWithoutExtension$.html"
Working directory: $FileDir$