Reut
Reut

Reputation: 1592

Create line plot on secondary y axis on top of heatmap draw line on wrong x-axis

I have two tables, one which I generate from heatmap and one which Ineed in order to draw line chart on secondary y axis. Creating the heatmap works no problem :

green = sns.light_palette("seagreen", reverse=True, as_cmap=True)
green.set_over('tomato')
sns.set(rc={'figure.figsize': (20.7, 10.27)})
sns.set(font_scale=2)
ax=sns.heatmap(df, square=True, linewidths=.5, annot=False, fmt='.3f',
               cmap=green, vmin=0, vmax=0.05)
    

enter image description here

The problem starts when I try to plot line on top of the heatmap. The line should have the same x-axis values and the values should be in secondary y-axis. This is how the line df looks like:

>>>day     value
0  14       315.7
1  15       312.3
2  16       305.9
3  17       115.2
4  18       163.2
5  19       305.78
...

I have tried to plot it on top as explained here:

green = sns.light_palette("seagreen", reverse=True, as_cmap=True)
green.set_over('tomato')
sns.set(rc={'figure.figsize': (20.7, 10.27)})
sns.set(font_scale=2)
ax=sns.heatmap(df, square=True, linewidths=.5, annot=False, fmt='.3f',
              cmap=green, vmin=0, vmax=0.05)

ax2=plt.twinx()
ax2.plot(df_line['day'], df_line['value'],color="blue")
line = ax2.lines[0]
line.set_xdata(line.get_xdata() + 0.5)


plt.show()

but then I get the line "shifted" to the left side, I get new "rows" on the y axis (the grey ones) and is just wrong. enter image description here

How can I align the line to match to the x-axis? and also to the y-axis not to have vertical rows at all? and to fit the heatmap so the values are not "oveR" the heat map?

Upvotes: 1

Views: 1711

Answers (1)

JohanC
JohanC

Reputation: 80339

Internally, the ticks of the heatmap are categorical. Moreover, they are shifted by a half. This makes tick 14 internally having position 0.5, 15 at 1.5 etc. You can correct the lineplot by subtracting the first day and adding 0.5.

To avoid the white lines from the twin ax, set its grid off.

To avoid the grey bars at the top and bottom, use plt.subplots_adjust() with the y-dimensions of the first ax.

As there are 7 rows of cells, a trick to align the ticks of the twin ax with the borders between the cells, could be to set its y limits 7*50 separated. For example ax2.set_ylim(150, 500).

Here is some example code:

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

days = np.arange(14, 31)
hours = np.arange(9, 16)
df_data = pd.DataFrame({'day': np.tile(days, len(hours)),
                        'hour': np.repeat(hours, len(days)),
                        'value': np.random.uniform(0, 0.1, len(hours) * len(days))})
df = df_data.pivot('hour', 'day', 'value')
green = sns.light_palette("seagreen", reverse=True, as_cmap=True)
green.set_over('tomato')
sns.set(rc={'figure.figsize': (20.7, 10.27)})
sns.set(font_scale=2)
ax = sns.heatmap(df, square=True, linewidths=.5, annot=False, cmap=green, vmin=0, vmax=0.05, cbar=False)
ax.tick_params(axis='y', length=0, labelrotation=0, pad=10)

df_line = pd.DataFrame({'day': days, 'value': np.random.uniform(120, 400, len(days))})
ax_bbox = ax.get_position()
ax2 = ax.twinx()
ax2.plot(df_line['day'] - df_line['day'].min() + 0.5, df_line['value'], color="blue", lw=3)
ax2.set_ylim(100, 450)
ax2.grid(False)
plt.subplots_adjust(bottom=ax_bbox.y0, top=ax_bbox.y1)  # to shrink the height of ax2 similar to ax
plt.show()

example plot

Upvotes: 5

Related Questions