1

When comparing two different Y variables, there is no real way of knowing which chart type belongs to which Y-Axis. I need a legend that says which chart type belongs to which data set.

With help from this site itself I've been able to plot different categorized factors using different chart types, but as you can see there is no way to tell which chart type belongs to which factor/variable Chart

This is the data table(tm_daily_df), and the current code

   report_date shift     UTL_R  Head_Count
0   2019-03-17     A  0.669107          39
1   2019-03-18     A  0.602197          69
2   2019-03-19     A  0.568741          72
3   2019-03-20     A  0.552013          78
4   2019-03-21     A  0.585469          57
5   2019-03-22     A  0.635652          61
6   2019-03-23     A  0.602197          51
7   2019-03-17     1  0.828020          16
8   2019-03-17     2  0.585469           8
9   2019-03-17     3  0.526922          15
10  2019-03-18     1  0.618924          30
11  2019-03-18     2  0.610560          20
12  2019-03-18     3  0.577105          19
13  2019-03-19     1  0.610560          28
14  2019-03-19     2  0.602197          26
15  2019-03-19     3  0.468375          18
16  2019-03-20     1  0.543650          33
17  2019-03-20     2  0.552013          26
18  2019-03-20     3  0.552013          19
19  2019-03-21     1  0.577105          22
20  2019-03-21     2  0.585469          19
21  2019-03-21     3  0.602197          16
22  2019-03-22     1  0.593833          26
23  2019-03-22     2  0.685835          20
24  2019-03-22     3  0.635652          15
25  2019-03-23     1  0.577105          23
26  2019-03-23     2  0.627288          16
27  2019-03-23     3  0.602197          12
fig, ax = plt.subplots(figsize=(15,6))
g = sns.lineplot(x='report_date',  y='UTL_R', data=tm_daily_df, ax=ax, hue = 'shift', legend = None,
             marker='o', markersize=10)
ax2 = ax.twinx()
g = sns.barplot(x='report_date',  y='Head_Count', data=tm_daily_df, ax=ax2, hue='shift',alpha=.5)
ax.set_title('Utilization Ratio vs HeadCount')
plt.show()

I want to have a legend that says which chart type belongs to which data set. In this case, there would be a secondary legend that shows a line and the word "UTL_R" and a square(or whatever would represent a bar graph) next to the word "Head_Count" . I'm also open to any other ideas that can define the applied chart types. Keep in mind this graph is one of many from a huge set of variables, it's not a single instance.

Is there maybe a way i can just put an image/small table into the figure if this is not possible?

ricsilo
  • 105
  • 1
  • 9

1 Answers1

0

tl;dr at the bottom

I recently needed to implement two legends on a project as well. My code is something like:

def plot_my_data(ax, local_zerog, local_oneg, local_maxg):
    # local_zerog list looks like: [local_zerog_dcmdcl_names, local_zerog_dcmdcl_values, local_zerog_time2double_names, local_zerog_time2double_values]
    # the others are structured the same way as well
    mpl.rcParams["lines.markersize"] = 7
    dcmdcl = ax.scatter(local_zerog[0], local_zerog[1], label='Zero G', facecolors='none', edgecolors='b') #dcmdcl
    ax.scatter(local_oneg[0], local_oneg[1], label="One G", facecolors='none', edgecolors='g')
    ax.scatter(local_maxg[0], local_maxg[1], label="Max G", facecolors='none', edgecolors='r')
    ax.tick_params(axis="x", direction="in", top=False, labeltop=False, labelbottom=True)
    ax.tick_params(axis="y", direction="in", right=True)
    labels = ax.get_xticklabels()
    plt.setp(labels, rotation=90, horizontalalignment='center')
    legend1 = ax.legend(loc=1)
    time2double = ax.scatter(local_zerog[2], local_zerog[3], label='Zero G', marker='s', color='b') #time2double
    ax.scatter(local_oneg[2], local_oneg[3], label="One G", marker='s', color='g')
    ax.scatter(local_maxg[2], local_maxg[3], label="Max G", marker='s', color='r')
    ax.plot(local_oneg[0], [0 for _ in local_oneg[0]], color='k')  # line at 0
    ax.plot(local_oneg[2], [0 for _ in local_oneg[2]], color='k')
    ax.legend([dcmdcl, time2double], ["dcmdcl [%]", "time2double [s]"], loc=2)
    plt.gca().add_artist(legend1)

