Как применить пользовательский порядок столбцов (по категориям) к коробочному графику панд?

РЕДАКТИРОВАТЬ: этот вопрос возник с пандами ~ 0,13 и был устарел прямой поддержкой где-то между версиями 0,15-0,18 (в соответствии с поздним ответом @Cireo)


Я могу получить boxplot из столбца зарплаты в панде DataFrame...

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

... однако я не могу понять, как определить порядок индекса, используемый в столбце "Категория" - я хочу указать свой собственный заказ в соответствии с другим критерием:

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

Как я могу применить свой собственный порядок столбцов к столбцам коробчатого графика? (кроме уродливой путаницы имен столбцов с префиксом для принудительного упорядочения)

"Категория" - это строка (на самом деле, она должна быть категориальной, но это было в столбце 0.13, где категоричным был гражданин третьего класса), принимая 27 различных значений: ['Accounting & Finance Jobs','Admin Jobs',...,'Travel Jobs']. Так что это легко можно pd.Categorical.from_array() с помощью pd.Categorical.from_array()

При проверке ограничение находится внутри pandas.tools.plotting.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()

enter image description here

Ответ 2

На самом деле я застрял с тем же вопросом. И я решил это, сделав карту и сбросив 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'))

Ответ 3

Обратите внимание, что pandas теперь может создавать категориальные столбцы. Если вы не возражаете, чтобы все столбцы присутствовали на вашем графике или соответствующим образом обрезали их, вы можете сделать что-то вроде ниже:

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

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

Недавний pandas также позволяет positions проходить весь путь от кадра к осям.

Ответ 4

РЕДАКТИРОВАТЬ: это правильный ответ после того, как прямая поддержка была добавлена где-то между версиями 0.15-0.18


Добавление отдельного ответа, что, возможно, может быть другой вопрос - обратная связь приветствуется.

Я хотел добавить пользовательский порядок столбцов в группе, что поставило передо мной много проблем. В конце концов, мне пришлось избегать попытки использовать boxplot из объекта groupby, а вместо этого самому проходить каждый подзаговор, чтобы groupby явные позиции.

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)

Ответ 5

Это может звучать глупо, но многие сюжеты позволяют вам определить порядок. Например:

Библиотека и набор данных

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