点文件的“锯齿状”输出

时间:2019-01-30 23:15:39

标签: python matplotlib

我一直试图将sklearn决策树中的.dot图插入pyplot子图中,并且一直在努力做到这一点。 pygraphviz库拒绝在我的Windows系统上工作,因此我使用了以下内容来插入图像:

from subprocess import check_call
from PIL import Image
import matplotlib.pyplot as plt 

gridSpec = fig.add_gridspec(3, 2)
treeSubPlot = plt.subplot(gridSpec.new_subplotspec((2, 1)))
treeSubPlot.tick_params(axis = "both", which = "both", bottom = False, top = False, labelbottom = False, right = False, left = False, labelleft = False)
treeSubPlot.set_xlabel("Final model")

# Obtain the size of a single plot in inches to inform the dot picture creator.
height = treeSubPlot.get_window_extent().y1 - treeSubPlot.get_window_extent().y0
width = treeSubPlot.get_window_extent().x1 - treeSubPlot.get_window_extent().x0

check_call(['dot','-Tpng', "-Gsize=" + str(width/100) + "," + str(height/100) + "!", "-Gdpi=100", "-Gratio=fill", 'random_tree.dot','-o','random.png'])
randImg = Image.open("random.png")
treeSubplot.imshow(randImg, aspect = "auto")

带有.dot文件:

digraph Tree {
node [shape=box] ;
0 [label="B field <= 332.72\nsamples = 19\nvalue = 0.41"] ;
1 [label="samples = 11\nvalue = 0.67"] ;
0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ;
2 [label="B field <= 576.73\nsamples = 8\nvalue = 0.04"] ;
0 -> 2 [labeldistance=2.5, labelangle=-45, headlabel="False"] ;
3 [label="samples = 4\nvalue = 0.05"] ;
2 -> 3 ;
4 [label="samples = 4\nvalue = 0.03"] ;
2 -> 4 ;
}

该图的结果是什么,但由于缺少更好的用语,它看起来非常“参差不齐”: Resulting subplot

我可以使用任何设置来阻止此操作吗?我正在尝试使图像完全适合子图,但仍然呈锯齿状。

1 个答案:

答案 0 :(得分:2)

有几个问题。

  • dot生成的图像实际上不是您在-Gsize={},{}!参数中指定的确切尺寸。
  • 存在四舍五入错误,因为matplotlib轴位于相对图形坐标中,但后来与轴的整数位置不完全匹配。
  • 最后,显示图像的轴非常小,因此上述效果更加明显。

一个可以提出的解决方案是找到轴的大致尺寸,调用dot程序并从中检索图像。然后根据实际图像尺寸设置轴的实际尺寸(与原始位置相比,这可能会使轴移动几个像素)。 在下面的代码中,我使用了更大的坐标轴。另外请注意,较高的dpi或图形尺寸会提高可读性。

import numpy as np
from subprocess import check_call
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.transforms as mtrans

dpi = 100
fig = plt.figure(dpi=dpi)
gridSpec = fig.add_gridspec(3, 2)
ax = plt.subplot(gridSpec.new_subplotspec((2, 1)))
#ax = fig.add_subplot(111)

ax.tick_params(axis = "both", which = "both", bottom = False, top = False, 
                        labelbottom = False, right = False, left = False, labelleft = False)
ax.set_xlabel("Final model")

# calculate nominal size supplied to `dot`
ext = ax.get_position().transformed(fig.transFigure).extents
bbox = mtrans.Bbox.from_extents(np.array(ext).astype(int))

height = bbox.height
width = bbox.width

# get image from dot
check_call(['dot','-Tpng', "-Gsize={},{}!".format(width/100, height/100),
            "-Gdpi={}".format(100), "-Gratio=fill", 'random_tree.dot','-o','random.png'])
randImg = np.array(Image.open("random.png"))

# get ACTUAL size from produced image
aheight, awidth, _ = randImg.shape

# create new bounding box from actual size
abbox = mtrans.Bbox.from_bounds(bbox.x0, bbox.y0, awidth, aheight)
# transform bbox to figure coordintes
abboxf = bbox.transformed(fig.transFigure.inverted())
# Use the thus created bbox to modify the position of the axes
ax.set_position(abboxf)

# finally plot the data
ax.imshow(randImg, aspect = "auto")
# save image (this should now be correct)
plt.savefig("randomdot_mpl.png")
# show image (this might still be slightly wrong, 
#             if the GUI changes the figure size on the fly.)
plt.show()

enter image description here

如您所见,结果仍然不够完美(例如,“值”中的l稍粗一些)。通常,更好的解决方案可能是根本不将图像放置在轴内,而使用figimage,这是放置在图形内的未经重采样的图像。如果仍要显示周围的轴,则需要使轴对“透视”透明。

这将通过以下代码完成

import numpy as np
from subprocess import check_call
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.transforms as mtrans

dpi = 100
fig = plt.figure(dpi=dpi)
#gridSpec = fig.add_gridspec(3, 2)
#ax = plt.subplot(gridSpec.new_subplotspec((2, 1)))
ax = fig.add_subplot(111)

ax.tick_params(axis = "both", which = "both", bottom = False, top = False, 
                        labelbottom = False, right = False, left = False, labelleft = False)
ax.patch.set_visible(False)
ax.set_xlabel("Final model")

# calculate nominal size supplied to `dot`
ext = ax.get_position().transformed(fig.transFigure).extents
bbox = mtrans.Bbox.from_extents(np.array(ext).astype(int))

height = bbox.height
width = bbox.width

# get image from dot
check_call(['dot','-Tpng', "-Gsize={},{}!".format(width/100, height/100),
            "-Gdpi={}".format(100), "-Gratio=fill", 'random_tree.dot','-o','random.png'])
randImg = np.array(Image.open("random.png"))

# produce a figimage, i.e. a non-resampled image
fig.figimage(randImg, xo=bbox.x0, yo=bbox.y0)

# save image (this should now be perfect)
plt.savefig("randomdot_mpl.png")
# show image (this should now be perfect)
plt.show()

enter image description here