Saturday, December 4, 2010

Color lists, again


It's common to want lists of colors for plotting, for heat maps or the RNA secondary structure plot from the other day (here). If you're doing this for a real project, I'd recommend matplotlib's LinearSegmentedColormap, which looks complicated but isn't hard to understand---I posted about it here.

Another option is to get the colors from R, either by getting R to write them to a file on disk, or using RPy. I posted about the former method before too (here). RColorBrewer has some great color schemes.

Finally, we can make our own list. It seems like a nice problem that exercises simple Python skills. The chief goal is flexibility. The core function in the code below assembles a list of tuples of rgb values, where each value is either a constant for the current series, or drawn from the hex values in order (either 00 to FF or reversed FF to 00).

The function's signature is:
series(r=None,g=None,b=None,rev='')

If a value is given for r, g, or b, that's the constant to use. Otherwise, we grab the list. For each of 'r','g', or 'b' that is contained in rev, we reverse the list. The second function simply assembles the color list. To show it in action, we make a list with 5 components: purple to blue, blue to cyan, cyan to green, green to yellow and yellow to red. The results are plotted with matplotlib.

code listing:

import matplotlib.pyplot as plt
import numpy as np

values = '0123456789ABCDEF'
hL = [m+n for m in values for n in values]
rhL = hL[:]
rhL.reverse()
N = len(hL)

# if r,g, or b are given, they are constants
# otherwise we copy hL or rhL
def series(r=None,g=None,b=None,rev=''):
L = list()
for name,c in zip('rgb',[r,g,b]):
if c:
L.append([c] * N)
else:
if name in rev:
temp = rhL[:]
else:
temp = hL[:]
L.append(temp)
return compose_rgb(L)

def compose_rgb(L):
rL = list()
for r,g,b in zip(L[0],L[1],L[2]):
rL.append(''.join(['#',r,g,b]))
return rL

# r decreasing -g +b
cL = series(g='00',b='FF',rev='r')
# -r g increasing +b
cL.extend(series(r='00',b='FF'))
# -r +g b decreasing
cL.extend(series(r='00',g='FF',rev='b'))
# r increasing +g -b
cL.extend(series(g='FF',b='00'))
# +r g decreasing -b
cL.extend(series(r='FF',b='00',rev='g'))

# take only every 8th value, 5 x 32
L = [cL[i] for i in range(0,1280,8)]
xL = range(1,33) * 5
yL = list()
for j in range(5):
yL.extend([j]*32)
plt.scatter(xL,yL,c=L,marker='s',s=200)
plt.savefig('example.png')