42piratas
42piratas

Reputation: 595

How to properly plot a line over bars?

This one used to work fine, but somehow it stopped working (I must have changed something mistakenly but I can't find the issue).

I'm plotting a set of 3 bars per date, plus a line that shows the accumulated value of one of them. But only one or another (either the bars or the line) is properly being plotted. If I left the code for the bars last, only the bars are plotted. If I left the code for the line last, only the line is plotted.

fig, ax = plt.subplots(figsize = (15,8))

df.groupby("date")["result"].sum().cumsum().plot(
    ax=ax,
    marker='D',
    lw=2,
    color="purple")

df.groupby("date")[selected_columns].sum().plot(
    ax=ax,
    kind="bar",
    color=["blue", "red", "gold"])

ax.legend(["LINE",  "X", "Y", "Z"])

Appreciate the help!

enter image description here

enter image description here

Upvotes: 0

Views: 162

Answers (2)

JohanC
JohanC

Reputation: 80319

Pandas draws bar plots with the x-axis as categorical, so internally numbered 0, 1, 2, ... and then setting the label. The line plot uses dates as x-axis. To combine them, both need to be categorical. The easiest way is to drop the index from the line plot. Make sure that the line plot is draw first, enabling the labels to be set correctly by the bar plot.

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

df = pd.DataFrame({'date': pd.date_range('20210101', periods=10),
                   'earnings': np.random.randint(100, 600, 10),
                   'costs': np.random.randint(0, 200, 10)})
df['result'] = df['earnings'] - df['costs']

fig, ax = plt.subplots(figsize=(15, 8))

df.groupby("date")["result"].sum().cumsum().reset_index(drop=True).plot(
    ax=ax,
    marker='D',
    lw=2,
    color="purple")
df.groupby("date")[['earnings', 'costs', 'result']].sum().plot(
    ax=ax,
    kind="bar",
    rot=0,
    width=0.8,
    color=["blue", "red", "gold"])
ax.legend(['Cumul.result', 'earnings', 'costs', 'result'])
# shorten the tick labels to only the date
ax.set_xticklabels([tick.get_text()[:10] for tick in ax.get_xticklabels()])
ax.set_ylim(ymin=0) # bar plots are nicer when bars start at zero
plt.tight_layout()
plt.show()

example plot

Upvotes: 1

Frank
Frank

Reputation: 1249

Here I post the solution:

import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
a=[11.3,222,22, 63.8,9]  
b=[0.12,-1.0,1.82,16.67,6.67]
l=[i for i in range(5)]

plt.rcParams['font.sans-serif']=['SimHei'] 
fmt='%.1f%%'
yticks = mtick.FormatStrFormatter(fmt)

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(l, b,'og-',label=u'A')
ax1.yaxis.set_major_formatter(yticks)
for i,(_x,_y) in enumerate(zip(l,b)):
    plt.text(_x,_y,b[i],color='black',fontsize=8,)
ax1.legend(loc=1)
ax1.set_ylim([-20, 30])
ax1.set_ylabel('ylabel')
plt.legend(prop={'family':'SimHei','size':8})
ax2 = ax1.twinx() 
plt.bar(l,a,alpha=0.1,color='blue',label=u'label')
ax2.legend(loc=2)
plt.legend(prop={'family':'SimHei','size':8},loc="upper left")
plt.show()

enter image description here

The key to this is the command

ax2 = ax1.twinx() 

Upvotes: 0

Related Questions