Reputation: 1061
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.
Upvotes: 1
Views: 102
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)))
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)))
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