Matplotlib Advanced Bar Plot

matplotlib advanced bar plot

I gotta run, but here's something to get you started:

import numpy as np
import matplotlib
matplotlib.rcParams['text.usetex'] = False
import matplotlib.pyplot as plt
import pandas

df = pandas.DataFrame(np.random.uniform(size=37)*100, columns=['A'])
threshold = 75
fig, ax = plt.subplots(figsize=(8,3))

good = df['A'][df['A'] >= threshold]
bad = df['A'][df['A'] < threshold]

ax.bar(left=good.index, height=good, align='center', color='ForestGreen', zorder=5)
ax.bar(left=bad.index, height=bad, align='center', color='Firebrick', zorder=5)

ax.axhline(y=threshold, linewidth=2, color='ForestGreen', zorder=0)

ax.set_xticks(df.index)
ax.set_xlim(left=df.index[0]-0.75, right=df.index[-1]+0.75)

def annotateBars(row, ax=ax):
if row['A'] < 20:
color = 'black'
vertalign = 'bottom'
vertpad = 2
else:
color = 'white'
vertalign = 'top'
vertpad = -2

ax.text(row.name, row['A'] + vertpad, "{:.1f}%".format(row['A']),
zorder=10, rotation=90, color=color,
horizontalalignment='center',
verticalalignment=vertalign,
fontsize=8, weight='heavy')

junk = df.apply(annotateBars, ax=ax, axis=1)

And that gives me:
annotated bar plot

How to plot a barplot with different hatch and edge color with legends using matplotlib?

Your can combine the line1 and line2 with a tuple in the handle lists.

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
# draw hatch
line1 = ax.bar(range(1, 5), range(1, 5), color='none', edgecolor='r', hatch="/", lw=1., zorder = 0)
# draw edge
line2 = ax.bar(range(1, 5), range(1, 5), color='none', edgecolor='k', zorder=1, lw=2.)

ax.set_xticks([1.5, 2.5, 3.5, 4.5])
ax.legend([(line1, line2)],['hatch'])
plt.show()

Sample Image

You can see the doc.

Single Stacked Bar Chart Matplotlib

First transpose one column DataFrame:

df.T.plot.barh(stacked=True, legend=False)

If 2 or more columns:

df[['Percentage']].T.plot.barh(stacked=True, legend=False)

How to create grouped bar plots in a single figure from a wide dataframe

  • This can be done with seaborn.barplot, or with just using pandas.DataFrame.plot, which avoids the additional import.
  • Annotate as shown in How to plot and annotate a grouped bar chart
    • Add annotations with .bar_label, which is available with matplotlib 3.4.2.
    • The link also shows how to add annotations if using a previous version of matplotlib.
  • Using pandas 1.3.0, matplotlib 3.4.2, and seaborn 0.11.1

With pandas.DataFrame.plot

  • This option requires setting x='name', or res1 and res2 as the index.
import pandas as pd

test_df = pd.DataFrame({'name': ['a', 'b', 'c'], 'res1': [1,2,3], 'res2': [4,5,6]})

# display(test_df)
name res1 res2
0 a 1 4
1 b 2 5
2 c 3 6

# plot with 'name' as the x-axis
p1 = test_df.plot(kind='bar', x='name', rot=0)

# annotate each group of bars
for p in p1.containers:
p1.bar_label(p, fmt='%.1f', label_type='edge')

Sample Image

import pandas as pd

test_df = pd.DataFrame({'name': ['a', 'b', 'c'], 'res1': [1,2,3], 'res2': [4,5,6]})

# set name as the index and then Transpose the dataframe
test_df = test_df.set_index('name').T

# display(test_df)
name a b c
res1 1 2 3
res2 4 5 6

# plot and annotate
p1 = test_df.plot(kind='bar', rot=0)

for p in p1.containers:
p1.bar_label(p, fmt='%.1f', label_type='edge')

Sample Image

