如何按中值对熊猫中的箱线图进行排序?

Posted

技术标签:

【中文标题】如何按中值对熊猫中的箱线图进行排序?【英文标题】:How can I sort a boxplot in pandas by the median values? 【发布时间】:2014-03-21 16:20:41 【问题描述】:

我想按类别XY 在数据框df 中绘制列Z 的箱线图。如何按中位数对箱线图进行降序排序?

import pandas as pd
import random
n = 100
# this is probably a strange way to generate random data; please feel free to correct it
df = pd.DataFrame("X": [random.choice(["A","B","C"]) for i in range(n)], 
                   "Y": [random.choice(["a","b","c"]) for i in range(n)],
                   "Z": [random.gauss(0,1) for i in range(n)])
df.boxplot(column="Z", by=["X", "Y"])

请注意,this question 非常相似,但它们使用不同的数据结构。我对 pandas 比较陌生(并且一般只做过一些关于 python 的教程),所以我不知道如何使我的数据与那里发布的答案一起工作。这可能更像是一个重塑而不是一个绘图问题。也许有使用groupby的解决方案?

【问题讨论】:

【参考方案1】:

您可以使用How to sort a boxplot by the median values in pandas 中的答案,但首先您需要对数据进行分组并创建一个新的数据框:

import pandas as pd
import random
import matplotlib.pyplot as plt

n = 100
# this is probably a strange way to generate random data; please feel free to correct it
df = pd.DataFrame("X": [random.choice(["A","B","C"]) for i in range(n)], 
                   "Y": [random.choice(["a","b","c"]) for i in range(n)],
                   "Z": [random.gauss(0,1) for i in range(n)])
grouped = df.groupby(["X", "Y"])

df2 = pd.DataFrame(col:vals['Z'] for col,vals in grouped)

meds = df2.median()
meds.sort_values(ascending=False, inplace=True)
df2 = df2[meds.index]
df2.boxplot()

plt.show()

【讨论】:

我必须更改:meds.sort(ascending=False)meds.sort_values(ascending=False, inplace=True) 才能完成这项工作(Pandas 0.20.1、Python 3.6.1、Windows 8)。 @StephenMcAteer 感谢您的提示。我没有使用最新版本的 Pandas,因此请随时编辑答案并为未来用户添加您的答案版本。 当中位数相同时,有没有办法进行备份排序?例如,如果两个中位数相同,则按其中一个四分位数排序。【参考方案2】:

在函数形式中类似于 answer 以提高可移植性

import pandas as pd

def boxplot_sorted(df, by, column):
  df2 = pd.DataFrame(col:vals[column] for col, vals in df.groupby(by))
  meds = df2.median().sort_values()
  df2[meds.index].boxplot(rot=90)

boxplot_sorted(df, by=["X", "Y"], column="Z")

【讨论】:

【参考方案3】:

回答标题中的问题,而不涉及绘制两个分类变量的所有组合的额外细节:

n = 100
df = pd.DataFrame("Category": [np.random.choice(["A","B","C","D"]) for i in range(n)],      
                   "Variable": [np.random.normal(0, 10) for i in range(n)])

grouped = df.loc[:,['Category', 'Variable']] \
    .groupby(['Category']) \
    .median() \
    .sort_values(by='Variable')

sns.boxplot(x=df.Category, y=df.Variable, order=grouped.index)

我添加了这个解决方案,因为很难将接受的答案减少到单个变量,我相信人们正在寻找一种方法来做到这一点。我自己多次来这个问题寻找这样的答案。

【讨论】:

与您的最小示例有一些不一致(第一个 'Category 后缺少 ',在分组和绘图。但它背后的整体想法对我的 seaborn 驱动的应用程序很有用。 @ChristianKarcher 感谢您指出这些事情。这就是我不复制和粘贴的结果。【参考方案4】:

我遵循了公认的答案,但是当我想覆盖使用另一个 y 轴(即ax.twinx())的第二个图时遇到了麻烦。问题是第二个图的 x 轴覆盖了排序顺序。

我最终只使用seaborn 完成了以下操作。这类似于@rocksNwaves 的答案,但我是用问题引入的术语来写的。 只需三步:

    如果您不介意创建一个组合“X”和“Y”的列,那么使用 seaborn 会让事情变得更容易:

    df["XY"] = df["X"] + df["Y"]
    

    当然,您可以按照自己的方式组合这两列。

    按XY排序,得到排序后的索引

    grouped = df.groupby(["XY"])
    order = grouped.median()["Z"].sort_values().index
    

    使用 seaborn 绘图

    sns.boxplot(x="XY", y="Z", data=df, order=order)
    

    请注意,您可以将 order 视为指定 x 轴上标签的顺序。

一个完整的程序:

import pandas as pd
import random
import seaborn as sns
import matplotlib.pyplot as plt
n = 100
# this is probably a strange way to generate random data; please feel free to correct it
df = pd.DataFrame("X": [random.choice(["A","B","C"]) for i in range(n)],
                   "Y": [random.choice(["a","b","c"]) for i in range(n)],
                   "Z": [random.gauss(0,1) for i in range(n)])

df["XY"] = df["X"] + df["Y"]
grouped = df.groupby(["XY"])
order = grouped.median()["Z"].sort_values().index
sns.boxplot(x="XY", y="Z", data=df, order=order, palette="light:#5A9")
plt.show()

df 看起来像

    X  Y         Z
0   A  a  0.894873
1   C  a -0.568682
2   C  b  0.985260
3   B  c  2.056287
...

剧情是这样的

【讨论】:

以上是关于如何按中值对熊猫中的箱线图进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

根据中值对箱线图进行排序

使用 Plotly Graph 对象按中值对箱线图进行排序

来自python中值表的箱线图

使用熊猫的箱线图

熊猫中两组的箱线图

R中的箱线图显示平均值