Plots.jl set figure size for LaTeX publications

Hi,
I want to use Plots.jl to make figures for a publication. I want the width of the figure to be exactly 246.0 points (pt). The format should be pdf. I want to do this, so I can import the pdf figure into LaTeX later and have the fonts appearing the same as set for plotting.

I come from pyplot and there you can convert to inches and then set the size of the resulting figure. As far as I am aware of Plots.jl only lets you specifiy the size in pixels.
I found https://github.com/JuliaPlots/Plots.jl/issues/501 pointing this out.

But maybe my approach is wrong already. I would be thankful for help!

jamble

A way to achieve this is using the pgfplots backend to save the figure into a tikzpicture.

Instead of adjusting the size before you produce the plot, you can adjust the size in the generated .tex file. By using the package pgfplots in LaTeX, the plot will be drawn directly in the PDF, where you can adjust the size, fonts, colors, and any other property you can think of.

An example:

using Plots; pgfplots() # Need to add PGFPlots first
plot(1:3)
savefig("example.tex")

I personally do not like the output the juliaplots for the .tex files, as they seem a bit messy to me, but here the first line has the height and you can also specify a width:

\begin{tikzpicture}[]
\begin{axis}[height = {101.6mm}, ylabel = {}, xmin = {0.94}, xmax = {3.06}, ymax = {3.06}, xlabel = {}, unbounded coords=jump,scaled x ticks = false,xlabel style = {font = {\fontsize{11 pt}{14.3 pt}\selectfont}, color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000}, draw opacity = 1.0, rotate = 0.0},xmajorgrids = true,xtick = {1.0,1.5,2.0,2.5,3.0},xticklabels = {$1.0$,$1.5$,$2.0$,$2.5$,$3.0$},xtick align = inside,xticklabel style = {font = {\fontsize{8 pt}{10.4 pt}\selectfont}, color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000}, draw opacity = 1.0, rotate = 0.0},x grid style = {color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000},
draw opacity = 0.1,
line width = 0.5,
solid},axis x line* = left,x axis line style = {color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000},
draw opacity = 1.0,
line width = 1,
solid},scaled y ticks = false,ylabel style = {font = {\fontsize{11 pt}{14.3 pt}\selectfont}, color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000}, draw opacity = 1.0, rotate = 0.0},ymajorgrids = true,ytick = {1.0,1.5,2.0,2.5,3.0},yticklabels = {$1.0$,$1.5$,$2.0$,$2.5$,$3.0$},ytick align = inside,yticklabel style = {font = {\fontsize{8 pt}{10.4 pt}\selectfont}, color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000}, draw opacity = 1.0, rotate = 0.0},y grid style = {color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000},
draw opacity = 0.1,
line width = 0.5,
solid},axis y line* = left,y axis line style = {color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000},
draw opacity = 1.0,
line width = 1,
solid},    xshift = 0.0mm,
    yshift = 0.0mm,
    axis background/.style={fill={rgb,1:red,1.00000000;green,1.00000000;blue,1.00000000}}
,legend style = {color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000},
draw opacity = 1.0,
line width = 1,
solid,fill = {rgb,1:red,1.00000000;green,1.00000000;blue,1.00000000},fill opacity = 1.0,text opacity = 1.0,font = {\fontsize{8 pt}{10.4 pt}\selectfont}},colorbar style={title=}, ymin = {0.94}, width = {152.4mm}]\addplot+ [color = {rgb,1:red,0.00000000;green,0.60560316;blue,0.97868012},
draw opacity = 1.0,
line width = 1,
solid,mark = none,
mark size = 2.0,
mark options = {
            color = {rgb,1:red,0.00000000;green,0.00000000;blue,0.00000000}, draw opacity = 1.0,
            fill = {rgb,1:red,0.00000000;green,0.60560316;blue,0.97868012}, fill opacity = 1.0,
            line width = 1,
            rotate = 0,
            solid
        }]coordinates {
(1.0, 1.0)
(2.0, 2.0)
(3.0, 3.0)
};
\addlegendentry{y1}
\end{axis}

\end{tikzpicture}

Using the standalone class (for example), the code above generates this figure (which I converted from PDF to PNG to upload it here):
test__Copy_

Finally, when using PGFPlots within a LaTeX document and you have settled on the size/look, you can use TikZ external library to print the figure into a standalone PDF with the size you have already selected, and that you can include directly into your document. This will make the compilation of the document a lot faster in many cases, specially when the figures need lots of points to be drawn.

Hope this helps!

2 Likes

