3

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.

Eypros
  • 5,370
  • 6
  • 42
  • 75
Forrest Thumb
  • 401
  • 5
  • 16
  • The question is when do you want to render the plot? In your case (lots of data points) it is better to render the plot with matplotlib, and not store it as a vector graphic to be interpreted and rendered later (by the printer or PDF viewer). So your real question boils down to "How can I make matplotlib render the plot contents, but keep everything else as vector/text?" I would be intrigued to know if this is possible as well. – ypnos May 24 '18 at 11:06
  • In your case, the solution probably is just to use `imshow` instead of `pcolormesh`. – ypnos May 24 '18 at 11:10
  • @ypnos using imshow comes usually with the price of not having appropriate x- and y-ticks. I actually need them. Also, using imshow leads to storing the plot as png which is not really what I want to do since I started doing all this vector graphic stuff with the purpose of not using down-sampled png graphics. They usually look not that nice in pdf documents. – Forrest Thumb May 24 '18 at 11:32
  • No, the usage of imshow has nothing to do with the backend used for storing the plot. It works perfectly fine with PDF backend, for example. Try `extent` option to imshow for custom axis range. – ypnos May 24 '18 at 11:40
  • @ypnos I found a solution using the svg backend: just add rasterized = True to the plt.pcolormesh() command. This will cause the plot to be rendered in python (and then to be stored as a png somewhere on my hard drive) and the text to be text. The svg file contains a link to the png file and will be perfectly converted to pdf using inkscape. – Forrest Thumb May 24 '18 at 11:45
  • Great! Gonna remember this. I would expect it to embed the rasterized graphics directly if you save with PDF backend. – ypnos May 24 '18 at 11:47
  • Maybe... I'm not sure about the PDF backend. But remember, I use the svg backend (and \$ instead of $ in the plt.title and plt.xlabel) to make latex render my text once I compile the tex document. That's why I prefer using the svg instead of the pdf backend. – Forrest Thumb May 24 '18 at 12:03
  • @Forrest Thumb since you provided the answer in comment you can add it as a proper answer to help others facing the same problem. – Eypros May 24 '18 at 12:43

1 Answers1

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

Forrest Thumb
  • 401
  • 5
  • 16