アルゴリズムとかオーダーとか

仕事で勉強したことなどをまとめてます

Docker環境でnltk.Treeグラフをpngとして出力する

今回も機械学習関連の記事である。
nltk.TreeのデータをPNG画像として出力したかったのですが、Docker環境で開発を行なっていた関係でちょっと手こずってしまった。
Docker環境でPNGを出力させる方法がなかなか見つからなかったので、記事としてまとめておく。

結論としては、以下のstackoverflowの回答でうまくいった。
stackoverflow.com

よくみる回答

nltk.TreeをPNG画像として出力する方法は、以下の解答がよく見つかる。
stackoverflow.com

コードとしては以下の通り。

from nltk.tree import Tree
from nltk.draw.tree import TreeView
import os

t = Tree.fromstring('(S (NP this tree) (VP (V is) (AdjP pretty)))')
TreeView(t)._cframe.print_to_file('output.ps')

os.system('convert output.ps output.png')

発生したエラー

上記のコードを実際に試すと、以下のエラーが発生した。

TclErrorTraceback (most recent call last)
Input In [2], in <cell line: 6>()
      3 import os
      5 t = Tree.fromstring('(S (NP this tree) (VP (V is) (AdjP pretty)))')
----> 6 TreeView(t)._cframe.print_to_file('output.ps')

File /opt/conda/lib/python3.9/site-packages/nltk/draw/tree.py:859, in TreeView.__init__(self, *trees)
    855 from math import ceil, sqrt
    857 self._trees = trees
--> 859 self._top = Tk()
    860 self._top.title("NLTK")
    861 self._top.bind("<Control-x>", self.destroy)

File /opt/conda/lib/python3.9/tkinter/__init__.py:2270, in Tk.__init__(self, screenName, baseName, className, useTk, sync, use)
   2268         baseName = baseName + ext
   2269 interactive = False
-> 2270 self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
   2271 if useTk:
   2272     self._loadtk()

TclError: no display name and no $DISPLAY environment variable

エラーの原因

このエラーの原因としては、PNG画像生成に使っているtkinterライブラリに起因する。
このライブラリは(詳しくはわかってないが)canvasを生成する際にXWindowをコールする。
今回はDocker上にanaconda環境を用意したため、もちろんX11が入っていない。そのため上記エラーが発生した。

解決方法

記事冒頭で紹介した通り、svglingcairosvgを利用することで解決できる。
stackoverflow.com

必要なライブラリは、pip3でインストールできる。

pip3 install svgling cairosvg

matplotlibを用いた解決方法もあるらしい。自分はmatplotlibがそもそもインストールされていなかった目、試していないが紹介しておく。

python - _tkinter.TclError: no display name and no $DISPLAY environment variable - Stack Overflow

余談:pngの背景を白に変更する方法

stackoverflowの回答通りにpng画像を生成すると、背景が透過された画像が生成される。
背景を白にしたい場合は、以下の様にbackground_colorオプションを指定すれば良い。

t = Tree.fromstring('(S (NP this tree) (VP (V is) (AdjP pretty)))')
# convert tree to svg
sv = tree2svg(t)
# write the svg as an image
cairosvg.svg2png(sv.tostring(), write_to="image.png", background_color="white")