5

I'm trying to plot a heatmap with seaborn and export it to pgf for easy import in Latex. This works more or less, the only thing that is bothering me is that the bar for the colours is exported as an extra png.

So if I run the export to pgf I get the following:

  • test.pgf
  • test-img0.png

This is definitely undesirably as a png is not scaling well. Some MWE:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame.from_csv("test.csv", sep=',')

sns.set('paper', 'white', rc={'font.size': 10, 'axes.labelsize': 10, 'legend.fontsize': 8, 'axes.titlesize': 10,
                              'xtick.labelsize': 8,
                              'ytick.labelsize': 8, "pgf.rcfonts": False})
plt.rc('font', **{'family': 'serif', 'serif': ['Times']})
plt.rc('text', usetex=True)
fig = plt.figure()
ax = sns.heatmap(df, linewidths=.5)
fig.savefig('test.pdf')
fig.savefig('test.pgf')

And the content of test.csv:

0,1,2,3,4
1,1,1,1,1
2,2,2,2,2
3,3,3,3,3

Does anyone have a solution to include the colour bar into the pgf output?

Daan
  • 940
  • 10
  • 22
Patrick
  • 275
  • 4
  • 14
  • 1
    I tested your code on seaborn 0.7.0 and matplotlib 1.5.1 in python 2.7 and python 3.5. Running the pgf output through latex gave me a nice figure with colourbar. Can you include your output in the question? – Daan Jun 20 '17 at 14:19
  • I guess the code works fine. But the problem OP has is that they want to include the `test-img0.png` in the pgf output and not as a separate png file. – ImportanceOfBeingErnest Jun 20 '17 at 14:28
  • I went a bit too fast there, thanks for pointing that out @ImportanceOfBeingErnest ! – Daan Jun 20 '17 at 22:38

1 Answers1

5

I discovered some peculiarities in the libraries that export to pgf/tikz.

First, in matplotlib, the quadmesh (i.e. the heatmap) is exported correctly to pgf, but the colorbar is exported as png (what the op encountered).

I tried the same thing with the matplotlib2tikz library. in this case, the quadmesh is exported as a png, but the colormap can be exported correctly. However, If you suppress the heatmap, for example by plotting the colormap to a separate Axes and clearing the heatmap axes, the colorbar is exported as png.

So if you combine the output of two exports, you can have them both.

The heatmap:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
df = pd.DataFrame.from_csv("test.csv", sep=',')

sns.set('paper', 'white',
        rc={'font.size': 10, 'axes.labelsize': 10,
            'legend.fontsize': 8, 'axes.titlesize': 10,
            'xtick.labelsize': 8,'ytick.labelsize': 8, 
            "pgf.rcfonts": False,
           })
plt.rc('font', **{'family': 'serif', 'serif': ['Times']})
plt.rc('text', usetex=True)
fig, ax = plt.subplots(figsize=(6,6))
ax = sns.heatmap(df, linewidths=.5, ax=ax, cbar=False)
fig.savefig('heatmap_nocbar.pgf')

Note that I suppress the plotting of the colorbar with cbar=False

Then get the colorbar:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from matplotlib2tikz import save as tikz_save

df = pd.DataFrame.from_csv("test.csv", sep=',')

sns.set('paper', 'white',
        rc={'font.size': 10, 'axes.labelsize': 10,
            'legend.fontsize': 8, 'axes.titlesize': 10,
            'xtick.labelsize': 8,'ytick.labelsize': 8, 
            "pgf.rcfonts": False,
           })
plt.rc('font', **{'family': 'serif', 'serif': ['Times']})
plt.rc('text', usetex=True)
fig, ax = plt.subplots()
ax = sns.heatmap(df, linewidths=.5, ax=ax, )
tikz_save('colormap.tex', figure=fig, strict=True)

The solution now requires some dirty work. I am aware that this is really not good practice, but this is the only way I could find.

In the colormap.tex file, you need to delete all the lines except the ones needed for the colormap.

The beginnings of colormap.tex:

\begin{tikzpicture}

\begin{axis}[

... % definition of axis: delete these lines

colorbar,
colormap={mymap}{[1pt]
...

So that you get this:

\begin{tikzpicture}

\begin{axis}[
hide axis,
colorbar,
colormap={mymap}{[1pt]
...

At the end of the file, delete everything between the closing of the square brackets and \end{axis}:

]
... %delete these lines
\end{axis}

This change in the file can be automatised, I included an example in the code on github

In your main tex file, you can add these two figures

\input{heatmap_nocbar.pgf}
\input{colorbar.tex}

If you want a full working example and full outputs, I have put everything from this answer on github.

This is probably far from a perfect answer for you, but I lack the python skills to dive deeper in the backends of matplotlib and matplotlib2tikz to give a nicer example.

Daan
  • 940
  • 10
  • 22
  • I kind of expected that it wouldn't be easy. I will try out your solution. Merging the files automatically would definitely be an improvement though. – Patrick Jun 21 '17 at 14:38
  • I wrote a quick code snippet to extract the lines from colorbar.tex, @Patrick. You can find it in the github repo (link in answer) – Daan Jun 21 '17 at 16:06