如何使用pandas plot获得两个图例,一个用于堆叠条的颜色,另一个用于条的图案填充?

2024-05-23 13:37:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在试图理解this post的答案,以便填充两个不同的传说

我为每个条创建了一个带有不同图案填充的聚集堆叠条形图,下面的代码与前面文章的答案略有不同

但我还并没有弄清楚如何得到一个带有颜色的图例和一个带有图案填充的图例

颜色图例应对应于A、B、C、D、E,如果条带阴影,则阴影图例应显示“带”,如果未带阴影,则应显示“不带”

import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap as coloring

# copy the dfs below and use pd.read_clipboard() to reproduce
df_1
     A   B   C   D   E
Mg  10  15  23  25  27
Ca  30  33   0  20  17

df_2
     A   B   C   D   E
Mg  20  12   8  40  10
Ca   7  26  12  22  16

hatches=(' ', '//')
colors_ABCDE=['tomato', 'gold', 'greenyellow', 'forestgreen', 'palevioletred']
dfs=[df_1,df_2]

for each_df, df in enumerate(dfs):
    df.plot(ax=plt.subplot(111), kind="barh", \
            stacked=True, hatch=hatches[each_df], \
            colormap=coloring.from_list("my_colormap", colors_ABCDE), \
            figsize=(7,2.5), position=len(dfs)-each_df-1, \
            align='center', width=0.2, edgecolor="darkgrey")

plt.legend(loc='center left', bbox_to_anchor=(1.0, 0.5), fontsize=12)

我得到的图是:

plot

有没有办法创建两个传说,然后一个挨着另一个或一个放在另一个下面?提前感谢^ ^


Tags: 答案fromimportdfmatplotlib颜色asplt
2条回答

由于在{{CD1>}中添加传奇是一个复杂的、广泛的步骤,请考虑使用@ jjjc与function solution引用的非常链接。不过,您需要根据水平条形图的需要调整该函数。具体而言:

  • 为颜色映射和DataFrame.plot调用添加一个参数
  • 对于水平版本,将条形图从kind='bar'调整为kind='barh'
  • 将x交换为y行:rect.set_y(rect.get_y() + 1 / float(n_df + 1) * i / float(n_col))
  • rect.set_height(1 / float(n_df + 1))行中用width交换height
  • 调整axe.set_xticksaxe.set_xticklabelsnp.arange(0, 120, 20)

功能

import numpy as np
import pandas as pd
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap as coloring

def plot_clustered_stacked(dfall, labels=None, title="multiple stacked bar plot", H="//",
                            colors_ABCDE=['tomato', 'gold', 'greenyellow', 'forestgreen', 'palevioletred'], **kwargs):
    """
       CREDIT: @jrjc (https://stackoverflow.com/a/22845857/1422451)

       Given a list of dataframes, with identical columns and index, create a clustered stacked bar plot. 
       labels is a list of the names of the dataframe, used for the legend
       title is a string for the title of the plot
       H is the hatch used for identification of the different dataframe
    """

    n_df = len(dfall)
    n_col = len(dfall[0].columns) 
    n_ind = len(dfall[0].index)
    axe = plt.subplot(111)

    for df in dfall : # for each data frame
        axe = df.plot(kind="barh",
                      linewidth=0,
                      stacked=True,
                      ax=axe,
                      legend=False,
                      grid=False,
                      colormap=coloring.from_list("my_colormap", colors_ABCDE),
                      edgecolor="darkgrey",
                      **kwargs)  # make bar plots

    h,l = axe.get_legend_handles_labels() # get the handles we want to modify
    for i in range(0, n_df * n_col, n_col): # len(h) = n_col * n_df
        for j, pa in enumerate(h[i:i+n_col]):
            for rect in pa.patches: # for each index
                rect.set_y(rect.get_y() + 1 / float(n_df + 2) * i / float(n_col))
                rect.set_hatch(H * int(i / n_col)) #edited part     
                rect.set_height(1 / float(n_df + 2))

    axe.set_xticks(np.arange(0, 125, 20))
    axe.set_xticklabels(np.arange(0, 125, 20).tolist(), rotation = 0)
    axe.margins(x=0, tight=None)
    axe.set_title(title)

    # Add invisible data to add another legend
    n=[]        
    for i in range(n_df):
        n.append(axe.bar(0, 0, color="gray", hatch=H * i, edgecolor="darkgrey"))

    l1 = axe.legend(h[:n_col], l[:n_col], loc=[1.01, 0.5])
    if labels is not None:
        l2 = plt.legend(n, labels, loc=[1.01, 0.1]) 
    axe.add_artist(l1)
    return axe

呼叫

plt.figure(figsize=(10, 4))
plot_clustered_stacked([df_1, df_2],["df_1", "df_2"])
plt.show()

plt.clf()
plt.close()

输出

Plot Output

我认为这个function solution by @jrjc对我的理解来说是相当复杂的,因此,我宁愿稍微改变我自己的东西并调整它

因此,我花了一些时间才明白,当为绘图创建第二个图例时,python会自动删除第一个图例,而此时必须使用add_artist()

添加第二个图例的另一个先决条件是命名绘图,并将.add_artist()方法应用于该特定绘图,以便python知道将新绘图粘贴到何处

简言之,这就是我如何创造我心目中的情节,我希望这些评论能让它变得更清晰,对任何人都有用

import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap as coloring
import matplotlib.patches as mpatches
# copy the dfs below and use pd.read_clipboard() to reproduce
df_1
     A   B   C   D   E
Mg  10  15  23  25  27
Ca  30  33   0  20  17
df_2
     A   B   C   D   E
Mg  20  12   8  40  10
Ca   7  26  12  22  16

hatches=(' ', '//')
colors_ABCDE=['tomato', 'gold', 'greenyellow', 'forestgreen', 'palevioletred']
dfs=[df_1,df_2]
for each_df, df in enumerate(dfs):
    #I name the plot as "figure"
    figure=df.plot(ax=plt.subplot(111), kind="barh", \
            stacked=True, hatch=hatches[each_df], \
            colormap=coloring.from_list("my_colormap", colors_ABCDE), \
            figsize=(7,2.5), position=len(dfs)-each_df-1, \
            align='center', width=0.2, edgecolor="darkgrey", \
            legend=False) #I had to False the legend too
legend_1=plt.legend(df_1.columns, loc='center left', bbox_to_anchor=(1.0, 0.5), fontsize=12)

patch_hatched = mpatches.Patch(facecolor='beige', hatch='///', edgecolor="darkgrey", label='hatched')
patch_unhatched = mpatches.Patch(facecolor='beige', hatch=' ', edgecolor="darkgrey", label='non-hatched')
legend_2=plt.legend(handles=[patch_hatched, patch_unhatched], loc='center left', bbox_to_anchor=(1.15, 0.5), fontsize=12)

# as soon as a second legend is made, the first disappears and needs to be added back again
figure.add_artist(legend_1) #python now knows that "figure" must take the "legend_1" along with "legend_2"

plot with two legends

我敢肯定,它可以更加优雅和自动化

相关问题 更多 >