Thanks for the detailed reply. I have some problems with this approach:

  • it seems a lot of steps which have to be done by hand: light post processing of data in Julia, exporting to .tex file, changing stuff in .tex file, generating pdf. I liked my workflow so far: light post processing + plotting + exporting in Julia in one function call -> recompiling (F5) latex document.
  • the next issue would be what type of figure processing will I do in Julia with Plots.jl and what in tikz. If I once generated and edited the .tex file I can not change the data anymore without copying or change the image processing with Plots.jl without redoing by hand everything in the .tex file again.
  • the .tex file blows up very quickly for my plots.
  • There are still some features which are not translated by Plots.jl to pgfplots, like axis mirroring, boxes around graphs, legend placing. At least this did not work in my case when changing from pyplot to pgfplots. This might be fixable.

Actually, I do not really understand why there is an issue with specifiying the size of an image in inches/mm/pt. Shouldn’t this be one of the first features implemented in a (meta) plotting package? Please correct me, if I am wrong!

EDIT: I found some more links/people having issues with that: https://github.com/JuliaPlots/Plots.jl/issues/897

2 Likes

What is the conversion between 246 points and a different unit? Is it 72 points per inch, i.e., you are looking for a figure to be 3.416 inches wide? If so, the only question is how many pixels per inch, i.e., dots per inch (dpi). Some journal require 300 dpi. If so, you would need to save a figure that is 1025 pixels wide. You can do that with the size argument in the Plots.jl plot function.

Would exporting to an SVG (or other vector graphics) be useful here? This way you can set the figure to any size without loss of resolution.

Also, could you provide the journal’s requirements on figures? That might be helpful.

Hi josephmarturano,
the Journal is two columns and the column width is exactly 246 pts. I used pyplot before and converted to inch and used something like set_size_inches((width_inch,height_inch)) on the final figure object.

It is of course an option to specify the dpi and convert to pixels and store in a pixel format. But I like to save as vector format and do not have to think about dpi at all - let the journal specify this. I do not know by now what dpi they require for pixel graphics.

The reason I am worried a bit about the exact figure size is that I want the fontsizes set in the figure appear exactly as the font sizes in the document.

Most of this should be fixed in the pgfplotsx-backend and if not, please open an issue :slight_smile:

In the context of TeX documents, 1 inch is equivalent to 72.27 pt. [ref]

Ah, I was not aware that there are 2 pfgplot wrappers. Is there a reason for that?

I like using matplots pyplot backend so far. Is there a possibility to set the size of the underlying pyplot object? I tried plotjl_fig.o.set_size_inches but it did not have any affect on the output image when I save with savefig from Plots.jl.

I didn’t add too many details in my initial answer so that it wouldn’t be too long. However:

  • I use the \input{} command in LaTeX to load the file, so if it it saved in the right place, there’s no extra manual steps.
  • In pgfplots you can load the data into your plot from a .dat or CSV file. Therefore, if you don’t want to change the figure every time (which is completely understandable, I don’t either), you can just export the data once you’re happy with the looks of the plot. Here https://tex.stackexchange.com/a/83929 is an example on how to achieve that. Note that in that example they mock the CSV file.

I agree that there are still some more steps involved, but in my opinion, the quality of the plots is much better with this approach. For example, fonts are consistent with the document, which is hard to achieve when loading a file produced with any external library.

2 Likes

If you know the PyPlot commands already, why wouldn’t you use PyPlot.jl directly without Plots.jl?

Mainly different tastes of interfaces.

1 Like

I just want to summarize, so I am sure I understood correctly what you suggested:

  • I export the data to e.g. csv file with Julia
    (from here no Julia)
  • for every figure in my document I have a seperate .tex file, where I do the plotting with pgfplots and use e.g. standalone class to save as pdf
  • then I import the .pdf figure, as I already do.

So, in other words, you suggest not using any Julia plotting library/wrapper at all, but instead use pgfplots directly in LaTeX, not the pgfplots or the pgfplotsx backend?

I asked people in my department, but very few are using Julia and none of them are doing their plots with Julia.

I hoped to get away with using Julia code only and not having to learn a new (complicated) plotting interface/language.

EDIT: I am still in the phase of finding what works best for me. I am always open for suggestions and ideas!

I am not an expert in PyPlot. I find the interface very unintuitive and complicated. Often I ended up trying 3,4 different ways of achieving something and only one of them worked and I could not figure out why. But this might just be because I never really learned how to use it properly.

Using Plots.jl made my code much cleaner and more readable. And it did what I thought it should do. Often the first time I tried. And I liked the idea of having different, but similar aliases for setting the same parameter, so you do not have to look up the exact spelling, e.g. underscore or not, shortcut or written out, etc.

