Eli Turasky
Eli Turasky

Reputation: 1061

Adding line plot using second axis to bar plot using custom dates as x ticks matplotlib

I have a stacked bar plot that is generated like this:

fig, ax1 = plt.subplots(figsize=(16,12))

bars_old= np.add(old_data['green'], old_data['orange']).tolist()
bars_new = np.add(df_['Green'], df_['Orange']).tolist()

today = pd.to_datetime(dt.date.today())
edge_colors = np.where(date_list==(today.strftime('%d %b')), 'black', 'white')
line_width= np.where(date_list==(today.strftime('%d %b')), 4, 0.5)


ax1.bar(old_dates, old_data['green'], color='green', zorder=5, linewidth=0.5, edgecolor='white')
ax1.bar(old_dates, old_data['orange'], color='orange', bottom=old_data['green'], zorder=5, linewidth=0.5, edgecolor='white')
ax1.bar(old_dates, old_data['red'], color='red', bottom=bars_old, zorder=5, linewidth=0.5, edgecolor='white')


ax1.bar(date_list, df_['Green'], color='green', zorder=5, linewidth=line_width, edgecolor=edge_colors)
ax1.bar(date_list, df_['Orange'], color='orange', bottom=df_['Green'], linewidth=line_width, zorder=5, edgecolor=edge_colors)
ax1.bar(date_list, df_['Red'], color='red', bottom=bars_new, zorder=5, linewidth=line_width, edgecolor=edge_colors)
plt.ylim(0,678)

The actual data in this case is not important, but rather the dates that are provided for the x-axis.

old_dates

Index(['01 Jul', '02 Jul', '03 Jul', '04 Jul', '05 Jul', '06 Jul', '07 Jul',
       '08 Jul'],
      dtype='object')

date_list

Index(['09 Jul', '10 Jul', '11 Jul', '12 Jul', '13 Jul', '14 Jul', '15 Jul',
       '16 Jul', '17 Jul', '18 Jul', '19 Jul', '20 Jul', '21 Jul', '22 Jul'],
      dtype='object')

As you can see, the dates are from 01 July - 22 July and are fully connected, and the plot displays what I want. However, now I try to add a line plot on a second axis like this:

ax2 = ax1.twinx()
ax2.plot(delta_dates, deltas, 'r', marker='o',markersize=15, lw=5)
plt.ylim(-2.5, 2.5)

delta_dates

Index(['09 Jul', '10 Jul', '11 Jul', '12 Jul', '13 Jul', '14 Jul', '15 Jul',
       '16 Jul', '17 Jul', '18 Jul', '19 Jul', '20 Jul', '21 Jul'],
      dtype='object')

print (np.shape(deltas))
(13,)

As you can see, the deltas and the delta_dates are the same size. However, when I try to add this to my existing bar plot, the line is plotted at the right dates, but it shifts the older dates to the right and leaves off the xlabels. What exactly is going on here? Pictures provided for more clarity.

image1 image2

Upvotes: 1

Views: 102

Answers (1)

Tom
Tom

Reputation: 8800

The problem is that you are plotting strings on the x-axis, not dates. You can see that the dtype of your dates are object.

If you create a similar plot instead using datetime-like data on the x-axis, you get the intended result. You just have to use matplotlib.dates to format your axes:

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

old_dates = pd.date_range('07-01-2020','07-12-2020',freq='1D')
date_list = pd.date_range('07-13-2020','07-23-2020',freq='1D')

fig, ax = plt.subplots()

ax.bar(old_dates, np.random.rand(len(old_dates)))
ax.bar(date_list, np.random.rand(len(date_list)))

ax2 = ax.twinx()
ax2.plot(date_list, np.random.rand(len(date_list)))

enter image description here

But if you instead convert the times to str, matplotlib basically creates a numeric position for each string, and it is not smart enough to (or it intentionally doesn't) assign them an order / identity in the way you are trying to do. So when you create the twin axes, it is basically resetting the position for ax2 and you get the problem you are seeing:

str1 = pd.Series(dr1).astype(str)
str2 = pd.Series(dr2).astype(str)

fig, ax = plt.subplots()

ax.bar(str1, np.random.rand(len(str1)))
ax.bar(str2, np.random.rand(len(str2)))

ax2 = ax.twinx()
ax2.plot(str2, np.random.rand(len(str2)))

enter image description here

The ticks are the full timestamp names in this example (causing more clutter), but the problem would be the same if they were fixed to the same "MONTH ##" format. I wrote in another post how it doesn't matter that the strings "look like" datetime information; they are still strings and the information they refer to cannot be plotted in order.

Upvotes: 3

Related Questions