如何绘制分布树的正确图表?

3

我使用带有Matplotlib的Python来可视化数据集中子组的分布百分比。

想象一棵树:

Data --- group1 (40%)
     -
     --- group2 (25%)
     -
     --- group3 (35%)


group1 --- A (25%)
       -
       --- B (25%)
       -
       --- c (50%)

还有,每个组都可以有多个子组,每个子组也同样如此。

我该如何绘制这些信息的适当图表?


我在使用matplotlib时遇到了问题,如果有人熟悉它并且有使用经验,那么他/她就有资格回答我的问题。我知道这个网站的运作方式。谢谢! - Ali Crash
不,我并没有评判你的资格,我的意思是评论/回答内容应该有助于解决问题,否则就不要浪费时间。问题很明确和具体。我想用matplotlib绘制一个图表。如果你知道解决方案,欢迎发表答案。 - Ali Crash
2个回答

2

我创建了一个最小化可复现的示例,我认为它符合你的描述,但如果不是你所需的,请告诉我。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
data = pd.DataFrame()
n_rows = 100
data['group'] = np.random.choice(['1', '2', '3'], n_rows)
data['subgroup'] = np.random.choice(['A', 'B', 'C'], n_rows)

例如,我们可以获取以下子组的计数。
In [1]: data.groupby(['group'])['subgroup'].value_counts()
Out[1]: group  subgroup
    1   A      17
        C      16
        B      5
    2   A      23
        C      10
        B      7
    3   C      8
        A      7
        B      7
 Name: subgroup, dtype: int64

我创建了一个函数,根据列的顺序(例如['group','subgroup']),计算所需的计数,并逐步绘制相应百分比的条形图。
import matplotlib.pyplot as plt
import matplotlib.cm

def plot_tree(data, ordering, axis=False):
    """
    Plots a sequence of bar plots reflecting how the data 
    is distributed at different levels. The order of the 
    levels is given by the ordering parameter.

    Parameters
    ----------
    data: pandas DataFrame
    ordering: list
        Names of the columns to be plotted.They should be 
        ordered top down, from the larger to the smaller group.
    axis: boolean
        Whether to plot the axis.

    Returns
    -------
    fig: matplotlib figure object.
        The final tree plot.
    """

    # Frame set-up
    fig, ax = plt.subplots(figsize=(9.2, 3*len(ordering)))
    ax.set_xticks(np.arange(-1, len(ordering)) + 0.5)
    ax.set_xticklabels(['All'] + ordering, fontsize=18)
    if not axis:
        plt.axis('off')
    counts=[data.shape[0]]

    # Get colormap
    labels = ['All']
    for o in reversed(ordering):
        labels.extend(data[o].unique().tolist())
    # Pastel is nice but has few colors. Change for a larger map if needed
    cmap = matplotlib.cm.get_cmap('Pastel1', len(labels))
    colors = dict(zip(labels, [cmap(i) for i in range(len(labels))]))

    # Group the counts
    counts = data.groupby(ordering).size().reset_index(name='c_' + ordering[-1])
    for i, o in enumerate(ordering[:-1], 1):
        if ordering[:i]:
            counts['c_' + o]=counts.groupby(ordering[:i]).transform('sum')['c_' + ordering[-1]]
    # Calculate percentages
    counts['p_' + ordering[0]] = counts['c_' + ordering[0]]/data.shape[0]
    for i, o in enumerate(ordering[1:], 1):
        counts['p_' + o] = counts['c_' + o]/counts['c_' + ordering[i-1]]

    # Plot first bar - all data
    ax.bar(-1, data.shape[0], width=1, label='All', color=colors['All'], align="edge")
    ax.annotate('All -- 100%', (-0.9, 0.5), fontsize=12)
    comb = 1  # keeps track of the number of possible combinations at each level
    for bar, col in enumerate(ordering):
        labels = sorted(data[col].unique())*comb
        comb *= len(data[col].unique())
        # Get only the relevant counts at this level
        local_counts = counts[ordering[:bar+1] + 
                              ['c_' + o for o in ordering[:bar+1]] + 
                              ['p_' + o for o in ordering[:bar+1]]].drop_duplicates()
        sizes = local_counts['c_' + col]
        percs = local_counts['p_' + col]
        bottom = 0  # start at from 0
        for size, perc, label in zip(sizes, percs, labels):
            ax.bar(bar, size, width=1, bottom=bottom, label=label, color=colors[label], align="edge")
            ax.annotate('{} -- {:.0%}'.format(label, perc), (bar+0.1, bottom+0.5), fontsize=12)
            bottom += size  # stack the bars
    ax.legend(colors)
    return fig

通过上述数据,我们将得到以下结果。
fig = plot_tree(data, ['group', 'subgroup'], axis=True)

Tree plot example


我想提前感谢你,但这不是我想要的。我想要一个单一的条形/正方形,数据值为100%(在我的示例树中),然后group1、group2、group3作为3个堆叠在一起的条形图,内部有所提到的百分比(40%、25%、35%)在数据条中。然后每个组再分成自己的子组,并附带其自己的百分比信息。我试着自己画堆叠条形图,但我无法将条形图推入彼此之间,它们只是堆叠在一起。 - Ali Crash
@Ali Crash,我不确定我完全理解你所寻找的内容,但我再试一次。请查看我的更新答案。 - AlCorreia
这不完全是我想要的,但它同样适合。我已经决定Matplotlib不是我目的的正确工具。像d3.js这样的前端工具在这些情况下更有帮助。谢谢。 - Ali Crash
传说在这里是错误的。 - quant

0

是的,我尝试过了,堆积条形图通常不适合我的目的,因为它们会将信息堆叠在彼此之上。例如,数据为100,组1为40,当你将它们一起绘制时,新的柱形图最大值为140,我希望组1绘制在数据内部,而不是在数据之上。 - Ali Crash
但是你必须在组级别上使用你的值 - 只绘制组 - 总共它们将给出整个数据的聚合值。 - MartinKitty
如果你认为你有解决方案,欢迎发布答案。我已经在堆积条形图上玩了很多,但没有取得任何进展。 - Ali Crash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接