同一图上的 Python 并排箱线图
Posted
技术标签:
【中文标题】同一图上的 Python 并排箱线图【英文标题】:Python Side-by-side box plots on same figure 【发布时间】:2016-09-08 14:05:21 【问题描述】:我正在尝试在 Python 2.7 中为下面 Pandas 数据框中的 E 列中的每个分类值生成一个箱线图
A B C D E
0 0.647366 0.317832 0.875353 0.993592 1
1 0.504790 0.041806 0.113889 0.445370 2
2 0.769335 0.120647 0.749565 0.935732 3
3 0.215003 0.497402 0.795033 0.246890 1
4 0.841577 0.211128 0.248779 0.250432 1
5 0.045797 0.710889 0.257784 0.207661 4
6 0.229536 0.094308 0.464018 0.402725 3
7 0.067887 0.591637 0.949509 0.858394 2
8 0.827660 0.348025 0.507488 0.343006 3
9 0.559795 0.820231 0.461300 0.921024 1
我愿意使用 Matplotlib 或任何其他绘图库来执行此操作。到目前为止,上面的代码可以将所有类别组合在一个图上。这是生成上述数据并生成绘图的代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Data
df = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
df['E'] = [1,2,3,1,1,4,3,2,3,1]
# Boxplot
bp = ax.boxplot(df.iloc[:,:-1].values, widths=0.2)
plt.show()
在本例中,类别为 1、2、3、4。我想在同一个图中并排绘制单独的箱线图,仅用于类别 1 和 2,并在图例中显示类别名称。
有没有办法做到这一点?
其他信息:
输出应该类似于here 中的第三个图 - 将“Yes”、“No”替换为“1”、“2”。
【问题讨论】:
这个例子行不通,因为matplotlib按列绘制数据,所以会有4个箱线图,分别标记为A、B、C和D。 是的,没错。我想有 4 个类别 1 的箱形图和 4 个类别 2 的箱形图。我添加了一个指向 OP 的链接,显示其他地方正在做类似的事情。我正在寻找与此类似的情节。 【参考方案1】:从这里开始:
import numpy
import pandas
from matplotlib import pyplot
import seaborn
seaborn.set(style="ticks")
# Data
df = pandas.DataFrame(numpy.random.rand(10,4), columns=list('ABCD'))
df['E'] = [1, 2, 3, 1, 1, 4, 3, 2, 3, 1]
您有几个选择。如果单独的轴没问题,
fig, axes = pyplot.subplots(ncols=4, figsize=(12, 5), sharey=True)
df.query("E in [1, 2]").boxplot(by='E', return_type='axes', ax=axes)
如果你想要 1 个轴,我认为 seaborn 会更容易。你只需要清理你的数据。
ax = (
df.set_index('E', append=True) # set E as part of the index
.stack() # pull A - D into rows
.to_frame() # convert to a dataframe
.reset_index() # make the index into reg. columns
.rename(columns='level_2': 'quantity', 0: 'value') # rename columns
.drop('level_0', axis='columns') # drop junk columns
.pipe((seaborn.boxplot, 'data'), x='E', y='value', hue='quantity', order=[1, 2])
)
seaborn.despine(trim=True)
seaborn 很酷的一点是,稍微调整参数可以在情节布局方面取得很多成就。如果我们切换 hue
和 x
变量,我们会得到:
ax = (
df.set_index('E', append=True) # set E as part of the index
.stack() # pull A - D into rows
.to_frame() # convert to a dataframe
.reset_index() # make the index into reg. columns
.rename(columns='level_2': 'quantity', 0: 'value') # rename columns
.drop('level_0', axis='columns') # drop junk columns
.pipe((seaborn.boxplot, 'data'), x='quantity', y='value', hue='E', hue_order=[1, 2])
)
seaborn.despine(trim=True)
如果您好奇,生成的数据框如下所示:
E quantity value
0 1 A 0.935433
1 1 B 0.862290
2 1 C 0.197243
3 1 D 0.977969
4 2 A 0.675037
5 2 B 0.494440
6 2 C 0.492762
7 2 D 0.531296
8 3 A 0.119273
9 3 B 0.303639
10 3 C 0.911700
11 3 D 0.807861
【讨论】:
当我尝试df = df.set_index('E', append=True).stack().to_frame().rename(columns='level_2': 'quantity', 0: 'value').drop('level_0', axis='columns')
时,我收到错误ValueError: labels ['level_0'] not contained in axis
。我希望能够在绘制之前看到数据框。是否可以在生成箱线图之前先生成 Pandas DF?是否可以在 seaborn 中自定义晶须大小、帽子大小、传单大小/颜色等内容?
是的。只是不要将它导入 seaborn.boxplot 并单独调用它。
感谢工作。是否可以在 seaborn 中自定义诸如胡须大小、帽子大小、传单大小/颜色等内容?例如。在 matplotlib 中,我会完成 bp = ax.boxplot()
,然后在 bp['whiskers'] 中处理胡须:whisker.set(lw=0.5,linestyle='-')。当我用bp = sns.boxplot()
尝试这个时,我得到TypeError: 'Axes' object has no attribute '__getitem__'
。可以进行这些自定义吗?
seaborn.boxplots
将所有额外的 kwargs 直接传递给 pyplot.boxplot
,其文档在这里:matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.boxplot。如果您想深入了解自定义箱线图,那值得单独提问
很好的答案。这回答了我的问题,并提供了另一种快速可视化数据框的方法。【参考方案2】:
@Paul_H 答案的补充。
单张matplotlib.axes.Axes
上的并排箱线图,没有 seaborn:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame(np.random.rand(10,4), columns=list('ABCD'))
df['E'] = [1, 2, 1, 1, 1, 2, 1, 2, 2, 1]
mask_e = df['E'] == 1
# prepare data
data_to_plot = [df[mask_e]['A'], df[~mask_e]['A'],
df[mask_e]['B'], df[~mask_e]['B'],
df[mask_e]['C'], df[~mask_e]['C'],
df[mask_e]['D'], df[~mask_e]['D']]
# Positions defaults to range(1, N+1) where N is the number of boxplot to be drawn.
# we will move them a little, to visually group them
plt.figure(figsize=(10, 6))
box = plt.boxplot(data_to_plot,
positions=[1, 1.6, 2.5, 3.1, 4, 4.6, 5.5, 6.1],
labels=['A1','A0','B1','B0','C1','C0','D1','D0'])
【讨论】:
以上是关于同一图上的 Python 并排箱线图的主要内容,如果未能解决你的问题,请参考以下文章