Я работаю над графиком, на котором хочу показать две группы на одной оси и третью группу в качестве значения заполнения. Проблема в том, что когда я рисую график, ось Y показывает значения в кортежах:
data_dict = {'major_group': list(np.array([['A']*10, ['B']*10]).flat),
'minor_group': ['q ','r ','s ','t ']*5,
'legend_group':np.repeat(['d','e','f','g','h','i'],[7,3,1,5,1,3])}
(pd.DataFrame(data= data_dict)
.groupby(['major_group', 'minor_group','legend_group'], observed = True)
.size()
.unstack()
.plot(kind='barh', stacked=True))
Результат:
Однако я ищу что-то вроде этого:
Как этого можно достичь? Можно ли установить какие-либо метки главной и второстепенной оси?
Этот код создаст горизонтальные столбцы, иерархически сгруппированные в метке оси Y:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from pandas import DataFrame
def create_data() -> DataFrame:
data_dict = {
'major_group': list(np.array([['A'] * 10, ['B'] * 10]).flat),
'minor_group': ['q', 'r', 's', 't'] * 5,
'legend_group': np.repeat(['d', 'e', 'f', 'g', 'h', 'i'], [7, 3, 1, 5, 1, 3])
}
return pd.DataFrame(data=data_dict).groupby(['major_group', 'minor_group', 'legend_group'], observed=True).size().unstack()
def plot_stacked_barh(df: DataFrame) -> None:
fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True)
for i, major_group in enumerate(df.index.levels[0]):
ax = axes[i]
df.loc[major_group].plot(kind='barh', stacked=True, ax=ax, width=.8)
if i == 0:
handles, labels = ax.get_legend_handles_labels()
ax.legend_.remove()
ax.set_ylabel(major_group, weight='bold')
ax.xaxis.grid(visible=True, which='major', color='black', linestyle='--', alpha=.4)
ax.set_axisbelow(True)
fig.legend(handles, labels, title='Legend Group')
plt.tight_layout()
fig.subplots_adjust(hspace=0)
plt.show()
def main() -> None:
df = create_data()
print(df)
plot_stacked_barh(df)
if __name__ == "__main__":
main()
Аналогичный вертикальный эквивалент можно найти здесь.
Мое предложение состоит в том, чтобы нарисовать GridSpec на основе value_counts основной группы (в вашем DataFrame) и поместить TextPath (например, [
, {
, (
, ...) внутри вторичных осей. :
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.patches import PathPatch
from matplotlib.text import TextPath
def brack_it(idx, maj_grp, txt = "["):
plt.subplot(gs[idx, 0]).axis("off")
tw = plt.gca().twinx()
tw.set(xticks=[], yticks=[])
tw.set_ylabel(maj_grp, labelpad=-10)
tw.spines[list(tw.spines)].set_visible(False)
tw.add_artist(PathPatch(TextPath((0.5, 0.12), txt, size=1.1), fc = "k"))
vc = df.reset_index().value_counts("major_group", normalize=True)
plt.figure(figsize=(6, 3))
gs = GridSpec(len(vc), 2, width_ratios=(1, 5), height_ratios=vc, wspace=0.1)
df.droplevel("major_group").rename_axis(None).plot.barh(
stacked=True, legend=False, grid=False, alpha=.9,
ax=plt.subplot(gs[:, 1]),
)
for i, n in enumerate(vc.index[::-1]):
brack_it(i, n)
Хорошее решение! Но, к сожалению, он плохо сочетался с длиной меток второстепенных групп. Когда на этих метках появляется больше символов, основная группа печатается прямо через них или поверх них.
Это сложнее, чем вы думаете... stackoverflow.com/q/31845258/6361531