Как получить две легенды, используя график pandas, одну для цветов столбцов с накоплением и одну для штрихов штрихов? - PullRequest
0 голосов
/ 21 апреля 2020

Я пытался понять ответ этого поста , чтобы заполнить две разные легенды.

Я создаю кластеризованный столбчатый график с разными штриховками для каждого столбца и моего кода Ниже немного отличается от ответа вышеупомянутого поста.

Но я не смог выяснить, как получить одну легенду с цветами и одну легенду с люками.

цветовая легенда должна соответствовать 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)

Сюжет I удалось получить это:

plot

Есть идеи, как создать две легенды и расположить их одну рядом с другой или одну под другой? Заранее спасибо ^ _ ^

Ответы [ 2 ]

1 голос
/ 04 мая 2020

Я думал, что это функциональное решение от @ jrj c довольно озадачивает мое понимание, и поэтому я предпочел немного изменить свою вещь и настроить ее.

Так Мне потребовалось некоторое время, чтобы понять, что, когда для сюжета создается вторая легенда, python автоматически стирает первую, и в этот момент необходимо использовать add_artist().

Другая необходимая предпосылка для добавьте вторую легенду для присвоения имени графику и примените метод .add_artist () к этому заданному c графику, чтобы 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

Я почти уверен, что он может быть еще более элегантным и автоматизированным.

1 голос
/ 21 апреля 2020

Поскольку добавление легенд в matplotlib является сложным, обширным шагом, рассмотрите возможность использования той самой ссылки, которую вы цитируете с помощью решения функции от @ jrj c. Тем не менее, вам нужно будет настроить функцию в соответствии с вашими потребностями горизонтальной гистограммы. В частности:

  • Добавьте аргумент для цветовой карты и в DataFrame.plot вызовите
  • Настройте линейный график от kind='bar' до kind='barh' для горизонтальной версии
  • Swap x для y в строке: rect.set_y(rect.get_y() + 1 / float(n_df + 1) * i / float(n_col))
  • Обмен width для height в строке: rect.set_height(1 / float(n_df + 1))
  • Настройка axe.set_xticks и axe.set_xticklabels для np.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

...