adin
adin

Reputation: 833

Display label stacked barh with values from dataframe

How can I display values for my stacked barh chart that come from a dataframe? How can I place the labels above their respective sections on each bar and modify the font so that it shows up well as a gray scale graphic?

It is related to this question but it has a list of values rather than two lists pulled from a pandas dataframe. If it were a singe list, I think I could pull values from a single record in the dataframe but with two lists, I'm not sure how to apply that to each bar in the bar graph.

My dataframe:

Delin.  Group1  Group2  Group3  Group4  Group5
Census  0.2829  0.3387  0.2636  0.0795  0.0353
USPS    0.2538  0.3143  0.2901  0.1052  0.0366

My code:

import os
import pandas as pd
import time
#
start_time   = time.time()
#
output_dir   = r"C:\Some\Directory\For\Ouputs"
#
output_fig   = "race_barh2.png"
#
fig_path     = os.path.join(output_dir, output_fig)
#
os.chdir(output_dir)
#
input_csv    = r"C:\Some\Directory\To\My.csv"
#
df           = pd.read_csv(input_csv, delimiter = ",")
#
ax           = df.plot.barh( stacked = True, color = ("#252525", "#636363", "#969696", "#cccccc", "#f7f7f7"), edgecolor = "black", linewidth = 1)
#
ax.set_xlabel("Percentage of Total",  fontsize = 18)
#
ax.set_ylabel("Boundary Delineation", fontsize = 18)
#
ax.set_yticklabels(["Census", "USPS"])
#
ax.set_xticklabels(["0%", "20%", "40%", "60%", "80%", "100%"])
#
horiz_offset = 1.03
#
vert_offset  = 1
#
ax.legend(bbox_to_anchor=(horiz_offset, vert_offset))
#
fig          = ax.get_figure()
#
fig.savefig(fig_path, bbox_inches = "tight", dpi = 600)
#
#
#
end_time     = round( time.time() - start_time, 5 )
#
print "Seconds elapsed: {0}".format(end_time)

enter image description here

Upvotes: 2

Views: 2231

Answers (1)

Chris
Chris

Reputation: 1357

You can do this similarly as in the referenced question, by annotating the bars. For a stacked bar chart you'll have to tweak the position of the labels a little to get them where you want. You can play around with the horizontalalignment, verticalalignment and adding a bit of a margin as I did (+.5).

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

#used gray colormap, you can use your own colors by replacing colormap='gray' with color=colors
colors = ["#252525", "#636363", "#969696", "#cccccc", "#f7f7f7"]
plt.rcParams['axes.prop_cycle'] = cycler(color=colors)

#dummy data
df = pd.DataFrame(np.random.randint(5, 8, (10, 3)), columns=['Group1', 'Group2', 'Group3'])

for col in df.columns.tolist():
    df[col] = df[col].apply(lambda x:x*100 / df[col].sum())

ax = df.T.plot.barh(stacked=True, colormap='gray', edgecolor='black', linewidth=1)

for lbl in ax.patches:
    ax.annotate("{:.0f}%".format(int(lbl.get_width())), (lbl.get_x(), lbl.get_y()+.5), verticalalignment='bottom', horizontalalignment='top', fontsize=8, color='black')

ax.legend(loc='center left', bbox_to_anchor=(1.0, .5))
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)

plt.show()

enter image description here

Upvotes: 3

Related Questions