Overlaying Pandas plot with Matplotlib is sensitive to the plotting order

Viewed 43

I have the following problem: I'm trying to overlay two plots: One Pandas plot via plot.area() for a dataframe, and a second plot that is a standard Matplotlib plot. Depending the coder order for those two, the Matplotlib plot is displayed only if the code is before the Pandas plot.area() on the same axes.

Example: I have a Pandas dataframe called revenue that has a DateTimeIndex, and a single column with "revenue" values (float). Separately I have a dataset called projection with data along the same index (revenue.index)

If the code looks like this:

import pandas as pd
import matplotlib.pyplot as plt  

fig, ax = plt.subplots(figsize=(10, 6))
# First -- Pandas area plot
revenue.plot.area(ax = ax)
# Second -- Matplotlib line plot 
ax.plot(revenue.index, projection, color='black', linewidth=3)
plt.tight_layout()
plt.show()

Then the only thing displayed is the pandas plot.area() like this:

1/ Pandas plot.area() and 2/ Matplotlib line plot

However, if the order of the plotting is reversed:

fig, ax = plt.subplots(figsize=(10, 6))
# First -- Matplotlib line plot 
ax.plot(revenue.index, projection, color='black', linewidth=3)
# Second -- Pandas area plot
revenue.plot.area(ax = ax)
plt.tight_layout()
plt.show()

Then the plots are overlayed properly, like this:

1/ Matplotlib line plot and 2/ Pandas plot.area()

Can someone please explain me what I'm doing wrong / what do I need to do to make the code more robust ? Kind TIA.

1 Answers

The values on the x-axis are different in both plots. I think DataFrame.plot.area() formats the DateTimeIndex in a pretty way, which is not compatible with pyplot.plot().

If you plot of the projection first, plot.area() can still plot the data and does not format the x-axis.

Mixing the two seems tricky to me, so I would either use pyplot or Dataframe.plot for both the area and the line:

import pandas as pd
from matplotlib import pyplot as plt

projection = [1000, 2000, 3000, 4000]

datetime_series = pd.to_datetime(["2021-12","2022-01", "2022-02", "2022-03"])
datetime_index = pd.DatetimeIndex(datetime_series.values)

revenue = pd.DataFrame({"value": [1200, 2200, 2800, 4100]})
revenue = revenue.set_index(datetime_index)

fig, ax = plt.subplots(1, 2, figsize=(10, 4))

# Option 1: only pyplot
ax[0].fill_between(revenue.index, revenue.value)
ax[0].plot(revenue.index, projection, color='black', linewidth=3)
ax[0].set_title("Pyplot")

# Option 2: only DataFrame.plot
revenue["projection"] = projection

revenue.plot.area(y='value', ax=ax[1])
revenue.plot.line(y='projection', ax=ax[1], color='black', linewidth=3)
ax[1].set_title("DataFrame.plot")

The results then look like this, where DataFrame.plot gives a much cleaner looking result: area plots

If you do not want the projection in the revenue DataFrame, you can put it in a separate DataFrame and set the index to match revenue:

projection_df = pd.DataFrame({"projection": projection})
projection_df = projection_df.set_index(datetime_index)
projection_df.plot.line(ax=ax[1], color='black', linewidth=3)
Related