为多个图表设置相同的图例

Set the same legend for multiple charts

我希望在我的 "multi chart area" 结尾有一个共同的图例。 "weeks_df_list" 是一个 pandas 数据框。 我的代码是:


    #
    weeks_df_list = [g for n, g in daily_data_df.groupby(_pd.Grouper(key='Transaction Date', freq='W'))]

    for my_df in weeks_df_list:
        my_df['day_of_the_week'] = my_df['Transaction Date'].dt.weekday_name
        my_df.set_index(keys=['day_of_the_week'], drop=True, inplace=True)

    fig, axs = plt.subplots(number_of_charts, 1, sharex=True, figsize=[8, 17])

    # Adjust horizontal space between axes
    fig.subplots_adjust(hspace=.5)
    for i in range(number_of_charts):
        print("i:", i)
        #axs[i].set_yticks(np.arange(-0.9, 1.0, 0.4))
        #axs[i].set_ylim(-1, 1)
        #axs[i] = weeks_df_list[i]['pct_daily_vol'].multiply(100).round(1).plot(label='% Daily Volumes')
        #percent daily
        axs[i].plot(weeks_df_list[i]['pct_daily_vol'].multiply(100).round(2), label='% Daily Volumes',
                     color='blue')
        axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())
        axs[i].legend(loc=2)
        #percent daily max
        axs[i].plot(weeks_df_list[i]['pct_daily_limit'].multiply(100).round(2), label='% Daily Limit',
                     color='orange')
        axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())
        axs[i].legend(loc=0)


        #secondary axis
        axs_2 = axs[i].twinx()
        axs_2.plot(weeks_df_list[i]['vwap'], label='VWAP Paid', color='green')
        axs_2.legend(loc=3)


        #comon variables
        axs[i].set_yticks(_np.arange(0, 100, 20))
        axs[i].set_ylim(0, 100)
        axs[i].set_title('Week:' + str(i + 1))
        axs[i].grid(True)


    plt.show()

我的数据是:

day_of_the_week;Transaction Date;Volume;vwap;mylow;myhigh;myopen;myclose;myvolume;20d_vol_avg;25%_limit;pct_daily_vol;pct_daily_limit
Monday;2019-09-02;35807;53.24725612310441;52.9;54.0;53.75;53.0;192570;246338.0;61584.0;0.18594277405618737;0.5814334892179787
Tuesday;2019-09-03;51200;52.923418945312505;52.75;53.25;53.25;53.1;231631;241551.0;60388.0;0.22104122505191448;0.847850566337683
Wednesday;2019-09-04;45100;52.97544235033262;52.5;53.4;53.35;53.0;220595;243379.0;60845.0;0.20444706362338222;0.7412277097542938
Thursday;2019-09-05;59000;51.50618474576272;51.2;52.0;51.65;51.55;740694;246378.0;61594.0;0.07965502623215524;0.9578855083287333
Friday;2019-09-06;59100;51.47736971235195;50.95;52.0;51.6;51.4;512996;273752.0;68438.0;0.1152055766516698;0.8635553347555451
Monday;2019-09-09;59100;51.450917935702215;51.15;51.7;51.2;51.25;215956;290220.0;72555.0;0.27366685806367963;0.8145544759148232
Tuesday;2019-09-10;60900;50.00561674876848;49.38;51.25;51.25;50.25;418767;289580.0;72395.0;0.14542693192156975;0.8412183161820568
Wednesday;2019-09-11;60800;50.00684062500002;49.56;50.45;50.45;49.7;335791;296832.0;74208.0;0.18106500769824088;0.8193186718413109
Thursday;2019-09-12;60800;50.0199384868421;49.66;50.3;49.88;50.2;241223;305352.0;76338.0;0.2520489339739577;0.7964578584715345
Friday;2019-09-13;60600;50.20141881188121;49.9;50.45;50.05;50.0;221205;292716.0;73179.0;0.27395402454736556;0.828106423974091
Monday;2019-09-16;61200;49.713364379084986;49.14;50.1;50.05;49.26;268788;293007.0;73252.0;0.22768873610429036;0.8354720690220062
Tuesday;2019-09-17;61300;49.60541109298533;48.96;50.2;49.26;50.0;364572;293632.0;73408.0;0.16814236968280613;0.8350588491717524
Wednesday;2019-09-18;60800;50.02049095394736;49.64;50.2;49.92;50.1;207805;304150.0;76038.0;0.2925819879213686;0.7996001999000499
Thursday;2019-09-19;60500;50.27256446280997;50.05;50.45;50.25;50.3;191168;304872.0;76218.0;0.3164755607633077;0.7937757485108504
Friday;2019-09-20;60700;50.136443822075755;49.86;50.35;50.1;50.3;375839;298466.0;74616.0;0.1615053254185968;0.8134984453736464
Monday;2019-09-23;60500;50.228577685950434;49.86;50.45;49.86;50.1;212277;296375.0;74094.0;0.2850049699213763;0.8165303533349529
Tuesday;2019-09-24;37295;50.85666282343475;49.9;51.3;49.9;51.3;348997;301849.0;75462.0;0.10686338277979467;0.49422225756009647
Wednesday;2019-09-25;39000;50.91075897435897;50.55;51.4;50.85;51.25;357430;305476.0;76369.0;0.10911227373191953;0.5106784166350221
Thursday;2019-09-26;22300;51.8501143497758;51.2;52.2;51.2;52.0;484304;312316.0;78079.0;0.04604545905051373;0.2856081660881927
Friday;2019-09-27;22300;51.96707174887891;51.4;52.3;51.95;52.15;111409;325248.0;81312.0;0.2001633620264072;0.27425226288862653

