#python #pandas #matplotlib
#python #панды #matplotlib
Вопрос:
Я хотел бы использовать matplotlib
для визуализации следующий pandas
фрейм данных, как показано на рисунке.
Эскиз показывает только то, что необходимо в общих чертах — нет необходимости иметь точный макет, как он изображен.
Как я могу выполнить эту задачу с помощью matplotlib
?
import pandas as pd
df = pd.DataFrame({'a': [0, 0, 0, 0, 0 , 1, 1,], 'b': [7, 7, 3, 3, 1, 2, 3, ], 'c': [102, 102, -50, -50, 30, 10, 10], })
df
a b c
0 0 7 102
1 0 7 102
2 0 3 -50
3 0 3 -50
4 0 1 30
5 1 2 10
6 1 3 10
Ответ №1:
Прежде чем приступить к визуализации, я бы предложил изменить форму ваших данных, чтобы сделать уровни вложенности явными и предварительно рассчитать частоты. Что-то вроде:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.gridspec as gridspec
temp_df = pd.concat([
df.groupby(["a"])["b"].value_counts().reset_index(name="count").rename(columns={"b":"value"}).assign(level_2="b"),
df.groupby(["a"])["c"].value_counts().reset_index(name="count").rename(columns={"c":"value"}).assign(level_2="c")
])
final_df = (temp_df
.rename(columns={"a":"level_1"})
[["level_1", "level_2", "value", "count"]]
.sort_values(["level_1", "level_2"]))
Результирующий фрейм данных будет выглядеть следующим образом:
level_1 level_2 value count
0 0 b 3 2
1 0 b 7 2
2 0 b 1 1
0 0 c -50 2
1 0 c 102 2
2 0 c 30 1
3 1 b 2 1
4 1 b 3 1
3 1 c 10 2
Теперь, чтобы отобразить значения и их количество таким вложенным способом, вы можете использовать GridSpec
для определения макета на основе того, сколько значений попадает под каждый вложенный уровень. Я жестко запрограммировал значения для иллюстрации этого игрушечного набора данных, но вы хотели бы обработать это программно для ваших реальных данных.
У вас есть 9 значений, поэтому у GridSpec
вас будет 9 столбцов. У вас есть 2 вложенных уровня, поэтому мы оставляем 2 нижние строки для вложенных меток и добавляем еще несколько строк для «размещения» гистограмм.
f = plt.figure(figsize=(10,4), dpi=300)
grid = gridspec.GridSpec(10, 9, figure=f)
mpl.rcParams["axes.edgecolor"] = "gainsboro"
# Use context manager to set mpl parameters for nested axs
with mpl.rc_context({"xtick.major.bottom": False, "ytick.major.left": False}):
# Level 1 axs (label, ax)
ax_level_1_0 = ("0", f.add_subplot(grid[9, 0:6]))
ax_level_1_1 = ("1", f.add_subplot(grid[9, 6:]))
level_1_axs = [ax_level_1_0, ax_level_1_1]
# Level 2 axs (label, ax)
ax_level_2_0b = ("B", f.add_subplot(grid[8, 0:3]))
ax_level_2_0c = ("C", f.add_subplot(grid[8, 3:6]))
ax_level_2_1b = ("B", f.add_subplot(grid[8, 6:8]))
ax_level_2_1c = ("C", f.add_subplot(grid[8, 8:]))
level_2_axs = [ax_level_2_0b, ax_level_2_0c, ax_level_2_1b, ax_level_2_1c]
# Actual count plot axs (level_1, level_2, ax)
ax_0b = (0, "b", f.add_subplot(grid[0:8, 0:3]))
ax_0b[2].set_ylabel("Frequency")
# Hide y-ticks
with mpl.rc_context({"ytick.major.left": False}):
ax_0c = (0, "c", f.add_subplot(grid[0:8, 3:6]))
ax_1b = (1, "b", f.add_subplot(grid[0:8, 6:8]))
ax_1c = (1, "c", f.add_subplot(grid[0:8, 8:]))
count_axs = [ax_0b, ax_0c, ax_1b, ax_1c]
# Remove white space between subplots
plt.subplots_adjust(wspace=0, hspace=0)
# Add label text to Level 1 and 2 axs
for label, ax in level_1_axs level_2_axs:
ax.text(0.5, 0.5, label, horizontalalignment='center',
verticalalignment='center', transform=ax.transAxes)
for l1, l2, ax in count_axs:
y = final_df.query(f'(level_1 == {l1}) amp; (level_2 == "{l2}")')["count"]
labels = final_df.query(f'(level_1 == {l1}) amp; (level_2 == "{l2}")')["value"]
x = range(len(y))
ax.bar(x, y, color="steelblue")
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.tick_params(
axis="x", direction="in", bottom=False, pad=-20,
colors="white", labelsize=15)