有没有一种有效的方法在Python中将2D图形以矢量图形的形式存储?

3

我目前正在尝试将Python绘制的图形存储为矢量图,以改善它们在LaTeX文档中的外观。对于一维图形,这个方法效果很好:

import numpy as np
import matplotlib as mpl
mpl.use('svg')
new_rc_params = {
    "font.family": 'Times',
    "font.size": 12,
    "font.serif": [],
    "svg.fonttype": 'none'} #to store text as text, not as path
mpl.rcParams.update(new_rc_params)
import matplotlib.pyplot as plt

x = np.linspace(-.5, .5, 1024)

plt.figure()
plt.plot(x, x)
plt.title('\$x = y\$')
plt.xlabel('\$x\$ [m]')
plt.ylabel('\$y\$ [m]')
plt.savefig('test.svg', format = 'svg', bbox_inches = 'tight')

这样我就可以在Inkscape中打开svg文件并将其转换为pdf/pdf_tex,绘图中的每个文本都将在文档中以latex格式呈现-->与文档中其他地方相同的字体和字号。

2D图作为svg文件会变得非常大。因此,我想将绘图存储为pdf(再次说明,我希望将文本保留为文本。这就是为什么我不能将绘图存储为.png的原因):

mpl.use('pdf')
new_rc_params = {
    "font.family": 'Times',
    "font.size": 12,
    "font.serif": []
    }
    #"svg.fonttype": 'none'} #not needed here since we don't use svg anymore
mpl.rcParams.update(new_rc_params)
import matplotlib.pyplot as plt

x = np.linspace(-.5, .5, 1024)
x, y = np.meshgrid(x, x)
z = np.exp(-(x**2 + y**2))

plt.figure()
plt.title('Gaussian plot: \$z = \exp{-(x^2 + y^2)}\$')
plt.pcolormesh(x, y, z)
plt.colorbar()
plt.savefig('test.pdf', bbox_inches='tight', format='pdf')

这将把2D图形存储为pdf格式。无论如何,现在存储图形需要一些时间,而且它变得相当大(即使仅使用500 x 500个点,文件大小也约为11 MB)。但是,文本被存储为文本。

不幸的是,我现在无法在inkscape中打开pdf文件,因为它总是在一段时间后崩溃。可能文件已经太大了。有什么建议吗?在这种情况下进一步降采样可能有效,但在一般情况下可能不起作用。


问题就在于你想何时呈现图表?对于你的情况(许多数据点),最好使用matplotlib渲染图表,而不是将其存储为矢量图形以供稍后解释和呈现(由打印机或PDF查看器)。因此,你真正的问题归结为“如何使matplotlib呈现图表内容,但保持其他所有内容为矢量/文本?”我也很想知道这是否可能。 - ypnos
在您的情况下,解决方案可能只是使用imshow而不是pcolormesh - ypnos
@ypnos 使用imshow通常会带来没有适当的x和y刻度的代价。我实际上需要它们。此外,使用imshow会将绘图存储为png,这并不是我想做的事情,因为我开始使用矢量图形来避免使用降采样的png图形。它们在PDF文档中看起来通常不那么好看。 - Forrest Thumb
不,imshow的使用与用于存储图形的后端无关。例如,它可以与PDF后端完美地配合使用。尝试使用“extent”选项来自定义轴范围。 - ypnos
@ypnos,我找到了使用SVG后端的解决方案:只需在plt.pcolormesh()命令中添加rasterized = True。这将导致绘图在Python中呈现(然后存储为PNG文件在我的硬盘上),并且文本为文本。SVG文件包含指向PNG文件的链接,并且将使用inkscape完美地转换为PDF。 - Forrest Thumb
显示剩余3条评论
1个回答

1

这是我在评论中提出的答案:

大型pdf/svg文件是将pcolormesh中的每个矩形存储为矢量图形所导致的。

我想通过将绘图存储为svg/pdf来获得高分辨率的图像,在将文件插入我的latex文档时,文本只需要渲染一次。如果分辨率足够好,绘图本身并不需要成为矢量图形。

因此,这是我的建议(导入的库与上述相同):

mpl.use('svg')
new_rc_params = {
    "font.family": 'Times', #probably python doesn't know Times, but it will replace it with a different font anyway. The final decision is up to the latex document anyway
    "font.size": 12, #choosing the font size helps latex to place all the labels, ticks etc. in the right place
    "font.serif": [],
    "svg.fonttype": 'none'} #to store text as text, not as path
mpl.rcParams.update(new_rc_params)

plt.figure(figsize = (6.49/2, 6.49/2)) #that's about half the text width of an A4 document
plt.pcolormesh(x, y, z, rasterized = True) # That's the trick. It will render the image already in python!
plt.xlabel('Math expression: \$a + b = c\$') # We need the backslashes because otherwise python will render the mathematic expression which will confuse latex
plt.savefig('test.svg', dpi = 1000, format = 'svg', bbox_inches = 'tight') # depends on your final figure size, 1000 dpi should be definitely enough for A4 documents

存储svg文件后,打开Inkscape。保存为pdf,并在“省略PDF中的文本并创建LaTex文件”处打勾。在你的LaTeX文件中,你需要使用

\begin{figure}
    \centering
    \input{test.pdf_tex}
    \caption{This should have the same font type and size as your xlabel}
\end{figure}

导入您的二维图。就是这样 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接