到目前为止,我在每张图表上都得到了图例,但我希望 "multichart area" 底部只有一个图例。 任何想法,输入链接,将不胜感激。 我试过了:

Click

和其他一些人,但显然我遗漏了一些东西。

我清理了一些照片。 所以尝试使用@SpghttCd :

    fig.subplots_adjust(hspace=.5)
    for i, ax in enumerate(axs):
        print("i:", i)
        #percent daily
        axs[i].plot(weeks_df_list[i]['pct_daily_vol'].multiply(100).round(2), label=('_', '')[i>0] + '% Daily Volumes',
                     color='blue')
        axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())
        #percent daily max
        axs[i].plot(weeks_df_list[i]['pct_daily_limit'].multiply(100).round(2), label=('_', '')[i>0] + '% Daily Limit',
                     color='orange')
        axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())
        #secondary axis
        axs_2 = axs[i].twinx()
        axs_2.plot(weeks_df_list[i]['vwap'], label=('_', '')[i>0] + 'VWAP Paid', color='green')

        #comon variables
        axs[i].set_yticks(_np.arange(0, 100, 20))
        axs[i].set_ylim(0, 100)
        axs[i].set_title('Week:' + str(i + 1))
        axs[i].grid(True)

    fig.legend(loc=8, ncol=3)
    plt.tight_layout(rect=[0, .05, 1, 1])
    plt.show()

我得到:

请指正。

从-afaik-matplotlib 3.1开始你可以使用图例,即像以前一样使用图例而不是plt的方法或你的axs 但是 fig:

fig.legend()

在您的底部居中和三列示例中,例如:

fig.legend(loc=8, ncol=3)

关于重叠:
legend 只能创建一个图例并将其放置在任何地方,为了防止重叠,您需要 plt.tight_layout() 具有适当的 rect 值,例如

plt.tight_layout(rect=[0, .05, 1, 1])

对于重复项:
legend 收集所有标记的图,所以这当然是为什么像上面那样在循环中创建图时会出现重复的原因。

但是,您可以使用一个不错的小功能来防止这种情况发生:规定的下划线禁止将标签添加到图例中,例如label='VWAP Paid' 不会出现在图例中。
知道这一点,您可以根据循环的计数器添加下划线,例如:

label=('', '_')[i>0] + 'VWAP Paid'

顺便说一句,你真的应该考虑使用

for i, ax in enumerate(axs):    # ...(axs.flatten()): if you would have several rows _and_ colmns

而不是

for i in range(number_of_charts):

它打开了写 ax 而不是 axs[i] 的机会,但如果你需要它仍然提供 i 作为计数器变量 (例如添加下划线除了在第一个循环中... :) )


编辑:

这将是您的代码和我的建议:

fig, axs = plt.subplots(len(weeks_df_list), sharex=True, figsize=[8, 17])

fig.subplots_adjust(hspace=.5)
for i, (ax, df) in enumerate(zip(axs, weeks_df_list)):
    print("i:", i)
    #percent daily
    ax.plot(df['pct_daily_vol'].multiply(100).round(2), label=('', '_')[i>0] + '% Daily Volumes', color='blue')
    ax.yaxis.set_major_formatter(mtick.PercentFormatter())
    #percent daily max
    ax.plot(df['pct_daily_limit'].multiply(100).round(2), label=('', '_')[i>0] + '% Daily Limit', color='orange')
    ax.yaxis.set_major_formatter(mtick.PercentFormatter())
    #secondary axis
    axs_2 = ax.twinx()
    axs_2.plot(df['vwap'], label=('', '_')[i>0] + 'VWAP Paid', color='green')

    #comon variables
    ax.set_yticks(np.arange(0, 100, 20))
    ax.set_ylim(0, 100)
    ax.set_title('Week:' + str(i + 1))
    ax.grid(True)