Where I had basically 6 sets of data: 3 for dcmdcl and 3 for time2double. Each has a different color/shape so basically I plotted all of one shape in the lines

dcmdcl = ax.scatter(local_zerog[0], local_zerog[1], label='Zero G', facecolors='none', edgecolors='b') #dcmdcl
ax.scatter(local_oneg[0], local_oneg[1], label="One G", facecolors='none', edgecolors='g')
ax.scatter(local_maxg[0], local_maxg[1], label="Max G", facecolors='none', edgecolors='r')
ax.tick_params(axis="x", direction="in", top=False, labeltop=False, labelbottom=True)
ax.tick_params(axis="y", direction="in", right=True)
labels = ax.get_xticklabels()
plt.setp(labels, rotation=90, horizontalalignment='center')
legend1 = ax.legend(loc=1)

where the last line generates a legend based off the various labels I've assigned. Now to differentiate between the shapes I took one dcmdcl and one time2double and made another legend. The relevant code is:

dcmdcl = ax.scatter(local_zerog[0], local_zerog[1], label='Zero G', facecolors='none', edgecolors='b') #dcmdcl
time2double = ax.scatter(local_zerog[2], local_zerog[3], label='Zero G', marker='s', color='b') #time2double
ax.legend([dcmdcl, time2double], ["dcmdcl [%]", "time2double [s]"], loc=2)

where I basically feed it two specific instances and tell it to create another legend from this information and place it at another location.

tl;dr

It looks like you already have the legend you want for one of the data sets, so now you basically need to run:

legend1 = ax.legend(['put a series of items you want to describe here'], ['put how you would like to title them (needs to be in same order as previous list)'], loc=2)
plt.gca().add_artist(legend1)

I think the order might be important here (I don't remember from when I made it), but if you'll notice my order is:

  • plot some stuff
  • legend1 = ax.legend(loc=1) to make a legend (not plotted yet, just a variable)
  • plot more stuff
  • ax.legend([dcmdcl, time2double], ["dcmdcl [%]", "time2double [s]"], loc=2) (note this is not assigned to a variable this time)
  • plt.gca().add_artist(legend1) now I use the variable made earlier to plot it via add_artist()

My code to generate each ax that is passed into my function above:

fig = plt.figure(figsize=(15, 15))

ax = fig.add_subplot(1, 3, 1)
zerog, oneg, maxg = build_plot_data(lower_mach)
plot_my_data(ax, zerog, oneg, maxg)
ax.set_title("Mach < .7")
Community
  • 1
  • 1
Reedinationer
  • 5,661
  • 1
  • 12
  • 33
  • I tried applying your legend input but i got this error: ValueError: Can not reset the axes. You are probably trying to re-use an artist in more than one Axes which is not supported – ricsilo Apr 04 '19 at 14:37
  • @ricsilo I know that my code does work to generate my plots, so I am not doing anything unsupported. – Reedinationer Apr 04 '19 at 16:52
  • "ValueError: Can not reset the axes. You are probably trying to re-use an artist in more than one Axes which is not supported" was the error message. I wasn't claiming anything else – ricsilo Apr 04 '19 at 17:21
  • Oh. You can code format within comments like `this` with backticks (`) to avoid confusion like that. On all multiple legend plot code I've seen it follows the same format. Like [here](https://riptutorial.com/matplotlib/example/32429/multiple-legends-on-the-same-axes) and [here](https://stackoverflow.com/questions/12761806/matplotlib-2-different-legends-on-same-graph) – Reedinationer Apr 04 '19 at 17:26