Отрегулируйте ось x гистограммы, чтобы она соответствовала метрике, а не категориальной шкале.

avatar
Markus
1 июля 2021 в 17:56
71
1
0

Контекст

У меня есть pandas-DataFrame, содержащий агрегированные данные, которые я хочу отобразить в виде гистограммы:

df = pd.DataFrame({
    "x_axis": np.arange(0.  1.  .01)
    , "counts": some_aggregation
})

# df looks like this
x_axis |counts 
--------------
0.00   | 1.0    
0.01   | 0.0    
0.02   | 14.0   
...
1.00   | 12.0   

Как видно, это обеспечивает

  • x_axis: столбец, который я хочу использовать для предоставления оси X, поскольку счетчики могут содержать нулевые значения
  • counts, который представляет собой некоторую агрегацию, полученную из группы, по которой я хочу построить график предоставленного распределения в x_axis.

С помощью самого seaborn или matplotlib это можно легко визуализировать:

s = sns.barplot(x='x_axis', y="counts", data=df)

Проблема

Когда я строю диаграмму, как описано, X-тики преобразуются в range(0, 100) с помощью matplotlib, поскольку предоставленное распределение в x_axis обрабатывается как категориальные данные. Печать меток показывает эту проблему:

list(s.get_xticklabels())

# Output
[Text(0, 0, '0.0'),
 Text(1, 0, '0.01'),
 Text(2, 0, '0.02'),
...
]

Конечно, я могу установить x-ticklabels в правильное положение, выполнив следующее:

# Change x-axis-position of x-tick-labels
s.set_xticks(s.get_xticks() * .01)

# Output
[Text(0.0, 0, '0.0'),
 Text(0.01, 0, '0.01'),
 Text(0.02, 0, '0.02'),
...
]

Во всяком случае, несмотря на то, что сами метки теперь находятся в правильном положении, сама диаграмма не обновляется, чтобы показать столбцы над вновь размещенными метками.

Вопрос

Могу ли я выполнить один из вариантов to:

  • Преобразовать "категориальную" шкалу sns.barplot в метрическую? Это было бы моим любимым решением, поскольку я действительно хочу разделить ось X с другими графиками, диапазон которых составляет set_xlim((0. 1.)).
  • .
  • Настроить график так, чтобы столбцы снова совпадали с s.set_xticks(s.get_xticks() * .01)?
Источник

Ответы (1)

avatar
JohanC
1 июля 2021 в 22:52
2

Вы можете использовать sns.histplot() с weights и явно установить границы ячеек между значениями x:

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

xs = np.arange(0.  1.  .01)
df = pd.DataFrame({"x_axis": xs,
                   "counts": np.random.randint(0, 10, len(xs))})
fig = plt.figure(figsize=(16, 4))
ax = sns.histplot(data=df, x="x_axis", weights="counts", bins=np.arange(-0.005, 1, 0.01))
ax.margins(x=0.005)  # the default margins left and right are too large
ax.set_xticks(xs)
ax.tick_params(axis='x', labelrotation=90)
fig.tight_layout()
plt.show()

sns.histplot using weights and explicit bins

Чтобы иметь больше стандартных x-меток, ax.set_xticks(xs) и вращение можно было бы исключить.

Также обратите внимание, что "np.arange(0. 1. .01)" может быть обманчивым. Поскольку он работает с числами с плавающей запятой, в зависимости от ошибок округления окончательный 1.0 может быть включен или нет. Один из способов справиться с этим — сделать конечное значение на несколько эпсилон больше (если вы хотите включить его, например, np.arange(0, 1.0001, .1)) или меньше (чтобы остановиться только на одно значение раньше). Или вы можете создать числа как целые числа, а затем разделить (np.arange(0, 100)/100).

Markus
2 июля 2021 в 12:13
0

Спасибо! Я адаптировал логику к своему коду, и он работает, включая общую ось X на нескольких графиках! :)