This is the approach that I use in Julia (using the pgfplots backend), Python (using tikzplotlib), and MATLAB (using matlab2tikz):

  • I generate a plot with any of the tools above. In the case of Julia, to the best of my knowledge, the data cannot be saved using savefig (unlike matlab2tikz for instance), so I export the data to CSV (using for example CSV.jl and call it from the .tex file. Here, changing the .tex file once is required so that it reads the data from the CSV file.
  • I use the following in my main .tex file:
\usetikzlibrary{external} % Can be placed as a list with other packages for clarity
\tikzexternalize[prefix=figures/] % Folder in which files are saved

\begin{document}
\tikzexternaldisable % No figures are externalized unless I require it
.
.
.
\tikzexternalenable % Enable externalization
\begin{figure}
\centering
\tikzsetnextfilename{example_figure} % name with which the figure will be saved
\input{example_figure.tex} % This file has the code generating the figure using PGFPlots
% \includegraphics{figures/example_figure.pdf} % use after externalizing, if needed
\caption{}
\end{figure}
\tikzexternalenable % Disable externalization
  • I tweak the file example_figure.tex for desired size and looks (just once). For example, for width I usually use:
width=\columnwidth % in example_figure.tex
  • If I need to change the data in the plot, I re-export the data and compile the .tex file again, without needing to change the .tex file.
  • Once I am happy with the plot, I convert it to PDF by compiling the file using shell escape:
$ pdflatex --shell-escape main.tex

This generates a PDF file called example_figure.pdf in the figures/ folder (as selected above). Then, I uncomment the line % \includegraphics{figures/example_figure.pdf} and comment the line \input{example_figure.tex} above and my figure is loaded into the main document as a PDF file. Note that you can use a standalone file to compile the figure, but this is not required (or needed).

For a bit of background, I converged to this method for a few reasons:

  • It works well across languages, so if I am working in Julia, Python, or MATLAB I can use the same steps to generate a high-quality figure,
  • If the data changes, I only need to export the data, but the plot stays the same (except for the externalization step, which needs to be re-run),
  • Figures can be very easily shared between LaTeX documents (for example, generate figures for a paper and then use them in a poster or presentation), where the appropriate fonts and sizes are used in each case.

I understand that the method that I just described requires a few more manual steps than what you were originally describing and that it’s not Julia-specific, but it allows you to achieve what you intend.

Hopefully you find a workflow that works for you.

4 Likes

There is also

if you like working with PGFPlots/LaTeX more directly.

Hi @kmundnic,

Thank you for your very detailed answer. This thread is moving away from asking a simple question to organizing my figure creation work process! :smiley:

I try to understand how you do your plotting. I do not understand, how you use Julia’s Plots.jl library. What data do you export? Only the raw data or is there plotting data encoded in the .csv file? If the first is true, where does Plots.jlget used? If the latter is true, don’t you have to retweak the example_figure.tex file everytime you change plotting settings in Julia’s Plots.jl?
Maybe, if you have time and are willing to, you could explain a bit more the content of example_figure.tex and when it needs changing.

Thanks, another time for your input!

@jamblejoe for convenience, I uploaded a full example in https://github.com/kmundnic/pgfplots-example. Feel free to download it and experiment with it, and ask questions here or add issues in the repo itself if needed.

3 Likes

Hi @kmundnic!

Thanks for this awesome answer! I had a look in the repository(https://github.com/kmundnic/pgfplots-example) already. Thanks a lot! I will open an issue there in case I have any further questions.

In case people are visiting this thread with a similar question, I will post the answert to my initial question. I digged in the source code a bit and worked out, how Plots.jl is setting thefigure size for the pyplot backend. They use an internal DPI value of 100, set by the constant DPI, found in … . The first line in the function _create_backend_figure in pyplot.jl tells the story:

w,h = map(px2inch, Tuple(s * plt[:dpi] / Plots.DPI for s in plt[:size]))

The size of the figure set by Plots.jl size argument is in pixels and then converted to inch using the DPI value of dpi( which can be set via plot(dpi=100)).
If you do not want to safe in a pixelformat, you do not have to care about the DPI value. Converting from pts to the correct inches works as follows:

width_pts = 246.0  # or any other value
inches_per_points = 1.0/72.27
width_inches = width_pts *inches_per_points
width_px= width_inches*100  # or  width_inches*DPI

Setting plot(size=(width_px, width_px)) now creates a square plot with exactly 246 points.

3 Likes