Is there an efficient way to store 2D plots as a v

2020-06-26 04:19发布

问题:

I'm currently trying to store python plots as vector graphics to improve their appearance in a latex document. For 1D plots this works quite fine:

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')

This way I can open the svg file in inkscape and convert it to pdf/pdf_tex and every text in the plot will be rendered in latex within the document --> same font and fontsize as everywhere else in the document.

2D plot get increadibly large as svg files. Therefore I want to store the plot as a pdf (again, I want to keep text as text. That's why I can't store the plot as .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')

This stores the 2D plot as a pdf. Anyway, storing the plot takes a while now and it gets quite large (even with only 500 x 500 points in the plot it's about 11 MB). But, the text is stored as text.

Unfortunately I can't open the pdf in inkscape now, for it always crashes after a while. Probably the file is already to large. Any suggestions? Further downsampling might work in this case, but probably not in general.

回答1:

Here is an answer I suggested in the comments:

The large pdf/svg files result from storing every rectangle in pcolormesh as a vector graphic.

What I wanted to achieve with storing the plot as svg/pdf was to get a high-res image where the text is rendered once I insert the file in my latex document. The plot itself does not really need to be a vector graphic if the resolution is good enough.

So here is my suggestion (imported libraries are the same as above):

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

Once you have stored the svg file, open it in Inkscape. Save as pdf and set the tick at 'Omit text in PDF and create LaTex file'. In your latex file you have to use

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

to import your 2D plot. That's it :)