IamWarmduscher
IamWarmduscher

Reputation: 965

How do I add multiple bar graphs from a pandas dataframe on one plot in matplotlib?

I have two different dataframes:

df_test1 = pd.DataFrame(
    [['<18', 80841], ['18-24', 334725], ['25-44', 698261], ['45-64', 273087], ['65+', 15035]],
    columns = ['age_group', 'total_arrests']
)

enter image description here

df_test2 = pd.DataFrame(
    [['<18', 33979], ['18-24', 106857], ['25-44', 219324], ['45-64', 80647], ['65+', 4211]],
    columns = ['age_group','total_arrests']
)

enter image description here

I created the following plot using matplotlib:

fig, ax = plt.subplots()

ax.bar(df_test1.age_group, df_test1.total_arrests, color = 'seagreen')
ax.bar(df_test2.age_group, df_test2.total_arrests, color = 'lightgreen')
ax.set_xlabel('Age Group')
ax.set_ylabel('Number of Arrests')
ax.set_title('Arrests vs. Felony Arrests by Age Group')
plt.xticks(rotation=0)
plt.legend(['All Arressts', 'Felony Arrests'])

ax.yaxis.set_major_formatter(
ticker.FuncFormatter(lambda y,p: format(int(y), ','))
)

for i,j in zip(df_test1.age_group, df_test1.total_arrests):
    ax.annotate(format(j, ','), xy=(i,j))

for i,j in zip(df_test2.age_group, df_test2.total_arrests):
    ax.annotate(format(j, ','), xy=(i,j))

plt.show()

enter image description here

I was expecting 2 separate bars, one for each dataframe column, df_test1.total_arrests and df_test2.total_arrests but instead I got a stacked bar chart. How can I get a chart with bars next to one another similar to the chart here Matplotlib plot multiple bars in one graph ? I tried adjusting my code to the one in that example but I couldn't get it.

Upvotes: 0

Views: 176

Answers (1)

Diziet Asahi
Diziet Asahi

Reputation: 40747

With only two bars, it's fairly easy. The solution is to align the bars on the "edge" of the tick, one bar is aligned to the left, the other to the right.

Repeat the same logic for proper alignment of the annotations. Half of them are left-aligned, the others are right-aligned

fig, ax = plt.subplots()

ax.bar(df_test1.age_group, df_test1.total_arrests, color = 'seagreen', width=0.4, align='edge')
ax.bar(df_test2.age_group, df_test2.total_arrests, color = 'lightgreen', width=-0.4, align='edge')
ax.set_xlabel('Age Group')
ax.set_ylabel('Number of Arrests')
ax.set_title('Arrests vs. Felony Arrests by Age Group')
plt.xticks(rotation=0)
plt.legend(['All Arressts', 'Felony Arrests'])
ax.yaxis.set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda y,p: format(int(y), ','))
)

for i,j in zip(df_test1.age_group, df_test1.total_arrests):
    ax.annotate(format(j, ','), xy=(i,j))

for i,j in zip(df_test2.age_group, df_test2.total_arrests):
    ax.annotate(format(j, ','), xy=(i,j), ha='right')

plt.show()

enter image description here

If you have more than 2 bars, then the situation is more complicated (see the code that you linked above). You'll have an easier time using seaborn, but you have to transform your dataframe a bit:

df = pd.merge(left=df_test1, right=df_test2, on='age_group')

df.columns=['age_group','all_arrests', 'felonies']
df = df.melt(id_vars=['age_group'], var_name='Type', value_name='Number')

fig, ax = plt.subplots()
sns.barplot(y='Number',x='age_group',hue='Type', data=df, hue_order=['felonies','all_arrests'])

enter image description here

Upvotes: 1

Related Questions