最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How do I plot a line over a bar chart with pandasdate index? - Stack Overflow

programmeradmin0浏览0评论

I've got a dataframe I'd like to represent as a stacked bar chart, and a series that I'd like to represent as a line (but with a different scale). I'd like to draw these as a line chart (for the series) drawn on top of a bar chart (for the dataframe) with a shared x-axis (being the time indices).

Here's a toy example I tried:

import pandas as pd
import numpy as np

idx = pd.date_range(start='2000-01-01', periods=20)
df = pd.DataFrame(np.random.rand(20,3), index=idx)
ax = df.plot.bar(stacked=True)

series = pd.Series(np.random.rand(20), index=idx)
series.plot(ax=ax, secondary_y=True)

However, when I run this I get the following:

It's as if the line chart has overwritten the bar chart somehow. Interestingly, when I remove the index from the dataframe and series (making it just default integers), it seems to work:

What am I doing wrong? I'm using pandas 2.2.2

I've got a dataframe I'd like to represent as a stacked bar chart, and a series that I'd like to represent as a line (but with a different scale). I'd like to draw these as a line chart (for the series) drawn on top of a bar chart (for the dataframe) with a shared x-axis (being the time indices).

Here's a toy example I tried:

import pandas as pd
import numpy as np

idx = pd.date_range(start='2000-01-01', periods=20)
df = pd.DataFrame(np.random.rand(20,3), index=idx)
ax = df.plot.bar(stacked=True)

series = pd.Series(np.random.rand(20), index=idx)
series.plot(ax=ax, secondary_y=True)

However, when I run this I get the following:

It's as if the line chart has overwritten the bar chart somehow. Interestingly, when I remove the index from the dataframe and series (making it just default integers), it seems to work:

What am I doing wrong? I'm using pandas 2.2.2

Share Improve this question asked Nov 19, 2024 at 23:40 quantquant 23.2k36 gold badges131 silver badges222 bronze badges 2
  • If you want to know reason, plot the two graphs separately and compare the x-ticks. In the case of matplotlib, the line and bar graphs often do not merge because the x-ticks are set differently even though the data is the same. – Panda Kim Commented Nov 19, 2024 at 23:43
  • One possible solution is to draw graphs with reset_index as you did, and manually assign xtick and xtick-label to each graph. Of course, this is cumbersome for dates. Hopefully there is a better solution, but if nothing else, this is the way to go. I'll also provide the code for the method I've suggested if there's no other answer when I see this later. – Panda Kim Commented Nov 19, 2024 at 23:51
Add a comment  | 

1 Answer 1

Reset to default 1

To align both charts you can convert your index to strings, using DatetimeIndex.strftime:

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

# `default_rng` now preferred random constructor
rng = np.random.default_rng(0)

# using `DatetimeIndex.strftime` to convert to strings
idx = pd.date_range(start='2000-01-01', periods=20).strftime('%Y-%m-%d')
df = pd.DataFrame(rng.random((20, 3)), index=idx)

# adding `alpha` to increase contrast example
ax = df.plot.bar(stacked=True, alpha=0.8)

# series = pd.Series(rng.random(20), index=idx)
# using `df.sum` to better showcase alignment data
series = df.sum(axis=1)

# adding `color` for contrast
series.plot(ax=ax, secondary_y=True, color='black')

# rotation for readability
ax.tick_params(axis='x', rotation=90)

plt.tight_layout()
plt.show()

Output:

The underlying issue here is that df.plot.bar interprets idx categorically, while df.plot.line treats it as a continuous datetime x-axis. The conversion to strings aligns them.

This would also work:

idx = pd.date_range(start='2000-01-01', periods=20)
df = pd.DataFrame(rng.random((20, 3)), index=idx)

# map `series` by *position* to align with categorical x-axis of bar
series = df.sum(axis=1).set_axis(range(len(df)))

Though, as you can see from the result, you would still want strftime for readability:

Output 2:


Aside: instead of ax.tick_params, consider improving readability with a step for the labels, combining ax.set_xticks and ax.set_xticklabels.

# ax.tick_params(axis='x', rotation=90)
# rest as above

# use a step for the labels
step = 3
ticks = list(range(0, len(idx), step))
tick_labels = idx[ticks]

ax.set_xticks(ticks)
ax.set_xticklabels(tick_labels, rotation=45)

plt.tight_layout()
plt.show()

Output:

发布评论

评论列表(0)

  1. 暂无评论