I would like to better integrate my plots into a LaTeX document. I know about PGFPlots(X) and I really like the idea, unfortunately I could only really work with Py(thon)Plots so far, because of the lack of features in other plotting backends. Is there a way to export plots similar to the way inkscape can do it, by splitting the file into a .pdf and a .pdf_tex containing all the text, including latex formatting?
My setting is maybe not the best option, but it works for my purposes. I use CondaPkg, PythonPlot, and PythonCall, and use for plotting either matplotlib or seaborn. To save it as LaTeX, I use tikzplotlib.
Note that this comes with some cons:
- tikzplotlib is seemingly not well maintined
- it most likely will not work with the newest version of matplotlib (I use 3.5.2 for that)
- When the plots get “complicated enough” the conversion to tikz is buggy. One has to repair this by hand.
Those seem to be some pretty big cons unfortunately. If I understand correctly, you’re using PythonPlot directly rather than through the Plots interface? I’m using Plots with the PythonPlot backend, so if I’d have to re-write all my plotting code I’d probably just leave it the way it is ![]()
Do you have some code that you’d like to share? I’m not too familiar with calling Python from Julia. A code example would really help me decide whether the effort will be worth it.
Exactly, I use PythonPlot directly (for historic reasons). I currently live with the cons, because I also do not want to re-implement my plotting code. You can find examples of the output here (in Section 5), and here (in Sections 6.6, and 6.7).
One advantage of this is that the pictures can be scaled ex-post such that they fit nicely into the LaTeX document.
Good that I saw your post today because my plotting framework stopped working. The reason is that tikzplotlib relies on webcolors which was adapted as reported here. So I found matplot2tikz which replaces tikzplotlib and seems to be maintained. For a code snippet, I used an AI to extract from my code and mimics the logic of my generic plot functions. Should work if you install the required packages. This should output a boxplot with random data.
using CondaPkg
using PythonCall
using DataFrames
using Random
# ==============================================================================
# 1. CONDA ENVIRONMENT SETUP
# ==============================================================================
println("--- Setting up Python Environment ---")
CondaPkg.add("matplotlib", version="=3.5.2")
CondaPkg.add_pip("matplot2tikz")
# ==============================================================================
# 2. PYTHON IMPORTS
# ==============================================================================
plt = pyimport("matplotlib.pyplot")
matplot2tikz = pyimport("matplot2tikz")
# Set global matplotlib params (mimicking declarations.jl)
plt.rcParams["font.family"] = "serif"
plt.rcParams["legend.framealpha"] = 0.9
plt.rcParams["savefig.format"] = "png"
# ==============================================================================
# 3. GENERIC PLOT FUNCTION (Adapted from plot_functions_generic.jl)
# ==============================================================================
"""
Adapted simpleBoxPlot that mimics the logic of the uploaded file
"""
function simpleBoxPlot(rdf::DataFrame, xcat::Symbol, ycat::Symbol)
# Construct output filename (mimicking original logic)
ofile = "bp_" * string(ycat) * "_by_" * string(xcat)
# Sort DataFrame (mimicking original logic)
sortorder = true
# xcat == :instCat ? sortorder = true : nothing # Context specific logic removed
sort!(rdf, (xcat), rev=sortorder)
# Get categories (mimicking original logic)
s_vals = unique(rdf[!, xcat])
# Prepare data for Matplotlib
# NOTE: We use Python lists (pylist) here to ensure PythonCall compatibility.
# The original file used Array{Any}, but PythonCall requires explicit lists
# for recent Matplotlib/Pandas interoperability.
data_to_plot = []
mylabels = []
for val in s_vals
sub_data = rdf[rdf[!, xcat] .== val, :][!, ycat]
push!(data_to_plot, pylist(sub_data))
push!(mylabels, string(val))
end
# Plot Settings (Defaults since 'p' struct is removed)
xsize = 6
ysize = 5
showfliers = true
# Create Figure
fig = plt.figure(figsize=(xsize, ysize))
# Plot Boxplot
# We use pylist() on the outer vector to ensure it is a list of lists
bp = plt.boxplot(pylist(data_to_plot), labels=pylist(mylabels), showfliers=showfliers, widths=0.7)
# Labels and Formatting
xcat_name = string(xcat)
ycat_name = string(ycat)
plt.xlabel(xcat_name)
plt.ylabel(ycat_name)
# Rotate labels if needed (mimicking original logic)
if xcat == :ifile
plt.xticks(rotation=90)
end
plt.tight_layout()
# Save outputs
println("Saving PNG to: $ofile.png")
plt.savefig(ofile * ".png")
println("Saving TikZ to: $ofile.tex")
# Switched to matplot2tikz.save
matplot2tikz.save(ofile * ".tex")
# Clean up
plt.close(fig)
return nothing
end
# ==============================================================================
# 4. DATA GENERATION & EXECUTION
# ==============================================================================
println("--- Generating Random Data ---")
rng = MersenneTwister(42)
n_samples = 50
# Create a DataFrame with random data
# We have a category column (:Category) and a value column (:Runtime)
julia_df = DataFrame(
Category = vcat(fill("Algorithm A", n_samples), fill("Algorithm B", n_samples), fill("Algorithm C", n_samples)),
Runtime = vcat(randn(rng, n_samples) .+ 10, randn(rng, n_samples) .+ 12, randn(rng, n_samples) .+ 15)
)
println("--- Creating Plot ---")
# Call the adapted function
simpleBoxPlot(julia_df, :Category, :Runtime)
println("--- Done! ---")
thanks, that looks like something I can work with. Those plots are pretty neat. I just need to adapt it to work with Plots. I’ll get back to this thread once I’ve managed to find time to do that.
Turns out its quite simple. For a plot generated with Plots using p=plot(... I need to add the following lines:
using CondaPkg, PythonCall
CondaPkg.add_pip("matplot2tikz")
matplot2tikz = pyimport("matplot2tikz")
display(p)
fig=Plots.backend_object(p)
matplot2tikz.save("tikzexport.tex", figure=fig)
Unfortunately, the result is not usable out of the box. The colorbar label is on the wrong side (inside the plot), my custom tick labels are not respected and log axes don’t seem to be supported. I could fix the colorbar label position, but fixing ticks manually is too cumbersome.