我打算在
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
选项的东西:
如果我用一个专栏来做这件事,它会起作用,这要归功于另一个SO问题Seaborn groupby pandas Series:
sns.boxplot(df.a1, groupby=df.b)
所以我想将所有列都放在一个图中(所有列都具有相似的比例)。
编辑:
上面的问题已被编辑,现在包含这个问题的“不干净”答案,但如果有人对这个问题有更好的想法,那就太好了。
正如其他答案所指出的,
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")
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
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])
相当于
df.filter(regex="a")
df[['a1','a2', 'a3', 'a4']]
希望这有帮助
它实际上并不比您链接的答案更好,但我认为在seaborn中实现此目的的方法是使用
这里没有使用列标题(我看到一个不同的线程想知道如何使用索引来做到这一点):
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)):
心理测量_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。这是相当小众的,但也许会对某人有所帮助。
列出项目