fig.legend(loc=8, ncol=3)
plt.tight_layout(rect=[0, .05, 1, 1])

不确定这是否适合你,因为我还没有测试过但值得一试。


#
weeks_df_list = [g for n, g in daily_data_df.groupby(_pd.Grouper(key='Transaction Date', freq='W'))]

for my_df in weeks_df_list:
    my_df['day_of_the_week'] = my_df['Transaction Date'].dt.weekday_name
    my_df.set_index(keys=['day_of_the_week'], drop=True, inplace=True)

fig, axs = plt.subplots(number_of_charts, 1, sharex=True, figsize=[8, 17])

# Adjust horizontal space between axes
fig.subplots_adjust(hspace=.5)
for i in range(number_of_charts):
    print("i:", i)
    #axs[i].set_yticks(np.arange(-0.9, 1.0, 0.4))
    #axs[i].set_ylim(-1, 1)
    #axs[i] = weeks_df_list[i]['pct_daily_vol'].multiply(100).round(1).plot(label='% Daily Volumes')
    #percent daily
    axs[i].plot(weeks_df_list[i]['pct_daily_vol'].multiply(100).round(2), label='% Daily Volumes',
                 color='blue')
    axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())
    #percent daily max
    axs[i].plot(weeks_df_list[i]['pct_daily_limit'].multiply(100).round(2), label='% Daily Limit',
                 color='orange')
    axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())


    #secondary axis
    axs_2 = axs[i].twinx()
    axs_2.plot(weeks_df_list[i]['vwap'], label='VWAP Paid', color='green')


    #comon variables
    axs[i].set_yticks(_np.arange(0, 100, 20))
    axs[i].set_ylim(0, 100)
    axs[i].set_title('Week:' + str(i + 1))
    axs[i].grid(True)

fig.legend(loc=0)


plt.show()

如果结果不是您想要的,我们深表歉意。

在看到问题作者的评论后,我正在编辑 post 并添加一条建议。这个想法是删除所有 pyplot 编码的图例并制作我们自己的图例。请注意顶部的必要导入(Line2D)。请将其添加到顶部的代码中。

from matplotlib.lines import Line2D

#
weeks_df_list = [g for n, g in daily_data_df.groupby(_pd.Grouper(key='Transaction Date', freq='W'))]

for my_df in weeks_df_list:
    my_df['day_of_the_week'] = my_df['Transaction Date'].dt.weekday_name
    my_df.set_index(keys=['day_of_the_week'], drop=True, inplace=True)

fig, axs = plt.subplots(number_of_charts, 1, sharex=True, figsize=[8, 17])

# Adjust horizontal space between axes
fig.subplots_adjust(hspace=.5)
for i in range(number_of_charts):
    print("i:", i)
    #axs[i].set_yticks(np.arange(-0.9, 1.0, 0.4))
    #axs[i].set_ylim(-1, 1)
    #axs[i] = weeks_df_list[i]['pct_daily_vol'].multiply(100).round(1).plot()
    #percent daily
    axs[i].plot(weeks_df_list[i]['pct_daily_vol'].multiply(100).round(2),color='blue')
    axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())
    #percent daily max
    axs[i].plot(weeks_df_list[i]['pct_daily_limit'].multiply(100).round(2),color='orange')
    axs[i].yaxis.set_major_formatter(mtick.PercentFormatter())


    #secondary axis
    axs_2 = axs[i].twinx()
    axs_2.plot(weeks_df_list[i]['vwap'],color='green')


    #comon variables
    axs[i].set_yticks(_np.arange(0, 100, 20))
    axs[i].set_ylim(0, 100)
    axs[i].set_title('Week:' + str(i + 1))
    axs[i].grid(True)

Manual_Legends = [Line2D([0],[0],color='blue',label='% Daily Volumes'),Line2D([0],[0],color='orange',label='% Daily Volumes'),Line2D([0],[0],color='green',label='VWAP Paid')]
plt.legend(handles=Manual_Legends,loc='lower center',,bbox_to_anchor=(0.5,-0.35),ncol=3,title='Legend of the plot')
plt.show()

看到您已经post编辑了数据,我可以让它在我这边工作。请参阅下面的附件。这是你想要的吗?请告诉我。