2

I am trying to use matplotlib to generate a plot in Python 2.7.

The plot has 6 y-variables - I only need 3 of these to be shown in the legend. For the remaining variables that are to be skipped, I need them to be plot as a common name in the legend. In order to determine which ones to skip in the legend, I have a Python list. eg. for variables A, B, C, D, E, F I need to replace A, C and D with one variable named H so that the legend has H, B, E, F instead of A, B, C, D, E, F.

Also, I need to use user-specified colors with each variable. The user specifies the color for all 6 variables - the user picks the same color for the variables that must be replaced.

Here is the code to show this:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import itertools

def flip(items, ncol):
    return itertools.chain(*[items[i::ncol] for i in range(ncol)])

fig = plt.figure()
ax = plt.subplot(111)

# Generate data:
x = pd.DataFrame(zip(*[np.arange(1,11,1)]*6),index=np.arange(10),columns=list('ABCDEF'))
y = pd.DataFrame(np.random.rand(10,6),columns=list('ABCDEF'))
x.insert(0,'tester_x',0)
y.insert(0,'tester_y',0)

# List of Pandas Dataframe columns to suppress in legend:
suppress = ['A', 'C', 'D','H']

colors = ['lightblue','r','lightblue','lightblue','purple','darkgrey']

labels_want = []
# Plot:
for i in range(1,len(y.columns.tolist())):
    if list(y)[i] in suppress:
        ax.plot(x.iloc[:,i], y.iloc[:,i], color = colors[i-1], linestyle='-', marker='o', linewidth=0.75, label=None)
    else:
        labels_want.append(list(y)[i])
        ax.plot(x.iloc[:,i], y.iloc[:,i], color = colors[i-1], linestyle='-', marker='o', linewidth=0.75, label=list(y)[i])
labels_want.insert(0,suppress[-1])

handles, labels = ax.get_legend_handles_labels()

# Put a legend below current axis
ax.legend(flip(handles, 2), flip(labels_want, 2), loc='best',ncol=2,numpoints=1)

plt.show()

OUTPUT:

enter image description here

This code drops the required variables in the legend and replaces it correctly in the legend. However, the colors are wrong. The colors should be red, purple and dark grey.i.e. there should not be any light blue plot curves. However, in the output, there is lightblue appearing in the legend.

Is there a way to suppress the plot labels A, C, D in the legend and replace them with H in lightblue?

  1. SOURCE for flip: Matplotlib legend, add items across columns instead of down
  2. SOURCE for main plotting code (showing legend outside plot): How to put the legend out of the plot

ADDITIONAL INFORMATION:

In the final legend, the label H should be for the lightblue curves. The labels B, E, F should be for the non-light blue curves. The problem with the current code is that variable E is incorrectly showing up as lightblue.

Community
  • 1
  • 1
edesz
  • 11,756
  • 22
  • 75
  • 123
  • In your line: colors = ['lightblue','r','lightblue','lightblue','purple','darkgrey'] you have lightblue three times, is this intended? – Untitled123 Dec 27 '15 at 15:27
  • Yes, though I should have given a better explanation in the original post. Here is the reasoning: the intention is to specify a color for each y-variable. The same color should be chosen for those variables that will be suppressed in the legend. In the code above, I indicated A, C and D will be suppressed - this would mean that lightblue would occur as the first, 3rd and 4th list elements. The other colors will be for the variables that are not to be suppressed in the legend. – edesz Dec 27 '15 at 17:37
  • What exactly is the issue? The current output looks to be what your code should be doing. – Untitled123 Dec 27 '15 at 17:40
  • Is it an indexing thing, since you are using color=colors[i-1] a lot – Untitled123 Dec 27 '15 at 17:40
  • The problem is that the variable E is currently lightblue. I need the legend to show: H as lightblue, B as red, E as purple and F as darkgrey. – edesz Dec 27 '15 at 17:42
  • Right, the index needs to be i-1 since the first column of the data must be ignored. So the loop starts at 1 instead of 0. But the colors are off... – edesz Dec 27 '15 at 17:43

2 Answers2

0

The reason strange colours are coming up is because this line:

ax.plot(x.iloc[:,i], y.iloc[:,i], color = colors[i-1], linestyle='-', marker='o', linewidth=0.75, label=list(y)[i])

should likely be

    ax.plot(x.iloc[:,i], y.iloc[:,i], color = colors[i], linestyle='-', marker='o', linewidth=0.75, label=list(y)[i])

Notice the index difference on colors. A quick way to debug this is explicitly print out what color[i] is when you set it, and make sure it matches correctly.

Untitled123
  • 1,317
  • 7
  • 20
  • Thanks. But, with this `i` I am getting the following: `IndexError: list index out of range`. I think it is because the color list is out of range. – edesz Dec 27 '15 at 17:47
  • Why does this start from 1? for i in range(1,len(y.columns.tolist())): , if the columns are ABCDEF – Untitled123 Dec 27 '15 at 17:51
  • The length of colors should be 6, as should the "y.columns.tolist()". Might be worth printing that out to see exactly what it is. – Untitled123 Dec 27 '15 at 17:54
0

I think it is worth it for me to describe what I ended up doing because my searching for a clean approach to accomplish this did not prove to be productive.

I generated the plot of the columns I wanted and then I produced the legend.

if list(y)[i] not in suppress:
        ax.plot(x.loc[:,list(y)[i]], y.loc[:,list(y)[i]], color = colors[i-1], linestyle='-', marker='o', linewidth=0.75, label=None)

This way, I simply ignored all the other (unwanted) columns. Then just before plt.show() and AFTER generating the legend, I simply generated the plot of the suppressed columns.

if list(y)[i] in suppress:
        ax.plot(x.loc[:,list(y)[i]], y.loc[:,list(y)[i]], color = colors[i-1], linestyle='-', marker='o', linewidth=0.75, label=None)

This way it completely ignored the suppressed columns in the legend. This gave the effect that I wanted. I had to change iloc() to loc() but that was a minor issue. I am sure there is a better (more elegant) way to get this outcome but I couldn't find information on one.

This hacky approach worked for me and I just went with it because it gave the required outcome.

edesz
  • 11,756
  • 22
  • 75
  • 123