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

Posted

技术标签:

【中文标题】为多个图表设置相同的图例【英文标题】:Set the same legend for multiple charts 【发布时间】:2020-01-31 04:54:34 【问题描述】:

我想在我的“多图表区域”的末尾有一个共同的图例。 “weeks_df_list”是一个熊猫数据框。 我的代码是:


    #https://***.com/questions/41625077/python-pandas-split-a-timeserie-per-month-or-week
    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

到目前为止,我在每个图表上都获得了图例,但我希望在“多图表区域”的底部只有一个图例。 任何想法,输入链接,将不胜感激。 我试过了:

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()

我明白了:

请纠正我。

【问题讨论】:

【参考方案1】:

从-afaik-matplotlib 3.1 开始,您可以使用图例,即像以前一样使用图例,但不能作为pltaxs 的方法,而是使用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])

【讨论】:

所以你会说这是***.com/questions/9834452/…的副本?在这种情况下,您可以直接标记。 fig.legend(loc=8, ncol=2),确实将图例放在图表下方,但仍位于一个图表的区域并复制每个图表的每个图表图例。所以我每个系列有 4 个以上的传奇 @ImportanceOfBeingErnest “所以你会说......?” - 对不起,我不明白。如果您发现重复,您应该标记它,不是吗...?你听起来好像我显然知道那个重复,但仍然创造了一个不利于更好判断的答案...... @NonoLondon 当然:我混淆了索引:不是('_', '')[i>0],而是('', '_')[i>0](或('_', '')[i==0])。抱歉,我会编辑... @SpghttCd 非常感谢,我现在可以使用循环了。非常感谢您的时间。电视【参考方案2】:

不确定这是否对您有用,因为我尚未对其进行测试,但值得一试。


#https://***.com/questions/41625077/python-pandas-split-a-timeserie-per-month-or-week
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()

如果结果不是您想要的,请道歉。

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

from matplotlib.lines import Line2D

#https://***.com/questions/41625077/python-pandas-split-a-timeserie-per-month-or-week
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()

看到你已经发布了数据后,我可以让它在我的最后工作。请参阅下面的附件。这是你想要的吗?请告诉我。

【讨论】:

因为 loc=0 是 pyplot 认为的最佳位置。而不是 fig.legend(loc=0) 使用@SpghttCd 建议您的内容。 loc=8 代表图的底部。 @NonoLondon 我通过添加新答案来编辑答案。请看看它是否有效。由于我无权访问您的数据,因此我没有对其进行测试。 @NonoLondon .. 在某种程度上我可以让它发挥作用。 是的!但在图表下方有图例。 Tx 非常 @NonoLondon 请检查。我编辑了答案和附图。如果这不是您想要的,请不要犹豫,要求再次修改。

以上是关于为多个图表设置相同的图例的主要内容,如果未能解决你的问题,请参考以下文章

excel图表图例怎么设置

07版EXCEL中的图表如何增加图例

在同一页面上的多个图表上将vAxis设置为相同比例

excel的图标如何设置标题、图例

plotly 和 ggplot 图例顺序交互

如何修改Tableau里图例的位置