1

I have a function that generates up to 4 different plots at once. The legend needs to be saved separately from the plot.

In my code I collect all of the labels and even create some for the peaks in the bar graphs.

I then display them separately but for some reason the plot is coming out blank.

Code:

degree = ' \u2109 '



def generate_graph_image():
    filename = 'test_bar.png'
    legend_file_name = 'legend.png'
    bar = True

    unit = 'Kw'
    time = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

    current_temperatures = [35, 45, 55, 65, 75, 85, 95, 100, 85, 65, 45, 35]
    # list is not always provided
    historic_temperatures = [35, 85,  35, 45, 55, 65, 75, 95, 100, 85, 65, 45,]

    current_readings = [.99, .75, .55, .10, .35, .05, .05, .08, .20, .55, .60, .85]
    # list is not always provided
    historic_readings = [.50, .05, .05, .08, .20,  .75, .55, .10, .35,.45, .65, .49, ]

    swap = True if sum(historic_readings) > sum(current_readings) else False

    time_label = 'Month'

    temp_label = f'Temperatures {degree}'
   
    current_data = {time_label: time, unit: current_readings, temp_label: current_temperatures}
    historic_data = {time_label: time, unit: historic_readings, temp_label: historic_temperatures}

    current_data_frame = pd.DataFrame(current_data)
    historic_data_frame = pd.DataFrame(historic_data)

    fig, current_ax = plt.subplots(figsize=(10, 6))
    current_color = 'blue'
    current_palette = "Reds"
    historic_color = 'black'
    historic_palette = "Blues_r"

    historic_ax = current_ax.twinx()
    historic_ax.axes.xaxis.set_visible(False)
    historic_ax.axes.yaxis.set_visible(False)
    historic_ax.axes.set_ylim(current_ax.axes.get_ylim())

    current_ax.set_xlabel('Time', fontsize=16)
    current_ax.set_ylabel(unit, fontsize=16, )

    current_peak = max(current_readings)
    current_peak_index = current_readings.index(current_peak)

    historic_peak = max(historic_readings)
    historic_peak_index = historic_readings.index(historic_peak)

    current_ax = sns.barplot(ax=current_ax, x=time_label, y=unit, data=current_data_frame, palette=current_palette, color=current_color, )
    current_ax.patches[current_peak_index].set_color('red')
    current_ax.patches[historic_peak_index].set_alpha(0.3)

    historic_ax = sns.barplot(ax=historic_ax, x=time_label, y=unit, data=historic_data_frame, palette=historic_palette,   color=historic_color, alpha=.7)
    historic_ax.patches[historic_peak_index].set_color('black')

    temperature_ax = current_ax.twinx()
    current_color = 'green'
    historic_color = 'orange'

    temperature_ax.set_ylabel(f'Temperature {degree}', fontsize=16,)
    temperature_ax = sns.lineplot(x=time_label, y=temp_label, data=current_data_frame, sort=False, color=current_color)
    temperature_ax.tick_params(axis='y', color=current_color
    temperature_ax = sns.lineplot(x=time_label, y=temp_label, data=historic_data_frame, sort=False, color=historic_color)
    temperature_ax.tick_params(axis='y', color=historic_color)

    plt.style.use('seaborn-poster')
    plt.style.use('ggplot')

    plt.savefig(fname=filename, dpi=200)

    figsize = (2.3, 2.3)
    fig_leg = plt.figure(figsize=figsize)
    fig_leg.set_size_inches(2.3, 2.3, forward=True)
    ax_leg = fig_leg.add_subplot(111)

    current_peak_reading_label = mpatches.Patch(color='red', label=f'Current Peak ({unit})')
    current_reading_label = mpatches.Patch(color='purple', label=f'Current {unit}')
    historic_peak_reading_label = mpatches.Patch(color='pink', label=f'Historic Peak ({unit})')
    historic_reading_label = mpatches.Patch(color='yellow', label=f'Historic {unit}')

    handles, labels = current_ax.get_legend_handles_labels()
    handles += [current_reading_label, current_peak_reading_label, historic_reading_label, historic_peak_reading_label]

    historic_handles, historic_labels = historic_ax.get_legend_handles_labels()
    handles += historic_handles
    labels += historic_labels
    temp_handles, temp_labels = temperature_ax.get_legend_handles_labels()
    handles += temp_handles
    labels += temp_labels
    ax_leg.legend(handles, labels, loc='center', frameon=False)
    # hide the axes frame and the x/y labels
    ax_leg.axis('off')
    fig_leg.savefig(legend_file_name, dpi=200, bbox_inches='tight')

    plt.show()

Output:enter image description here

2
  • Good that you have included a complete runnable example. Could you reduce the code a bit to the point that it would be Minimal, Reproducible Example? Commented Nov 12, 2020 at 17:41
  • @np8 I've simplified the code. Commented Nov 12, 2020 at 17:50

1 Answer 1

1

Fundamentally, unless you use hue, seaborn plots will not render a legend. Therefore, your handles and labels originate empty. Additionally, while the mpatches section populates handles, you keep labels empty and not being equal in length, no legend is rendered in final.

Consider adjustments to your current code by sections (full code in link at bottom). However, below is for demonstration. Adjust process with understanding of above hue and mpatches labels issues.

Data (add period column for hue argument)

current_data_frame = pd.DataFrame(current_data).assign(period='current')
#    Month    Kw  Temperatures  ℉   period
# 0    Jan  0.99                35  current
# 1    Feb  0.75                45  current
# 2    Mar  0.55                55  current
# 3    Apr  0.10                65  current
# 4    May  0.35                75  current
# 5   June  0.05                85  current
# 6   July  0.05                95  current
# 7    Aug  0.08               100  current
# 8    Sep  0.20                85  current
# 9    Oct  0.55                65  current
# 10   Nov  0.60                45  current
# 11   Dec  0.85                35  current

historic_data_frame = pd.DataFrame(historic_data).assign(period='historic')
#    Month    Kw  Temperatures  ℉    period
# 0    Jan  0.50                35  historic
# 1    Feb  0.05                85  historic
# 2    Mar  0.05                35  historic
# 3    Apr  0.08                45  historic
# 4    May  0.20                55  historic
# 5   June  0.75                65  historic
# 6   July  0.55                75  historic
# 7    Aug  0.10                95  historic
# 8    Sep  0.35               100  historic
# 9    Oct  0.45                85  historic
# 10   Nov  0.65                65  historic
# 11   Dec  0.49                45  historic

Barplot (call .get_legend().remove() to remove from original plot)

current_ax = sns.barplot(ax=current_ax, x=time_label, y=unit, hue='period', data=current_data_frame, palette=current_palette, color=current_color, )
current_ax.patches[current_peak_index].set_color('red')
current_ax.patches[historic_peak_index].set_alpha(0.3)
current_ax.get_legend().remove()

historic_ax = sns.barplot(ax=historic_ax, x=time_label, y=unit, hue='period', data=historic_data_frame, palette=historic_palette, color=historic_color, alpha=.7)
historic_ax.patches[historic_peak_index].set_color('black')
historic_ax.get_legend().remove()

Line Plots (call .get_legend().remove() to remove from original plot)

temperature_ax.set_ylabel(f'Temperature {degree}', fontsize=16,)

temperature_ax = sns.lineplot(x=time_label, y=temp_label, hue='period', data=current_data_frame, sort=False, palette=['g'])
temperature_ax.tick_params(axis='y', color=current_color)
temperature_ax.get_legend().remove()

temperature_ax = sns.lineplot(x=time_label, y=temp_label, hue='period', data=historic_data_frame, sort=False, palette=['orange'])
temperature_ax.get_legend().remove()
temperature_ax.tick_params(axis='y', color=historic_color)

Mpatches (add label for each handle)

#... same mpatches code ...

handles, labels = current_ax.get_legend_handles_labels()

# ADD LABEL TO CORRESPOND TO HANDLE
labels += ['current_reading_label', 'current_peak_reading_label', 'historic_reading_label', 'historic_peak_reading_label']
handles += [current_reading_label, current_peak_reading_label, historic_reading_label, historic_peak_reading_label]

historic_handles, historic_labels = historic_ax.get_legend_handles_labels()
handles += historic_handles
labels += historic_labels

temp_handles, temp_labels = temperature_ax.get_legend_handles_labels()
handles += [temp_handles[1]] + [temp_handles[3]]   # SKIP 1ST AND 3RD ITEMS (LEGEND TITLE, 'period')
labels += [temp_labels[1]] + [temp_labels[3]]      # SKIP 1ST AND 3RD ITEMS (LEGEND TITLE, 'period')

Full Code


Legend Plot (colors/palette need adjustment)

Legend Plot

Bar and Line Plot (colors/palette need adjustment)

Bar and Line Plot

Sign up to request clarification or add additional context in comments.

7 Comments

which version of seaborn and matplotlib are you using? I'm getting a ValueError for the hue argument.
nevermind! My mistake. This works perfectly thank you!
Is there a way to update the value handles? Example after once I get the temp_handles variable, how can I update to display "current temperature" instead "current"?
Simply assign period column in data frame as 'current temperature' instead of 'current'. By the way, with hue, you can combine the two similarly structured data frames (pd.concat([current_data_frame, historic_data_frame])) for a single barplot with dodge = False argument and single lineplot call. Adjust palettes as needed.
If I were to update he value of period="Current Temperature" then the bar charts would also say Temperature. Bar chart needs to say Current kw while line plot says Current Temperature
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.