Boxplot: пользовательская ширина в seaborn

#python #matplotlib #seaborn #boxplot

#python #matplotlib #seaborn #boxplot

Вопрос:

Я пытаюсь построить коробочные графики в seaborn, ширина которых зависит от логарифма значения оси x. Я создаю список ширин и передаю его параметру widths =widths в seaborn.boxplot.

Тем не менее, я понимаю, что

 raise ValueError(datashape_message.format("widths"))
ValueError: List of boxplot statistics and `widths` values must have same the length
  

Когда я отлаживал и проверял, в статистике boxplot есть только один dict, тогда как у меня 8 boxplots.
Не могу точно определить, в чем проблема.

Вот изображение Boxplot

Я использую фрейм данных pandas и seaborn для построения графика.

Ответ №1:

Boxplot от Seaborn, похоже, не понимает widths= параметр.

Вот способ создать boxplot для каждого x значения с помощью matplotlib, boxplot который принимает width= параметр. Приведенный ниже код предполагает, что данные организованы в фрейме данных panda.

 from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

df = pd.DataFrame({'x': np.random.choice([1, 3, 5, 8, 10, 30, 50, 100], 500),
                   'y': np.random.normal(750, 20, 500)})
xvals = np.unique(df.x)
positions = range(len(xvals))
plt.boxplot([df[df.x == xi].y for xi in xvals],
            positions=positions, showfliers=False,
            boxprops={'facecolor': 'none'}, medianprops={'color': 'black'}, patch_artist=True,
            widths=[0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
means = [np.mean(df[df.x == xi].y) for xi in xvals]
plt.plot(positions, means, '--k*', lw=2)
# plt.xticks(positions, xvals) # not needed anymore, as the xticks are set by the swarmplot
sns.swarmplot('x', 'y', data=df)
plt.show()
  

пример графика

Связанный с этим вопрос о том, как установить ширину поля в зависимости от размера группы. Ширина может быть рассчитана как некоторая максимальная ширина, умноженная на размер каждой группы по сравнению с размером самой большой группы.

 from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

y_true = np.random.normal(size=100)
y_pred = y_true   np.random.normal(size=100)
df = pd.DataFrame({'y_true': y_true, 'y_pred': y_pred})
df['y_true_bin'] = pd.cut(df['y_true'], range(-3, 4))

sns.set()
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12, 5))
sns.boxplot(x='y_true_bin', y='y_pred', data=df, color='lightblue', ax=ax1)

bins, groups = zip(*df.groupby('y_true_bin')['y_pred'])
lengths = np.array([len(group) for group in groups])
max_width = 0.8
ax2.boxplot(groups, widths=max_width * lengths / lengths.max(),
            patch_artist=True, boxprops={'facecolor': 'lightblue'})
ax2.set_xticklabels(bins)
ax2.set_xlabel('y_true_bin')
ax2.set_ylabel('y_pred')
plt.tight_layout()
plt.show()
  

boxplot с шириной в зависимости от размера подмножества

Комментарии:

1. Это несколько решает мою проблему, но создает другую. Теперь, когда я создаю seaborn.swarmplot или полосовую диаграмму, это каким-то образом сдвигает всю фигуру на 1 boxplot. Ссылка

2. Вы можете удалить, plt.xticks(positions, xvals) если тики установлены с помощью swarmplot. Возможно, вы не изменили старое plt.xticks(range(1, len(xvals) 1), xvals) , так как это изменило бы значения. Средства должны быть нанесены с использованием того же positions , что и boxplot.

3. Boxplot находится на своем месте, но средняя линия и x-тики все еще сдвинуты, хотя новый график

4. Да, теперь он отображается точно так, как ожидалось. 🙂

5. @buhtz Действительно, более «точным» ответом было бы: widths параметр не поддерживается в seaborn. Если вы также хотите включить hue , было бы еще менее очевидно, как поддерживать уклонение от оттенков и при этом получать хороший график, избегая как перекрывающихся полей, так и полей, расположенных слишком далеко друг от друга.