【问题标题】:how to build hierarchy labels in a horizontal stacked bar chart如何在水平堆叠条形图中构建层次结构标签
【发布时间】:2019-01-11 03:23:16
【问题描述】:

我想要一个在 y 轴上带有层次标签的水平堆叠条形图。我搜索了一下,发现了以下很好的示例和代码。

但它适用于垂直堆积条形图。我想将其应用于水平条形图,所以我只是更改了kind='barh',但这不起作用。

我设法通过在最后几行中将所有 x 更改为 y 来删除默认的 ylabels。但是在定义的函数中将 x 更改为 y 并没有给我想要的:层次结构标签仍在 x 轴上。

有人可以帮忙吗?谢谢。

P.S.:为了让事情不那么混乱,我将我从第二个答案中找到的原始代码发布到 this question,而不是我尝试修改的代码

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from itertools import groupby

def test_table():
data_table = pd.DataFrame({'Room': ['Room A'] * 4 + ['Room B'] * 3,
                       'Shelf': ['Shelf 1'] * 2 + ['Shelf 2'] * 2 + ['Shelf 1'] * 2 + ['Shelf 2'],
                       'Staple':['Milk', 'Water', 'Sugar', 'Honey', 'Wheat', 'Corn', 'Chicken'],
                       'Quantity': [10, 20, 5, 6, 4, 7, 2,],
                       'Ordered': np.random.randint(0, 10, 7)
                       })
data_table
def add_line(ax, xpos, ypos):
line = plt.Line2D([xpos, xpos], [ypos + .1, ypos],
                  transform=ax.transAxes, color='black')
line.set_clip_on(False)
ax.add_line(line)

def label_len(my_index,level):
labels = my_index.get_level_values(level)
return [(k, sum(1 for i in g)) for k,g in groupby(labels)]

def label_group_bar_table(ax, df):
ypos = -.1
scale = 1./df.index.size
for level in range(df.index.nlevels)[::-1]:
    pos = 0
    for label, rpos in label_len(df.index,level):
        lxpos = (pos + .5 * rpos)*scale
        ax.text(lxpos, ypos, label, ha='center', transform=ax.transAxes)
        add_line(ax, pos*scale, ypos)
        pos += rpos
    add_line(ax, pos*scale , ypos)
    ypos -= .1

df = test_table().groupby(['Room','Shelf','Staple']).sum()
fig = plt.figure()
ax = fig.add_subplot(111)
df.plot(kind='bar',stacked=True,ax=fig.gca())

#Below 3 lines remove default labels
labels = ['' for item in ax.get_xticklabels()]
ax.set_xticklabels(labels)
ax.set_xlabel('')
label_group_bar_table(ax, df)
fig.subplots_adjust(bottom=.1*df.index.nlevels)
plt.show()

【问题讨论】:

    标签: python pandas matplotlib axis-labels


    【解决方案1】:

    你可以这样做。

    import matplotlib.pyplot as plt
    import matplotlib.gridspec as gridspec
    import pandas as pd
    import numpy as np
    
    data_table = pd.DataFrame({'Room': ['Room A'] * 4 + ['Room B'] * 3,
                               'Shelf': ['Shelf 1'] * 2 + ['Shelf 2'] * 2 + ['Shelf 1'] * 2 + ['Shelf 2'],
                               'Staple': ['Milk', 'Water', 'Sugar', 'Honey', 'Wheat', 'Corn', 'Chicken'],
                               'Quantity': [10, 20, 5, 6, 4, 7, 2, ],
                               'Ordered': np.random.randint(0, 10, 7)
                               })
    
    arrays = [list(data_table['Room']), list(data_table['Shelf']), list(data_table['Staple'])]
    data_table = data_table.groupby(['Room', 'Shelf', 'Staple']).sum()
    index = pd.MultiIndex.from_tuples(list(zip(*arrays)))
    
    df = pd.DataFrame(data_table[['Ordered', 'Quantity']], index=index).T
    
    # plotting
    fig = plt.figure()
    height_ratios = [len(df[df.columns.levels[0][0]].columns), len(df[df.columns.levels[0][1]].columns)] #i.e. 4, 3
    gs = gridspec.GridSpec(nrows=len(df.columns.levels[0]), ncols=1, height_ratios=height_ratios)
    
    ax1 = fig.add_subplot(gs[0,0])
    ax2 = fig.add_subplot(gs[1,0], sharex=ax1)
    axes = [ax1, ax2]
    for i, col in enumerate(df.columns.levels[0]):
        print(col)
        ax = axes[i]
        df[col].T.plot(ax=ax, stacked=True, kind='barh', width=.8)
    
        ax.legend_.remove()
        ax.set_ylabel(col, weight='bold')
        ax.xaxis.grid(b=True, which='major', color='black', linestyle='--', alpha=.4)
        ax.set_axisbelow(True)
    
        for tick in ax.get_xticklabels():
            tick.set_rotation(0)
    
    ax.legend()
    # make the ticklines invisible
    ax.tick_params(axis=u'both', which=u'both', length=0)
    plt.tight_layout()
    # remove spacing in between
    fig.subplots_adjust(wspace=0)  # space between plots
    
    plt.show()
    

    我改编了之前的答案of mine。请注意,层次结构分组显然在wishlist 上,因此,这是在此处手动完成的。

    【讨论】:

    • 感谢克里斯的帮助。但我真正的 df 有更多的层次,这样说:arrays = [['Fruit', 'Fruit', 'Fruit', 'Veggies', 'Veggies', 'Veggies','Fruit', 'Fruit', 'Fruit', 'Veggies'], ['Bananas', 'Oranges', 'Pears', 'Carrots', 'Potatoes', 'Celery','Bananas', 'Oranges', 'Pears', 'Carrots'], ['A','B','C','D','E','nan','G','H','I','J'], ['a','b','c','d','e','f','nan','h','i','nan']] index = pd.MultiIndex.from_tuples(list(zip(*arrays))) df = pd.DataFrame(np.random.randint(10, 50, size=(4, 10)), columns=index) df.sort_index(axis=1,inplace=True).
    • 我的问题是: 1. 我怎样才能在层次结构中显示其他级别? 2. 如何使钢筋的厚度自动计算为相等? 3.如何去掉nan,而不显示在图中?
    • 我已经根据您的初始 data_table 更新了我的答案(应该直接完成)。我已经操纵了您的 data_table 以将其塑造成一个“多索引”数据框。 1. 额外级别以第一级为基础,见df.columns.levels[0],图表数量也以此为基础。 2.我不完全确定你所说的计算相等是什么意思? 3. 你是说你的标签中有 NaN 吗?还是在您的数值中?如果是数字,如果存在 NaN,您可以轻松删除列/行。见熊猫的dropna
    • 2.这意味着每个图的高度是根据显示的条数调整的,因为我并不总是在每个图中有 4 个条。我已经编辑了问题中的data_table 以反映这一点。
    • 我明白了,这也是可能的。您需要使用 gridspec 之类的东西,然后您可以输入特定的 height_ratios,在本例中为 4 和 3 以反映要绘制的项目。查看更新的答案。
    猜你喜欢
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多