当输入是DataFrame时,在seaborn中对箱线图进行分组

问题描述 投票:0回答:5

我打算在

pandas dataframe
中绘制多列,所有列都使用
groupby
内的
seaborn.boxplot
分组。对于
matplotlib
matplotlib: Group boxplots 中的类似问题,这里有一个很好的答案,但考虑到
seaborn.boxplot
附带
groupby
选项,我认为在
seaborn
中执行此操作可能会更容易。

这里我们举一个失败的可重现示例:

import seaborn as sns
import pandas as pd
df = pd.DataFrame([[2, 4, 5, 6, 1], [4, 5, 6, 7, 2], [5, 4, 5, 5, 1],
                   [10, 4, 7, 8, 2], [9, 3, 4, 6, 2], [3, 3, 4, 4, 1]],
                  columns=['a1', 'a2', 'a3', 'a4', 'b'])

# display(df)
   a1  a2  a3  a4  b
0   2   4   5   6  1
1   4   5   6   7  2
2   5   4   5   5  1
3  10   4   7   8  2
4   9   3   4   6  2
5   3   3   4   4  1

#Plotting by seaborn
sns.boxplot(df[['a1','a2', 'a3', 'a4']], groupby=df.b)

我得到的是完全忽略

groupby
选项的东西:

Failed groupby

如果我用一个专栏来做这件事,它会起作用,这要归功于另一个SO问题Seaborn groupby pandas Series

sns.boxplot(df.a1, groupby=df.b)

seaborn that does not fail

所以我想将所有列都放在一个图中(所有列都具有相似的比例)。

编辑:

上面的问题已被编辑,现在包含这个问题的“不干净”答案,但如果有人对这个问题有更好的想法,那就太好了。

python pandas matplotlib seaborn boxplot
5个回答
28
投票

正如其他答案所指出的,

boxplot
函数仅限于绘制箱线图的单个“层”,并且
groupby
参数仅在输入是系列并且您有要使用的第二个变量时才有效将观察结果放入每个框中..

但是,您可以通过

factorplot
函数,使用
kind="box"
来实现我认为您希望实现的目标。但是,您首先必须将示例数据帧“融合”为所谓的长格式或“整洁”格式,其中每列都是一个变量,每行都是一个观察结果:

df_long = pd.melt(df, "b", var_name="a", value_name="c")

那么绘制起来就非常简单了:

sns.factorplot("a", hue="b", y="c", data=df_long, kind="box")

enter image description here


11
投票

您可以直接使用

sns.boxplot
(轴级函数)或
sns.catplot
kind='box'
(图形级函数)。有关更多详细信息,请参阅图形级函数与轴级函数

正如 @mwaskom 所解释的,您必须将示例数据框转换为“长格式”,其中每列都是一个变量,每行都是一个观察值:

melt

df_long = pd.melt(df, "b", var_name="a", value_name="c") # display(df_long.head()) b a c 0 1 a1 2 1 2 a1 4 2 1 a1 5 3 2 a1 10 4 2 a1 9

sns.boxplot

plt.figure(figsize=(5, 5)) ax = sns.boxplot(x="a", hue="b", y="c", data=df_long) ax.spines[['top', 'right']].set_visible(False) sns.move_legend(ax, bbox_to_anchor=(1, 0.5), loc='center left', frameon=False)

使用更少的代码行创建与
sns.catplot
相同的绘图。

sns.boxplot

结果图

Seaborn 的 groupby 函数采用 Series 而不是 DataFrame,这就是它不起作用的原因。


8
投票

g = sns.catplot(kind='box', data=df_long, x='a', y='c', hue='b', height=5, aspect=1)

它给出:

注意 

fig, ax = plt.subplots(1,2, sharey=True) for i, grp in enumerate(df.filter(regex="a").groupby(by=df.b)): sns.boxplot(grp[1], ax=ax[i]) sns 相当于

df.filter(regex="a")

df[['a1','a2', 'a3', 'a4']]
希望这有帮助


它实际上并不比您链接的答案更好,但我认为在seaborn中实现此目的的方法是使用


5
投票

功能,因为 groupby 参数仅为传递给 boxplot 函数的 Series 定义。

