0

I want to plot a number of curves for different models. I have curves for different structures. The structures need to have the same color and the different models a different linestyle. I then want 2 legends, one with the colors corresponding to the structures and one with the linestyles. This is the code that I already have:

plt.figure(figsize=(12,8))

styles = ['-', '--']
colors = ['blue', 'orange', 'green', 'red', 'purple', 'brown', 'pink', 'grey']


plans = ['Clinical', 'AP']

for j, plan in enumerate(plans):

    for i, structure in enumerate(structures_dvh):
    # for i, structure in enumerate(structure_test):

        if plan == 'Clinical':

            mean_dose, mean_volume = compute_mean_dvh(Lung_R2_adap, structure)
            plt.plot(mean_dose, mean_volume, label = structure, c = colors[i], ls = styles[j])

        elif plan == 'AP':
           
            mean_dose, mean_volume = compute_mean_dvh(Lung_AP, structure)
            plt.plot(mean_dose, mean_volume, c = colors[i], ls = styles[j])

        elif plan == 'PS':

            mean_dose, mean_volume = compute_mean_dvh(Lung_PS, structure)
            plt.plot(mean_dose, mean_volume, c = colors[i], ls = styles[j])

# plt.legend(handles = linestyle_legend_handles, loc = 'lower left')
plt.legend(loc = 'lower center', bbox_to_anchor=(0.5, -0.25), fancybox=True, shadow=True, ncol=3)

plt.title('Mean DVH')
plt.xlabel('Relative dose')
plt.ylabel('Volume [%]')
plt.grid(True)
plt.tight_layout()

plt.show()
1
  • I see that the answer you received gave a different interpretation wrt the organization of the legends… could you please clarify what is the authentic interpretation? tia
    – gboffi
    Commented May 15 at 13:49

2 Answers 2

1

There are multiple way to go about this (see, e.g., matplotlib: 2 different legends on same graph and this Matplotlib example), but here's a basic example:

from matplotlib import pyplot as plt
import numpy as np

plans = ["Clinical", "AP"]
structures = ["A", "B"]

colours = ["blue", "orange"]
styles = ["-", "--"]

# dictionary to store all your plotted lines
lines = {p: [] for p in plans}

fig, ax = plt.subplots()

N = 10
x = np.linspace(0, 1, N)

for j, plan in enumerate(plans):
    for i, structure in enumerate(structures):
        # plot some fake data
        l, = ax.plot(x, np.random.randn(N) + j * 2 + i, color=colours[i], ls=styles[j])

        # append the plotted lines
        lines[plan].append(l)

locs = ["upper left", "upper right"]

# create legend for each plan (I've put the plan name as a title for the legend)
for i, plan in enumerate(plans):
    leg = ax.legend(lines[plan], structures, title=plan, loc=locs[i])
    ax.add_artist(leg)

enter image description here

1

enter image description here

I took the liberty of reorganizing your code a little bit

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.lines as ml

structures = ['S1','S2','S3','S4']
plans = ['Ma','Mb','Mc']

color = dict(zip(structures, 'rgbm'))
style = dict(zip(plans, 'solid dashed dashdot'.split()))

# plot random stuff, no labels
fig, ax = plt.subplots(layout='constrained')
lw = 3.5
np.random.seed(20240516)
for structure in structures:
    for plan in plans:
        ax.plot(np.random.rand(2), np.random.rand(2),
            color=color[structure], ls=style[plan], lw=lw)

# here we construct  the lists of lines to be used in the legends        
slines = [ml.Line2D([], [], color=c, lw=lw) for c in color.values()]
plines = [ml.Line2D([], [], color="#553300", ls=s, lw=lw) for s in style.values()]

# here we make the legends,
hl = 5 
slegend = ax.legend(slines, structures, loc=5, handlelength=hl, title='Structures')
plegend = ax.legend(plines, plans, loc=4, handlelength=hl, title='Plans')

# Matplotlib accepts only a single legend (the last one)
# so we have to add the first one explicitly
ax.add_artist(slegend)

plt.show()

Not the answer you're looking for? Browse other questions tagged or ask your own question.