matplotlibでtexコマンドとformatを組み合わせるときにハマった話

要約

matplotlibでtexコマンドとstr.format()メソッドを併用した次のコードで問題が発生しました.

title = r'\texttt{shape}, $\mathrm{eps} = {eps}$'.format(shape=shape, eps=eps)

これを次のように書き換えて解決しました.

title = r'\texttt {shape}, $\mathrm{{eps}} = {eps:.0e}$'.format(shape=shape, eps=eps)

f:id:Panaki:20181020171806p:plain

1. はじめに

pythonには文字列メソッド str.format() が用意されています. 数値や文字列を様々な書式に変換してくれるメソッドです.

例えば,1.2345 → 1.2 という変換を行いたいとき.

print("{num:.2}".format(num=1.2345))
>> 1.2

引数を指定できるのが便利ですよね.

formatの詳しい使い方はこちらで解説されています.

2. 問題と解決策

pythonで図を作るときにmatplotlibを使っています.

で,この図のタイトルを付けるときにハマりました.

matplotlibではグラフのラベルやタイトルを付けるときにtex記法が使えます.

texコマンドは\command{str}という感じで使いますが,この {}と先ほど紹介した'{}'.format()がケンカしました.

コード

title = r'\texttt{shape}, $\mathrm{eps} = {eps}$'.format(shape=shape, eps=eps)
ax.set_title(title, fontsize=40)

エラー

RuntimeError: latex was not able to process the following string:
b'\\\\textttsmooth, $\\\\mathrm0.0001 = 0.0001$'

どうやら,formatメソッドによって\texttt{shape}{}が展開されて,\textttshapeになってしまったようですね. 後ろも同じように,\mathrm0.0001になっていますね.

2.1 解決策

前者ではformatによって代入された文字列を\textttに渡して欲しい.

  • \texttt{shape}の間にスペースを挟んで,\texttt {shape}としたら解決しました.

後者では eps = 1e-4と表示されて欲しい.

  • \mathrm{eps}epsformatの引数ではなく,単なる文字列として扱われるように,\mathrm{{eps}}としてあげれば解決ですね.
  • 一桁の指数表示にしたいので,\mathrm{{eps:.0e}}とすれば希望の表示になりそうです.

最終的に以下のコードで下の図の生成に成功しました.

(引数名変えたり$$外せばいいんですけどね...)

fig = plt.figure()
ax = plt.subplot(111)

arx = np.linspace(0, 2*np.pi, 100)
ary = np.array([np.cos(x) for x in xx])
ax.plot(arx, ary)

eps = 0.0001
shape = 'smooth'

title = r'\texttt {shape}, $\mathrm{{eps}} = {eps:.0e}$'.format(shape=shape, eps=eps)
ax.set_title(title, fontsize=40)

f:id:Panaki:20181020170529p:plain

3. まとめ

texコマンドと'{str}'.formatメソッドを組み合わせるときの注意点と解決策は以下のとおりです.

texコマンドの{}内の文字と,formatメソッドの引数名を同名にすると展開されてコマンド名の一部になる

  • formatメソッドの引数をtexコマンドに渡したいとき
    • texコマンドと引数の間にスペースを入れて'\command {name}'.format(name)とする
  • formatメソッドの引数と同名の文字列を,文字列としてそのまま使いたいとき
    • 二重括弧で囲んで,\command{{name}}とする.