Question

I am trying to graph families of curves using Matplotlib. I am graphing the data directly using scatter() and then plotting a fit line (least squares from scipy) using plot(). I do not know how many sets of data there will be beforehand, or the limits, etc.

I need to be able to cycle the colors of these lines and points so everything from one set of data matches. Plot rotates colors using some internal default and scatter is coming out as all one color. The data sets could get close together, so just going with the assumption that it will be clear from which points are close to which fit line is not good enough, and since I don't know how many curves there will be manually making a color choice is not scalable.

Further, because these are families of curves (think transistor plots), I need to be able to show the relevant labeling with the curve. What I would like to do is to write the information on the fit line itself.

Does anyone know of a good way to either of these?

Was it helpful?

Solution

This tries to answer all your questions. The code below cycles a maximum of 7 colors. If you need more you should create a more sofisticated generator, as that shown in another answer.

import numpy as np
from matplotlib import pyplot as plt

def get_color():
    for item in ['r', 'g', 'b', 'c', 'm', 'y', 'k']:
        yield item

x = 0.3 * np.array(range(40))

color = get_color()

for group in range(5):
    # generates a collection of points
    y = np.exp2(x + 0.5 * group)
    # fit to a polynomial
    z = np.polyfit(x, y, 6)
    p = np.poly1d(z)

    acolor = next(color)
    
    plt.scatter(x, y, color=acolor, marker='o')
    plt.plot(x, p(x), acolor + '-', label=str(group))

plt.legend()
plt.xlim((0, 15))
plt.show() 

enter image description here

The generator in the above code is a bit of an overkilling for the example, but it gives the structure for a more complex calculation. If you only need a few colors you could use a simple iterator

>>> color = iter(list_of_colors)
>>> acolor = next(color)

and if you need to cycle endlessly, you can use itertools.cycle:

>>> from itertools import cycle
>>> color = cycle(['r', 'g', 'b', 'c', 'm', 'y', 'k'])
>>> next(color)
'r'
>>> 

Edit: You have several options to get n diferent colors. As I indicated before you can use a generator using the method indicated in other answer. For example, replacing get_color with a different generator:

import colorsys
import numpy as np
from matplotlib import pyplot as plt

def get_color(color):
    for hue in range(color):
        hue = 1. * hue / color
        col = [int(x) for x in colorsys.hsv_to_rgb(hue, 1.0, 230)]
        yield "#{0:02x}{1:02x}{2:02x}".format(*col)
    
x = 0.3 * np.array(range(40))

color = get_color(15)

for group in range(15):
    # generates a collection of points
    y = np.exp2(x + 0.5 * group)
    # fit to a polynomial
    z = np.polyfit(x, y, 6)
    p = np.poly1d(z)

    acolor = next(color)

    plt.scatter(x, y, color=acolor, marker='o')
    plt.plot(x, p(x), color=acolor, linestyle='dashed', label=str(group))

plt.legend()
plt.xlim((0, 15))
plt.show() 

You get 15 different colors.

enter image description here

Similar colors are however contiguous not giving a good resolution/contrast. You can increase contrast by skipping hue values with:

for hue in range(0, color*3, 3):

The other problem when drawing many lines is the legend...

OTHER TIPS

I have a similar case where I want to give the same color to multiple lines while still supporting an arbitrary number of lines without defining them all manually. This is a function I came up with to generate colors:

import colorsys

def get_colors(i, total):
    hue = i*(1.0/total)
    dark = [int(x) for x in colorsys.hsv_to_rgb(hue, 1.0, 100)]
    light = [int(x) for x in colorsys.hsv_to_rgb(hue, 1.0, 230)]
    return "#{0:02x}{1:02x}{2:02x}".format(*dark), "#{0:02x}{1:02x}{2:02x}".format(*light)

As you can see, it generates total colors with maximum distance in a dark and a light version.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top