这里有一些代码 - 
FacetGrid 是必要的,因为(据我所知)构面映射只能将单个列作为参数,因此数据需要转换为“长”格式。

pd.melt

    

faceted seaborn boxplot这并没有给这个对话添加太多内容,但是在与这个问题斗争了超过保证的时间之后(实际的集群不可用),我想我应该添加我的实现作为另一个例子。它有一个叠加的散点图(因为我的数据集有多烦人),使用索引显示融化,以及一些美学调整。我希望这对某人有用。


1
投票

这里没有使用列标题(我看到一个不同的线程想知道如何使用索引来做到这一点):output_graph g = sns.FacetGrid(pd.melt(df, id_vars='b'), col='b') g.map(sns.boxplot, 'value', 'variable')

生成的数据帧(行=sample_n xvariable_n(在我的例子中为1626 x 6 = 9756)):


索引集群00.00.00.00.00.0如果您想将索引与熔化一起使用:
心理测量_tst 与平均值的标准差
结果_var_1 -1.276182 1
结果_var_1 -1.118813 2
结果_var_1 -1.276182 9754
结果_var_6 0.892548 9755
结果_var_6 1.420480
combined_array: ndarray = np.concatenate([dbscan_output.data, dbscan_output.labels.reshape(-1, 1)], axis=1) cluster_data_df: DataFrame = DataFrame(combined_array) if you want to use labelled columns: column_names: List[str] = list(outcome_variable_names) column_names.append('cluster') cluster_data_df.set_axis(column_names, axis='columns', inplace=True) graph_data: DataFrame = pd.melt( frame=cluster_data_df, id_vars=['cluster'], # value_vars is an optional param - by default it uses columns except the id vars, but I've included it as an example # value_vars=['outcome_var_1', 'outcome_var_2', 'outcome_var_3', 'outcome_var_4', 'outcome_var_5', 'outcome_var_6'] var_name='psychometric_test', value_name='standard deviations from the mean' )

这是图形代码: (完成列标题 - 只需注意 y 轴 = value_name、x 轴 = var_name、hue = id_vars):

graph_data: DataFrame = pd.melt(
    frame=cluster_data_df,
    id_vars=cluster_data_df.columns[-1],
    # value_vars=cluster_data_df.columns[:-1],
    var_name='psychometric_test',
    value_name='standard deviations from the mean'
)

请注意:我的大部分时间都花在调试熔化函数上。我主要遇到错误

# plot graph grouped by cluster sns.set_theme(style="ticks") fig = plt.figure(figsize=(10, 10)) fig.set(font_scale=1.2) fig.set_style("white") # create boxplot fig.ax = sns.boxplot(y='standard deviations from the mean', x='psychometric_test', hue='cluster', showfliers=False, data=graph_data) # set box alpha: for patch in fig.ax.artists: r, g, b, a = patch.get_facecolor() patch.set_facecolor((r, g, b, .2)) # create scatterplot fig.ax = sns.stripplot(y='standard deviations from the mean', x='psychometric_test', hue='cluster', data=graph_data, dodge=True, alpha=.25, zorder=1) # customise legend: cluster_n: int = dbscan_output.n_clusters ## create list with legend text i = 0 cluster_info: Dict[int, int] = dbscan_output.cluster_sizes # custom method legend_labels: List[str] = [] while i < cluster_n: label: str = f"cluster {i+1}, n = {cluster_info[i]}" legend_labels.append(label) i += 1 if -1 in cluster_info.keys(): cluster_n += 1 label: str = f"Unclustered, n = {cluster_info[-1]}" legend_labels.insert(0, label) ## fetch existing handles and legends (each tuple will have 2*cluster number -> 1 for each boxplot cluster, 1 for each scatterplot cluster, so I will remove the first half) handles, labels = fig.ax.get_legend_handles_labels() index: int = int(cluster_n*(-1)) labels = legend_labels plt.legend(handles[index:], labels[0:]) plt.xticks(rotation=45) plt.show() asds
。我的输出要求我连接结果变量值表和簇 (DBSCAN),并且我会在 concat 方法中的簇数组周围放置额外的方括号。所以我有一个列,其中每个值都是一个不可见的 List[int],而不是一个普通的 int。这是相当小众的,但也许会对某人有所帮助。

列出项目

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.