python - Ordering of elements in Pandas stacked bar chart
问题描述
I'm trying to graph information about the portion of a household's income earned in a specific industry across 5 districts in a region.
I used groupby to sort the information in my data frame by district:
df = df_orig.groupby('District')['Portion of income'].value_counts(dropna=False)
df = df.groupby('District').transform(lambda x: 100*x/sum(x))
df = df.drop(labels=math.nan, level=1)
ax = df.unstack().plot.bar(stacked=True, rot=0)
ax.set_ylim(ymax=100)
display(df.head())
District Portion of income
A <25% 12.121212
25 - 50% 9.090909
50 - 75% 7.070707
75 - 100% 2.020202
Since this income falls into categories, I would like to order the elements in the stacked bar in a logical way. The graph Pandas produced is below. Right now, the ordering (starting from the bottom of each bar) is:
- 25 - 50%
- 50 - 75%
- 75 - 100%
- <25%
- Unsure
I realize that these are sorted in alphabetical order and was curious if there was a way to set a custom ordering. To be intuitive, I would like the order to be (again, starting from the bottom of the bar):
- Unsure
- <25%
- 25 - 50%
- 50 - 75%
- 75 - 100%
Then, I would like to flip the legend to display the reverse of this order (ie, I would like the legend to have 75 - 100 at the top, as that is what will be at the top of the bars).
解决方案
要对收入类别强加自定义排序顺序,一种方法是将它们转换为CategoricalIndex
.
要反转 matplotlib 图例条目的顺序,请使用get_legend_handles_labels
此 SO 问题中的方法:Reverse legend order pandas plot
import pandas as pd
import numpy as np
import math
np.random.seed(2019)
# Hard-code the custom ordering of categories
categories = ['unsure', '<25%', '25 - 50%', '50 - 75%', '75 - 100%']
# Generate some example data
# I'm not sure if this matches your input exactly
df_orig = pd.DataFrame({'District': pd.np.random.choice(list('ABCDE'), size=100),
'Portion of income': np.random.choice(categories + [np.nan], size=100)})
# Unchanged from your code. Note that value_counts() returns a
# Series, but you name it df
df = df_orig.groupby('District')['Portion of income'].value_counts(dropna=False)
df = df.groupby('District').transform(lambda x: 100*x/sum(x))
# In my example data, np.nan was cast to the string 'nan', so
# I have to drop it like this
df = df.drop(labels='nan', level=1)
# Instead of plotting right away, unstack the MultiIndex
# into columns, then convert those columns to a CategoricalIndex
# with custom sort order
df = df.unstack()
df.columns = pd.CategoricalIndex(df.columns.values,
ordered=True,
categories=categories)
# Sort the columns (axis=1) by the new categorical ordering
df = df.sort_index(axis=1)
# Plot
ax = df.plot.bar(stacked=True, rot=0)
ax.set_ylim(ymax=100)
# Matplotlib idiom to reverse legend entries
handles, labels = ax.get_legend_handles_labels()
ax.legend(reversed(handles), reversed(labels))
推荐阅读
- android - 我正在创建浏览器应用程序,但当我单击任何 URL 时,Android 不会在选择器中检测到我的应用程序
- python - pandas isin 需要可散列的数据?
- angular - 如何使用 Angular 8 上传图片
- java - 滚动时无法隐藏 TabLayout(帮助!)
- java - 线程有自己的数据副本吗?
- javascript - 使用 discord.js 创建角色
- nlp - 使用带 Torchtext 的 annoy 进行最近邻搜索
- podfile - pod 'MaterialComponents/TextFields' 安装错误
- c - 引用指针时出现意外值
- qt - 嵌套命名空间中的 QObject 类