Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib - add colorbar to a sequence of line plots

I have a sequence of line plots for two variables (x,y) for a number of different values of a variable z. I would normally add the line plots with legends like this:

import matplotlib.pyplot as plt  fig = plt.figure() ax  = fig.add_subplot(111) # suppose mydata is a list of tuples containing (xs, ys, z)  # where xs and ys are lists of x's and y's and z is a number.  legns = [] for(xs,ys,z) in mydata:    pl = ax.plot(xs,ys,color = (z,0,0))    legns.append("z = %f"%(z)) ax.legends(legns)  plt.show() 

But I have too many graphs and the legends will cover the graph. I'd rather have a colorbar indicating the value of z corresponding to the color. I can't find anything like that in the galery and all my attempts do deal with the colorbar failed. Apparently I must create a collection of plots before trying to add a colorbar.

Is there an easy way to do this? Thanks.

EDIT (clarification):

I wanted to do something like this:

import matplotlib.pyplot as plt import matplotlib.cm     as cm  fig = plt.figure() ax  = fig.add_subplot(111) mycmap = cm.hot # suppose mydata is a list of tuples containing (xs, ys, z)  # where xs and ys are lists of x's and y's and z is a number between 0 and 1 plots = [] for(xs,ys,z) in mydata:    pl = ax.plot(xs,ys,color = mycmap(z))    plots.append(pl) fig.colorbar(plots) plt.show() 

But this won't work according to the Matplotlib reference because a list of plots is not a "mappable", whatever this means.

I've created an alternative plot function using LineCollection:

def myplot(ax,xs,ys,zs, cmap):     plot = lc([zip(x,y) for (x,y) in zip(xs,ys)], cmap = cmap)     plot.set_array(array(zs))     x0,x1 = amin(xs),amax(xs)     y0,y1 = amin(ys),amax(ys)     ax.add_collection(plot)     ax.set_xlim(x0,x1)     ax.set_ylim(y0,y1)     return plot 

xs and ys are lists of lists of x and y coordinates and zs is a list of the different conditions to colorize each line. It feels a bit like a cludge though... I thought that there would be a more neat way to do this. I like the flexibility of the plt.plot() function.

like image 346
Rafael S. Calsaverini Avatar asked Dec 01 '11 14:12

Rafael S. Calsaverini


People also ask

How do I fill multiple lines in Matplotlib?

We can fill an area between multiple lines in Matplotlib using the matplotlib. pyplot. fill_between() method. The fill_between() function fills the space between two lines at a time, but we can select one pair of lines to fill the area between multiple lines.

How do you add a Colorbar to a subplot?

Create a figure and a set of subplots wuth two rows and two columns. Make a list of colormaps. Iterate the axes and create a pseudocolor plot with a non-regular rectangular grid. Make colorbars with the same axes of pcolormesh.


2 Answers

(I know this is an old question but...) Colorbars require a matplotlib.cm.ScalarMappable, plt.plot produces lines which are not scalar mappable, therefore, in order to make a colorbar, we are going to need to make a scalar mappable.

Ok. So the constructor of a ScalarMappable takes a cmap and a norm instance. (norms scale data to the range 0-1, cmaps you have already worked with and take a number between 0-1 and returns a color). So in your case:

import matplotlib.pyplot as plt sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(min=0, max=1)) plt.colorbar(sm) 

Because your data is in the range 0-1 already, you can simplify the sm creation to:

sm = plt.cm.ScalarMappable(cmap=my_cmap) 

Hope that helps somebody.

EDIT: For matplotlib v1.2 or greater the code becomes:

import matplotlib.pyplot as plt sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(vmin=0, vmax=1)) # fake up the array of the scalar mappable. Urgh... sm._A = [] plt.colorbar(sm) 

EDIT: For matplotlib v1.3 or greater the code becomes:

import matplotlib.pyplot as plt sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1)) # fake up the array of the scalar mappable. Urgh... sm._A = [] plt.colorbar(sm) 

EDIT: For matplotlib v3.1 or greater simplifies to:

import matplotlib.pyplot as plt sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1)) plt.colorbar(sm) 
like image 110
pelson Avatar answered Nov 08 '22 16:11

pelson


Here's one way to do it while still using plt.plot(). Basically, you make a throw-away plot and get the colorbar from there.

import matplotlib as mpl import matplotlib.pyplot as plt  min, max = (-40, 30) step = 10  # Setting up a colormap that's a simple transtion mymap = mpl.colors.LinearSegmentedColormap.from_list('mycolors',['blue','red'])  # Using contourf to provide my colorbar info, then clearing the figure Z = [[0,0],[0,0]] levels = range(min,max+step,step) CS3 = plt.contourf(Z, levels, cmap=mymap) plt.clf()  # Plotting what I actually want X=[[1,2],[1,2],[1,2],[1,2]] Y=[[1,2],[1,3],[1,4],[1,5]] Z=[-40,-20,0,30] for x,y,z in zip(X,Y,Z):     # setting rgb color based on z normalized to my range     r = (float(z)-min)/(max-min)     g = 0     b = 1-r     plt.plot(x,y,color=(r,g,b)) plt.colorbar(CS3) # using the colorbar info I got from contourf plt.show() 

It's a little wasteful, but convenient. It's also not very wasteful if you make multiple plots as you can call plt.colorbar() without regenerating the info for it.

enter image description here

like image 36
Boris Avatar answered Nov 08 '22 17:11

Boris