1

I am generating a subplot with two separate charts: one using a barplot in Matplotlib and a heatmap in Seaborn. I am running into issues where the x-axis on the barplot does not align with the Seaborn x-axis, as shown in the following image:

enter image description here

Further reading here shows that the barplot axes in Matploblib are categorical [0,1,2,x], so my code below accounted for this. I use a variable with the stored result of Numpy arange over the total number of plot points (variable objects_30 in the code below), which is fed into both x-axes for the Matplotlib barplot and Seaborn heatmap graphs.

Any suggestions on how to get the Matplotlib x-axis to distribute identically to the Seaborn x-axis?

Plot Code Snippet:

objects_30pos = np.arange(len(objects_30))
ymax_pos = np.arange(len(objects_30))

heatmap_blnd = pd.DataFrame({'ENS MEMBER':'S1', 'highs':temps, 'FORECAST PERIOD':objects_30pos})
hmap_data = pd.pivot_table(heatmap_blnd, values='highs', index=['ENS MEMBER'], columns=['FORECAST PERIOD'])

run = (time + timedelta(hours=1)).strftime('%-H AM %a. %d %b. %Y')

fig, ax = plt.subplots(figsize=(14,8), sharex=True, sharey=True)
gs = gridspec.GridSpec(2, 1, height_ratios=[11, 1]) 
bar_width = 0.40

min_high = min(temps)
max_high = max(temps)
max_highhght = max_high + 5
min_highhght = min_high - 5
highheight = [x - min_highhght for x in temps]

#plt.subplot(2, 1, 1)
plt.subplot(gs[0])
rectstop = plt.bar(ymax_pos, height=highheight, width=0.60, bottom=min_highhght, color='#1e90ff', edgecolor='black', linewidth=2, zorder=3)
for rect in rectstop:
    y_value = rect.get_height()+min_highhght
    x_value = rect.get_x() + rect.get_width() / 2
    space = 2
    va = 'bottom'
    label = y_value
    plttxt = plt.annotate(label, (x_value, y_value), xytext=(0, space), textcoords="offset points", ha='center', va=va)
    plttxt.set_fontsize(12)
    plttxt.set_weight('semibold')
plt.xticks(ymax_pos, objects_30, fontsize=12)
plt.yticks(fontsize=12)
plt.yticks(fontsize=12)
plt.ylim(min_highhght,max_highhght)
if 100 in range(min_highhght,max_highhght):
    plt.axhline(y=100, color='red', linestyle='--')
if 32 in range(min_highhght,max_highhght):
    plt.axhline(y=32, color='blue', linestyle='--')
if 0 in range(min_highhght,max_highhght):
    plt.axhline(y=0, color='purple', linestyle='--')
plt.ylabel('Temperature [°F]', fontsize=12, fontweight='semibold')
plt.tick_params(axis='x', pad=10, length=10)
plt.grid(True, linestyle='--', zorder=0)

plt.title(r"$\bf{6\ Hour\ Temperatures\ [°F]}$" | " + name +"", loc='left', fontsize=13)
plt.title(r"$\bf{Run:}$"+ run +"", loc='right', fontsize=13)
plt.grid(True, linestyle='--', zorder=0)

#plt.subplot(2, 1, 2)
plt.subplot(gs[1])
heatmap = sns.heatmap(hmap_data, cbar=True, vmin=-50 ,vmax=120, cmap=cmap, annot=True, cbar_kws = dict(orientation='horizontal', pad=0.01, aspect=175, ticks=[-50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]), annot_kws = {'size':12, 'ha':'center'}, linewidth=1.5, linecolor='black')
heatmap.set_xlabel("",fontsize=12, fontweight='semibold')
heatmap.set_ylabel("",fontsize=10, fontweight='bold')
heatmap.xaxis.set_ticks_position('top')
heatmap.set(xticklabels=[])
#plt.xticks(rotation=0, ha='center', fontsize=12)
heatmap.tick_params(axis='x', length=10)
plt.yticks(rotation=0, ha='right', fontsize=12)
TornadoEric
  • 399
  • 3
  • 16

1 Answers1

-1

Not quite the answer you were after, but it seems to me the information in the bars and the heatmap is redundant.

Wouldn't you get the exact same information if you were to color the bars using the colormap instead of showing the heatmap below.

cmap = 'coolwarm'
N=32
vmin = -50
vmax = 120
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)
sm = matplotlib.cm.ScalarMappable(cmap=cmap, norm=norm)
temps = np.random.randint(vmin,vmax,size=(N,))
labels = ['Lbl\n{:d}'.format(i+1) for i in range(N)]

min_high = np.min(temps)
max_high = np.max(temps)
max_highhght = max_high + 5
min_highhght = min_high - 5
highheight = temps - min_highhght

fig, ax1 = plt.subplots(figsize=(14,8))

#bar plot
rectstop = ax1.bar(labels, height=highheight, width=0.60, bottom=min_highhght, 
                   color=plt.get_cmap(cmap)(norm(temps)), edgecolor='black', linewidth=2)
for x,temp in enumerate(temps):
    ax1.annotate('{:.0f}'.format(temp), xy=(x, temp), xytext=(0, 2), xycoords='data', textcoords="offset points", 
                 ha='center', va='bottom', fontsize=12, fontweight='semibold')

fig.colorbar(sm, ax=ax1, pad=0.02, aspect=100, ticks=np.arange(vmin,vmax+1,10))

enter image description here

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75
  • I don't like to downvote but the question is still a valid question for anyone wanting to share axes in subplots and your answer is not addressing that. Even if what the OP is trying to achieve doesn't seem optimal it's a valid technical question I feel. – Martin O Leary Jan 21 '22 at 10:39