如何在matplotlib中根据不同的组绘制直方图?

6

我有一个类似这样的表格:

value    type
10       0
12       1
13       1
14       2

生成虚拟数据:

import numpy as np

value = np.random.randint(1, 20, 10)
type = np.random.choice([0, 1, 2], 10)

我想在Python 3中使用matplotlib(v1.4)完成一个任务:

  • 绘制value的直方图
  • type分组,即使用不同颜色区分类型
  • "bars"的位置应该是"dodge",即并排
  • 由于value的范围很小,所以我会使用identity作为bins,即bin的宽度为1

问题:

  • 如何根据type的值给条形图分配颜色,并从colormap中绘制颜色(例如Accent或matplotlib中的其他cmap)?我不想使用命名颜色(即'b','k','r'
  • 我的直方图中的条形图重叠在一起,如何"躲避"条形图?

注意事项

  1. 我已经尝试了Seaborn、matplotlib和pandas.plot两个小时,但无法得到所需的直方图。
  2. 我阅读了matplotlib的示例和用户指南。令人惊讶的是,我没有找到有关如何从colormap中分配颜色的教程。
  3. 我在Google上搜索失败了,无法找到简洁的示例。
  4. 我猜想可以使用matplotlib.pyplot完成任务,而不需要导入许多模块,比如matplotlib.cmmatplotlib.colors
2个回答

8

对于您的第一个问题,我们可以创建一个等于1的虚拟列,然后通过按值和类型分组来生成计数。

对于您的第二个问题,您可以直接将颜色图传递给plot,使用colormap参数:

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import seaborn
seaborn.set() #make the plots look pretty

df = pd.DataFrame({'value': value, 'type': type})
df['dummy'] = 1
ag = df.groupby(['value','type']).sum().unstack()
ag.columns = ag.columns.droplevel()

ag.plot(kind = 'bar', colormap = cm.Accent, width = 1)
plt.show()

enter image description here


谢谢。我可以使用“hist”来获得相同的结果,而不需要通过枢轴计数吗? - Zelong
嗯,我不确定你如何使用hist来实现这一点,因为我只用过hist来绘制单个系列。 - maxymoo

0

每当您需要按另一个变量(使用颜色)分组绘制变量时,seaborn通常比matplotlib或pandas提供更方便的方法。因此,这里是使用seaborn histplot函数的解决方案:

import numpy as np                 # v 1.19.2
import pandas as pd                # v 1.1.3
import matplotlib.pyplot as plt    # v 3.3.2
import seaborn as sns              # v 0.11.0

# Set parameters for random data
rng = np.random.default_rng(seed=1) # random number generator
size = 50
xmin = 1
xmax = 20

# Create random dataframe
df = pd.DataFrame(dict(value = rng.integers(xmin, xmax, size=size),
                       val_type = rng.choice([0, 1, 2], size=size)))

# Create histogram with discrete bins (bin width is 1), colored by type
fig, ax = plt.subplots(figsize=(10,4))
sns.histplot(data=df, x='value', hue='val_type', multiple='dodge', discrete=True,
             edgecolor='white', palette=plt.cm.Accent, alpha=1)

# Create x ticks covering the range of all integer values of df['value']
ax.set_xticks(np.arange(df['value'].min(), df['value'].max()+1))

# Additional formatting
sns.despine()
ax.get_legend().set_frame_on(False)

plt.show()

histogram_grouped

正如您所注意到的,这是一个直方图而不是条形图,除了在数据集中不存在x轴值的情况下(例如12和14),柱之间没有空格。

考虑到接受的答案提供了一个pandas中的条形图,并且在某些情况下,条形图可能是显示直方图的相关选择,因此这里介绍如何使用seaborn的countplot函数创建一个条形图:

# For some reason the palette argument in countplot is not processed the
# same way as in histplot so here I fetch the colors from the previous
# example to make it easier to compare them
colors = [c for c in set([patch.get_facecolor() for patch in ax.patches])]

# Create bar chart of counts of each value grouped by type
fig, ax = plt.subplots(figsize=(10,4))
sns.countplot(data=df, x='value', hue='val_type', palette=colors,
              saturation=1, edgecolor='white')

# Additional formatting
sns.despine()
ax.get_legend().set_frame_on(False)

plt.show()

countplot_grouped

由于这是一个条形图,因此不包括值12和14,这会产生一种有点欺骗性的图表,因为没有显示这些值的空白空间。另一方面,每组条形之间有一些空间,这使得更容易看出每个条形所属的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接