22

我打算在 a 中绘制多个列,所有列都使用insidepandas dataframe由另一列分组。对于 matplotlib中的类似问题,这里有一个很好的答案:Group boxplots但考虑到选项附带的事实,我认为在.groupbyseaborn.boxplotmatplotlib seaborn.boxplotgroupbyseaborn

在这里,我们使用一个失败的可重现示例:

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'])

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

我得到的是完全忽略groupby选项的东西:

分组失败

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

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

不会失败的seaborn

所以我想把我的所有列都放在一个图中(所有列的比例都差不多)。

编辑:

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

4

5 回答 5

27

正如其他答案所指出的那样,该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")

在此处输入图像描述

于 2014-08-13T14:39:07.427 回答
6

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

作为一种解决方法,您可以这样做:

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']]

   a1  a2  a3  a4
0   2   4   5   6
1   4   5   6   7
2   5   4   5   5
3  10   4   7   8
4   9   3   4   6
5   3   3   4   4

希望这可以帮助

于 2014-08-13T14:16:07.840 回答
6

您可以直接使用boxplot(我想当问题被问到时,这是不可能的,但seaborn版本 > 0.6 是这样)。

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

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

然后你只需绘制它:

sns.boxplot(x="a", hue="b", y="c", data=df_long)

用箱线图获得的图

于 2019-06-07T17:36:22.167 回答
5

它并没有比您链接的答案更好,但我认为在 seaborn 中实现这一点的方法是使用该FacetGrid功能,因为 groupby 参数仅针对传递给 boxplot 函数的 Series 定义。

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

g = sns.FacetGrid(pd.melt(df, id_vars='b'), col='b')
g.map(sns.boxplot, 'value', 'variable')

刻面seaborn箱线图

于 2014-08-13T11:55:26.840 回答
1

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

输出图

这里没有使用列标题(我看到一个不同的线程想知道如何使用索引来做到这一点):

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'
)

生成的数据框(rows = sample_n x variable_n(在我的情况下为 1626 x 6 = 9756)):

指数 心理测量_tst 与平均值的标准差
0 0.0 结果_var_1 -1.276182
1 0.0 结果_var_1 -1.118813
2 0.0 结果_var_1 -1.276182
9754 0.0 结果_var_6 0.892548
9755 0.0 结果_var_6 1.420480

如果你想在融化中使用索引:

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'
)

这是图形代码:(使用列标题完成 - 请注意 y-axis=value_name, x-axis = var_name, hue = id_vars):

# 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

请注意:我的大部分时间都花在调试 melt 功能上。我主要得到了错误"*only integer scalar arrays can be converted to a scalar index with 1D numpy indices array*"。我的输出要求我将结果变量值表和集群 (DBSCAN) 连接起来,并且我会在 concat 方法中的集群数组周围放置额外的方括号。所以我有一个列,其中每个值都是一个不可见的 List[int],而不是一个普通的 int。这是非常利基的,但也许它会帮助某人。

  1. 项目清单
于 2021-09-22T14:57:13.090 回答