如何将自定义列顺序(在分类上)应用于熊猫箱线图?

Posted

技术标签:

【中文标题】如何将自定义列顺序(在分类上)应用于熊猫箱线图?【英文标题】:How to apply custom column order (on Categorical) to pandas boxplot? 【发布时间】:2013-03-10 14:29:12 【问题描述】:

编辑:这个问题早在 2013 年就出现在 pandas ~0.13 中,并且被直接支持 boxplot 的版本在 0.15-0.18 之间(根据@Cireo's late answer;自有人问起,pandas 大大改进了对分类的支持)而被淘汰。 /p>


我可以在 pandas DataFrame 中获得薪水列的boxplot...

train.boxplot(column='Salary', by='Category', sym='')

...但是我不知道如何定义列“类别”上使用的索引顺序 - 我想提供我自己的自定义顺序,根据另一个标准:

category_order_by_mean_salary = train.groupby('Category')['Salary'].mean().order().keys()

如何将我的自定义列顺序应用于箱线图列? (除了丑陋的用前缀来强制排序的列名)

'Category' 是一个字符串(实际上,应该是一个分类,但这是在 0.13 中,分类是三等公民)列有 27 个不同的值:['Accounting & Finance Jobs','Admin Jobs',...,'Travel Jobs']。所以可以很容易地用pd.Categorical.from_array()分解它

经过检查,限制在 pandas.tools.plotting.py:boxplot() 内部,它在不允许排序的情况下转换列对象:

pandas.core.frame.py.boxplot() 是传递给 pandas.tools.plotting.py:boxplot() 实例化... matplotlib.pyplot.py:boxplot() 实例化... matplotlib.axes.py:boxplot()

我想我可以破解一个自定义版本的 pandas boxplot(),或者深入到对象的内部。并提交增强请求。

【问题讨论】:

【参考方案1】:

如果没有工作示例,很难说如何做到这一点。我的第一个猜测是只添加一个包含您想要的订单的整数列。

一种简单的蛮力方法是一次添加一个箱线图。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(np.random.rand(37,4), columns=list('ABCD'))
columns_my_order = ['C', 'A', 'D', 'B']
fig, ax = plt.subplots()
for position, column in enumerate(columns_my_order):
    ax.boxplot(df[column], positions=[position])

ax.set_xticks(range(position+1))
ax.set_xticklabels(columns_my_order)
ax.set_xlim(xmin=-0.5)
plt.show()

【讨论】:

为您添加了详细信息以及解决方法的想法。添加一个单独的独立整数列并不能给出一个像样的图表,因为现在您的列标签是(难以辨认的)整数,而不是文本。 (将文本前缀混入类别名称以强制自定义排序顺序可能是最快的黑客攻击。但仍然很难看) pandas DataFrame cannot handle a Categorical column,不像 R. 不是我要去的地方。我通常只使用带有硬编码查找表的apply。不过,请参阅我为不同方法编辑的回复。 呃!我怎么没想到!好主意。【参考方案2】:

编辑:这是在版本 0.15-0.18 之间添加直接支持后的正确答案


tl;dr:对于最近的熊猫 - 使用 positions 参数到 boxplot。

添加一个单独的答案,这可能是另一个问题 - 感谢反馈。

我想在 groupby 中添加自定义列顺序,这给我带来了很多问题。最后,我不得不避免尝试从groupby 对象中使用boxplot,而是自己遍历每个子图以提供明确的位置。

import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame()
df['GroupBy'] = ['g1', 'g2', 'g3', 'g4'] * 6
df['PlotBy'] = [chr(ord('A') + i) for i in xrange(24)]
df['SortBy'] = list(reversed(range(24)))
df['Data'] = [i * 10 for i in xrange(24)]

# Note that this has no effect on the boxplot
df = df.sort_values(['GroupBy', 'SortBy'])
for group, info in df.groupby('GroupBy'):
    print 'Group: %r\n%s\n' % (group, info)

# With the below, cannot use
#  - sort data beforehand (not preserved, can't access in groupby)
#  - categorical (not all present in every chart)
#  - positional (different lengths and sort orders per group)
# df.groupby('GroupBy').boxplot(layout=(1, 5), column=['Data'], by=['PlotBy'])

fig, axes = plt.subplots(1, df.GroupBy.nunique(), sharey=True)
for ax, (g, d) in zip(axes, df.groupby('GroupBy')):
    d.boxplot(column=['Data'], by=['PlotBy'], ax=ax, positions=d.index.values)
plt.show()

在我的最终代码中,确定位置会稍微多一些,因为每个排序值都有多个数据点,我最终不得不执行以下操作:

to_plot = data.sort_values([sort_col]).groupby(group_col)
for ax, (group, group_data) in zip(axes, to_plot):
    # Use existing sorting
    ordering = enumerate(group_data[sort_col].unique())
    positions = [ind for val, ind in sorted((v, i) for (i, v) in ordering)]
    ax = group_data.boxplot(column=[col], by=[plot_by], ax=ax, positions=positions)

【讨论】:

原来的问题已经关闭多年了,为什么不为这个答案添加一个新问题呢?指定熊猫 0.20+ 不确定发布问题然后自己回答的礼仪 =/ 没关系。在这种情况下也是可取的 - 这个问题在熊猫 0.19 的某个时候已经过时了【参考方案3】:

实际上,我遇到了同样的问题。我通过制作地图并重置xticklabels来解决它,代码如下:

df = pd.DataFrame("A":["d","c","d","c",'d','c','a','c','a','c','a','c'])
df['val']=(np.random.rand(12))
df['B']=df['A'].replace('d':'0','c':'1','a':'2')
ax=df.boxplot(column='val',by='B')
ax.set_xticklabels(list('dca'))

【讨论】:

请注意set_xticklabels() 会给出错误的结果,因为它只是覆盖了现有的标签。 set_xticklabels(list('dca')) 没有按照您和 OP 的意图将标签 d' 的值移动到第一位,而是将第一个标签的任何内容重新标记为“d”【参考方案4】:

请注意,pandas 现在可以创建分类列。如果您不介意在图表中显示所有列,或者适当地修剪它们,您可以执行以下操作:

http://pandas.pydata.org/pandas-docs/stable/categorical.html

df['Category'] = df['Category'].astype('category', ordered=True)

最近的 pandas 似乎也允许 positions 从框架一直传递到轴。

https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/pyplot.py https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/axes/_axes.py

【讨论】:

链接说 0.15,但我对此表示怀疑。我不确定该功能当时是否已完全集成。我能够在0.19.2 中完成所有这些操作 感谢您的检查。【参考方案5】:

正如 Cireo 指出的:

使用新的 positions= 属性:

df.boxplot(column=['Data'], by=['PlotBy'], positions=df.index.values)

我知道这是以前精确的,但对于像我这样的新手来说还不够清楚/总结

【讨论】:

cc: @Cireo 你可能想编辑你的答案以清楚起见【参考方案6】:

如果您对箱线图中的默认列顺序不满意,您可以通过在箱线图函数中设置 column 参数将其更改为特定顺序。

检查以下两个示例:

np.random.seed(0)
df = pd.DataFrame(np.random.rand(37,4), columns=list('ABCD'))

##
plt.figure()
df.boxplot()
plt.title("default column order")

##
plt.figure()
df.boxplot(column=['C','A', 'D', 'B'])
plt.title("Specified column order")

【讨论】:

【参考方案7】:

这可能听起来有点傻,但许多情节允许您确定顺序。例如:

库和数据集

import seaborn as sns
df = sns.load_dataset('iris')

具体顺序

p1=sns.boxplot(x='species', y='sepal_length', data=df, order=["virginica", "versicolor", "setosa"])
sns.plt.show()

【讨论】:

【参考方案8】:

这可以通过应用分类顺序来解决。你可以自己决定排名。我将举一个星期几的例子。

提供工作日的分类顺序

#List categorical variables in correct order
weekday = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
#Assign the above list to category ranking
wDays = pd.api.types.CategoricalDtype(ordered= True, categories=Weekday)
#Apply this to the specific column in DataFrame
df['Weekday'] = df['Weekday'].astype(wDays)
# Then generate your plot
plt.figure(figsize = [15, 10])
sns.boxplot(data = flights_samp, x = 'Weekday', y = 'Y Axis Variable', color = colour)

【讨论】:

以上是关于如何将自定义列顺序(在分类上)应用于熊猫箱线图?的主要内容,如果未能解决你的问题,请参考以下文章

向熊猫数据框箱线图添加标签?

使用 Debezium 的 Quarkus 发件箱模式:如何将自定义列添加到发件箱表

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

为熊猫箱线图(groupby)设置无标题

熊猫仅分箱时间列而不是自定义范围中的日期[重复]

在 Wordpress 中,如何将自定义帖子类型的默认管理员排序顺序设置为自定义列?