Я хотел бы создать одну фигуру с двумя осями Y: Count
(из гистограммы) и Density
(из KDE).
Я хочу использовать sns.displot
в Seaborn >= v 0.11
.
import seaborn as sns
df = sns.load_dataset('tips')
# graph 1: This should be the Y-Axis on the left side of the figure
sns.displot(df['total_bill'], kind='hist', bins=10)
# graph 2: This should be the Y-axis on the right side of the figure
sns.displot(df['total_bill'], kind='kde')
Написанный мной код генерирует два отдельных графика; Я мог бы просто использовать сетку фасетов для двух отдельных графиков, но я хочу быть более кратким и поместить две оси Y на двух отдельных сетках в одну фигуру, разделяющую одну и ту же ось X.
displot()
— это функция уровня фигуры, которая может создавать несколько подзаголовков внутри фигуры. Таким образом, у вас нет контроля над отдельными осями.
Для создания комбинированных графиков можно использовать базовые функции уровня осей: histplot()
и kdeplot()
для Seaborn v.0.11. Эти функции принимают параметр ax=
. twinx()
создает вторую ось Y.
import matplotlib.pyplot as plt
import seaborn as sns
df = sns.load_dataset('tips')
fig, ax = plt.subplots()
sns.histplot(df['total_bill'], bins=10, ax=ax)
ax2 = ax.twinx()
sns.kdeplot(df['total_bill'], ax=ax2)
plt.tight_layout()
plt.show()
Редактировать:
Как упоминалось в комментариях, оси Y не выровнены. Левая ось только что-то говорит о гистограмме. Например. самая высокая корзина с высотой 68 означает, что между 12.618
и 17.392
всего ровно 68 купюр. Правая ось говорит только что-то о kde. Например. значение y 0.043
для x=20
будет означать, что вероятность того, что общий счет будет между 19.5
и 20.5
, составляет около 4,3%.
Чтобы выровнять оба, подобные sns.histplot(..., kde=True)
, можно рассчитать площадь гистограммы (ширина интервала, умноженная на количество значений данных) и использовать в качестве коэффициента масштабирования. Такое масштабирование сделало бы площадь гистограммы и площадь под кривой kde равными при измерении в пикселях:
num_bins = 10
bin_width = (df['total_bill'].max() - df['total_bill'].min()) / num_bins
hist_area = len(df) * bin_width
ax2.set_ylim(ymax=ax.get_ylim()[1] / hist_area)
Обратите внимание, что правая ось была бы больше похожа на процент, если бы гистограмма использовала ширину бина со степенью десяти (например, sns.histplot(..., bins=np.arange(0, df['total_bill'].max()+10, 10)
). Какие бины будут наиболее подходящими, сильно зависит от того, как вы хотите интерпретировать свои данные.
Хорошая проработка; вы точно правильно интерпретируете значение плотности в своем редактировании. Просто отметим для других, что «плотность» как значение оси Y имеет очень высокую вероятность неправильного толкования, поэтому будьте осторожны при отображении такого графика.
Хорошее объяснение различий между уровнями фигуры и уровня осей, но если вам буквально нужна кривая KDE поверх гистограммы, вы должны добавить
kde=True
к вызовуhistplot
(илиdisplot
). Правда, это не даст вам обе оси Y, но на самом деле они здесь не эквивалентны, как предполагает сюжет (ограничения устанавливаются автоматическим масштабированием matplotlib для художников, а не математическим соотношением между количеством / плотностью) и осью плотности все равно толком не интерпретируется.