With seaborn.barplot

  • Convert the dataframe from a wide to long format with pandas.DataFrame.melt, and then use the hue parameter.
import pandas as pd
import seaborn as sns

test_df = pd.DataFrame({'name': ['a', 'b', 'c'], 'res1': [1,2,3], 'res2': [4,5,6]})

# melt the dataframe into a long form
test_df = test_df.melt(id_vars='name')

# display(test_df.head())
name variable value
0 a res1 1
1 b res1 2
2 c res1 3
3 a res2 4
4 b res2 5

# plot the barplot using hue; switch the columns assigned to x and hue if you want a, b, and c on the x-axis.
p1 = sns.barplot(data=test_df, x='variable', y='value', hue='name')

# add annotations
for p in p1.containers:
p1.bar_label(p, fmt='%.1f', label_type='edge')
  • With x='variable', hue='name'

Sample Image

  • With x='name', hue='variable'

Sample Image

plotting multiple stacked bar chart

IIUC, try:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 5, figsize=(20, 8))
iax = iter(ax.flatten())
for n, g in df.groupby('Business Unit'):
g.loc[n, :].plot.bar(ax=next(iax), title=f'{n}', stacked=True)

Sample Image

How do I plot a categorical bar chart with different classes for each category in Matplotlib?

Supposing the data resides in a dataframe, the bars can be generated by looping through the categories:

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

# first create some test data, similar in structure to the question's
categories = ['Subject', 'Illumination', 'Location', 'Daytime']
df = pd.DataFrame(columns=['Category', 'Class', 'Value'])
for cat in categories:
for _ in range(np.random.randint(2, 7)):
df = df.append({'Category': cat,
'Class': "".join(np.random.choice([*'tuvwxyz'], 10)),
'Value': np.random.uniform(10, 17)}, ignore_index=True)

fig, ax = plt.subplots()
start = 0 # position for first label
gap = 1 # gap between labels
labels = [] # list for all the labels
label_pos = np.array([]) # list for all the label positions

# loop through the categories of the dataframe
# provide a list of colors (at least as long as the expected number of categories)
for (cat, df_cat), color in zip(df.groupby('Category', sort=False), ['navy', 'orange'] * len(df)):
num_in_cat = len(df_cat)
# add a text for the category, using "axes coordinates" for the y-axis
ax.text(start + num_in_cat / 2, 0.95, cat, ha='center', va='top', transform=ax.get_xaxis_transform())
# positions for the labels of the current category
this_label_pos = np.arange(start, start + num_in_cat)
# create bars at the desired positions
ax.bar(this_label_pos, df_cat['Value'], color=color)
# store labels and their positions
labels += df_cat['Class'].to_list()
label_pos = np.append(label_pos, this_label_pos)
start += num_in_cat + gap
# set the positions for the labels
ax.set_xticks(label_pos)
# set the labels
ax.set_xticklabels(labels, rotation=30)
# optionally set a new lower position for the y-axis
ax.set_ylim(ymin=9)
# optionally reduce the margin left and right
ax.margins(x=0.01)
plt.tight_layout()
plt.show()

bar plot with different categories

Unexpected behavior from matplotlib using bar plots for floating values

You need to adjust the width of the bars (by default, the width is set to 0.8; with values as small as yours you can see why this becomes a problem).

Your data plotted with width set to 0.01 (plt.bar(a,b, width = 0.01)) looks like:

Sample Image

Both issues you mention are due to this width parameter. The bars in barplot are centered around their x-values, thus 0.1 is the center of the first bar (with witdh of 0.8, it spans from -0.3 to 0.5). Also, since this first bar is the tallest, it covers anything within that range (so all except the last bar are at least partially covered, due to overlap).

How to make bar plot of a list in Python

I believe you want something like this:

ax = sns.barplot(x=np.arange(len(lii)), y=lii)
ax.bar_label(ax.containers[0])
plt.axis('off')
plt.show()

Bar plot



Related Topics



Leave a reply



Submit