I am trying to plot a line chart on top of a bar chart using Python's pandas library. The bar chart is a clustered chart from a DataFrame and looks like so when plotted by itself:
The line chart is the averages across the whole dataset:
I can successfully plot both sets of data on the same axis object if using all line charts:
But when I switch the DataFrame for the clustered bar to actually using a bar chart and plot it along with the line chart, the line chart wants to plot from the second index position, leading to an offset.
The answer to this question has an interesting comment about the way matplotlib treats the x-axis for both bar and line charts, which may be relevant here, but I can't work out what to do with that insight.
What's a good way to fix this alignment issue?
Code to reproduce:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
table_data = {2019: {1: np.nan,
2: np.nan,
3: np.nan,
4: 1.4200000000000002,
5: 0.8193548387096775,
6: 1.420689655172414,
7: 0.4645161290322581,
8: 0.10322580645161289,
9: 0.29333333333333333,
10: 1.7741935483870968,
11: 0.32,
12: 6.703225806451614},
2020: {1: 5.52,
2: 12.613793103448277,
3: 0.9428571428571428,
4: 0.1793103448275862,
5: 0.39354838709677414,
6: 1.3866666666666667,
7: 1.9800000000000002,
8: 0.6689655172413793,
9: 0.19333333333333336,
10: 5.896774193548388,
11: 0.6896551724137931,
12: 4.103225806451613},
2021: {1: 2.7935483870967746,
2: 5.15,
3: 9.696774193548388,
4: 3.74,
5: 2.8967741935483873,
6: 0.9103448275862069,
7: 1.6516129032258065,
8: 0.3,
9: 0.38571428571428573,
10: 5.141935483870968,
11: 8.58,
12: 6.052173913043479},
2022: {1: 2.3923076923076922,
2: 31.678571428571427,
3: 8.761290322580646,
4: np.nan,
5: np.nan,
6: np.nan,
7: np.nan,
8: np.nan,
9: np.nan,
10: np.nan,
11: np.nan,
12: np.nan}}
means = {1: 3.6137931034482755,
2: 16.435294117647057,
3: 7.132530120481928,
4: 1.797752808988764,
5: 1.3698924731182796,
6: 1.240909090909091,
7: 1.358695652173913,
8: 0.3522727272727273,
9: 0.28863636363636364,
10: 4.2709677419354835,
11: 3.2247191011235956,
12: 5.578823529411765}
df_bars = pd.DataFrame(table_data)
df_means = pd.DataFrame.from_dict(means, orient = 'index', columns = ['Mean'])
# Clustered bar chart by itself
df_bars.plot(kind = 'bar',
title = 'Average Daily Rainfall by Month',
ylabel = 'Average Daily Rainfall (mm)',
figsize = (10, 6)
)
# Line chart by itself
df_means.plot(
kind = 'line',
title = 'Average Daily Rainfall by Month',
ylabel = 'Average Daily Rainfall (mm)',
y = 'Mean'
)
# Show all data as line charts. This works OK
ax_avg = df_bars.plot(kind = 'line',
title = 'Average Daily Rainfall by Month',
ylabel = 'Average Daily Rainfall (mm)',
figsize = (10, 6)
)
df_means.plot(
ax = ax_avg,
kind = 'line',
y = 'Mean'
)
plt.show()
# Show bar data and line chart on the one plot. The line chart is offset!
ax_avg2 = df_bars.plot(kind = 'bar',
title = 'Average Daily Rainfall by Month',
ylabel = 'Average Daily Rainfall (mm)',
figsize = (10, 6)
)
df_means.plot(
ax = ax_avg2,
kind = 'line',
y = 'Mean'
)
plt.show()
You can use reset_index
to change the indexing of line dataframe back to start with zero.
This will allow your bars to line up with zero-based indexing like this:
ax_avg2 = df_bars.plot(kind = 'bar',
title = 'Average Daily Rainfall by Month',
ylabel = 'Average Daily Rainfall (mm)',
figsize = (10, 6)
)
df_means.reset_index().plot(
ax = ax_avg2,
kind = 'line',
y = 'Mean'
)
Output: