Color Scheme Generator for Charts

Sometimes one needs to show many data series on one chart. In such cases, usually distinction between those data series is achieved by using different colors for each data set. Most charting libraries leave colors selection to developer. This can be problem when there is need to choose more than six colors which can be easily distinguished by most people. Below you can find Python program which can be helpful.

Program generates palette of colors where each color can be easily distinguished from other colors in its neighborhood. Difference between colors is computed using W3C’s formula for color contrast. Minimum contrast (brightness and color), palette size, neighborhood diameter and maximum number of iterations can be easily set in first few lines of program code.

New colors are created using pseudo-random generators and tested for minimum contrast. If contrast test fails, the color is dropped and new one is created. Besides of minimum required contrast, the size of neighborhood has great impact on probability that test will fail. Too big neighborhood diameter could make it impossible to create complete neighborhood and lock the program. To prevent this, there is limit for number of iterations.

#!/usr/bin/python

from random import randint

class Color:
    __c = [0, 0, 0]
    __cB = None
    def __init__(self, r=-1, g=-1, b=-1):
        if r == -1:
            r = randint(1,255)
        if g == -1:
            g = randint(1,255)
        if b == -1:
            b = randint(1,255)
        self.__c = [r, g, b]
    def __getitem__(self, key):
        return self.__c[key]
    def __setitem__(self, key, value):
        self.__c[key] = value
        self.__cB = None
    def getBritness(self):
        if self.__cB:
            return self.__cB
        else:
            self.__cB = ((self.__c[0] * 299) +
                        (self.__c[1] * 587) +
                        (self.__c[2] * 114)) / 1000
            return self.__cB
    def __sub__(self, other):
        return ((max(self.__c[0], other[0]) - min(self.__c[0], other[0])) +
                (max(self[1], other[1]) - min(self[1], other[1])) +
                (max(self[2], other[2]) - min(self[2], other[2])))
    def __str__(self):
        return "%02x%02x%02x" % (self.__c[0], self.__c[1], self.__c[2])

colors = []
num = 25
comparedNum = 5

c = Color()
colors.append(c)
stopper = 100000

minBrit = 35
minColDiff = 100

print "searching...",
for n in range(num)[1:]:
    while stopper:
        newC = Color()
        passed = 0
        for back in range(comparedNum):
            testedNum = n-(back+1)
            if testedNum < 0:
                passed += 1
                continue
            c = colors[testedNum]
            bDiff = abs(c.getBritness() - newC.getBritness())
            cDiff = c - newC
            if  cDiff >= minColDiff and  bDiff > minBrit:
                passed += 1
            else:
                break
        if passed == comparedNum:
            colors.append(newC)
            print "found:", newC,
            break

        stopper -= 1

if not stopper > 0:
    print "searching failed"
else:
    print "finished"


html = """
<head>
<style>
body {
    margin: 0px;
    padding: 0px
}
div {
    width: %dpx;
    height: 100px;
    float: left;
}
</style>
</head>
<body>
""" % (800/n)

python = "colors = ("

for c in colors:
    html += ´<div style="background-color: #%s"></div>\\n´ % c
    python += ´"%s", ´ % c

python = python[:-2] + ")"
html += "</body>"

out = file("colors.html", "w")

print >>out, html
print python

out.close()