0

I need to compute tuples (of integers) of arbitrary (but the same) length into RGB values. It would be especially nice if I could have them ordered more-or-less by magnitude, with any standard way of choosing sub-ordering between (0,1) and (1,0).

Here's how I'm doing this now:

  1. I have a long list of RGB values of colors.

    colors = [(0,0,0),(255,255,255),...]
    
  2. I take the hash of the tuple mod the number of colors, and use this as the index.

    def tuple_to_rgb(atuple):
        index = hash(atuple) % len(colors)
        return colors[index]
    

This works OK, but I'd like it to work more like a heatmap value, where (5,5,5) has a larger value than (0,0,0), so that adjacent colors make some sense, maybe getting "hotter" as the values get larger.

I know how to map integers onto RGB values, so perhaps if I just had a decent way of generating a unique integer from a tuple that sorted first by the magnitude of the tuple and then by the interior values it might work.

I could simply write my own sort comparitor, generate the entire list of possible tuples in advance, and use the order in the list as the unique integer, but it would be much easier if I didn't have to generate all of the possible tuples in advance.

Does anyone have any suggestions? This seems like something do-able, and I'd appreciate any hints to push me in the right direction.

For those who are interested, I'm trying to visualize predictions of electron occupations of quantum dots, like those in Fig 1b of this paper, but with arbitrary number of dots (and thus an arbitrary tuple length). The tuple length is fixed in a given application of the code, but I don't want the code to be specific to double-dots or triple-dots. Probably won't get much bigger than quadruple dots, but experimentalists dream up some pretty wild systems.

Community
  • 1
  • 1
Rick
  • 1,784
  • 3
  • 15
  • 25
  • Why not simply sort the list of tuples? A custom sort key returning the magnitude (avg value of the 3 components) and the tuple itself would do here. – Martijn Pieters May 11 '14 at 00:02
  • @Martijn Yes, that's what I was getting at in the paragraph starting with "I could simply...". It's probably the only way to do everything I want. The advantage to my hash() based approach is that I don't need the entire list of possible tuples in advance. But I don't really see an alternative. – Rick May 11 '14 at 00:05
  • @MartijnPieters This wasn't as hard to implement as I feared. (1) I created an indexing function of all possible occupations. (2) I then "decorated" the list with the sum of the tuple, and sorted and then unpacked the list using the standard decorate-sort-undecorate pattern. (3) I then took the index of any given tuple in the list and passed it into matplotlib's colormaps. Works pretty well with only a few minutes of work. – Rick May 11 '14 at 00:41
  • There is no need for a decorate-sort-undecorate; use a `key` callable for the `sorted()` function or `.sort()` method instead. – Martijn Pieters May 11 '14 at 00:44
  • @MartijnPieters Yep, that worked. Thanks! I'm going to wait a few hours to see whether any cool suggestions come in, but, if they don't, I'll add my code for this as the answer. – Rick May 11 '14 at 00:53
  • What's the magnitude of a tuple of `n` values? – martineau May 11 '14 at 01:42
  • @martineau I'm using sum() now, but pretty much any norm would work. – Rick May 11 '14 at 02:50

2 Answers2

0

Here's the code I came up with:

class Colormapper:
    """
    Create a colormap to map tuples onto RGBA values produced by matplolib's
    cmap function.

    Arguments are the maximum value of each place in the tuple. Dimension of
    the tuple is inferred from the length of the args array.
    """
    def __init__(self,*args):
        from itertools import product
        import matplotlib.pyplot as plt

        self.occs = sorted(list(product(*[xrange(arg+1) for arg in args])),key=sum)
        self.n = float(len(self.occs))
        self.hotmap = plt.get_cmap('hot')
        return

    def __call__(self,occ):
        ind255 = int(255*self.occs.index(occ)/self.n)
        return self.hotmap(ind255)

Here's an example of the result of this code:

double dot stability diagram

Rick
  • 1,784
  • 3
  • 15
  • 25
0

Here's an alternative method. Since the dots I've generated so far only have a subset of the possible occupations, the color maps were skewed one way, and didn't look as good. This method requires a list of possible states to be passed in, and thus these must be generated in advance, but the resulting colormaps look much nicer.

class Colormapper2:
    """
    Like Colormapper, but uses a list of possible occupations to
    generate the maps, rather than generating all possible occupations.
    The difference is that the systems I've explored only have a subset
    of the possible states occupied, and the colormaps look better 
    this way.
    """
    def __init__(self,occs,**kwargs):
        import matplotlib.pyplot as plt
        colormap = kwargs.get('colormap','hot')
        self.occs = sorted(list(occs),key=sum)
        self.n = float(len(self.occs))
        self.cmap = plt.get_cmap(colormap)
        return

    def __call__(self,occ):
        ind255 = int(255*self.occs.index(occ)/self.n)
        return self.cmap(ind255)

Here's an example of the resulting image:

double dot stability diagram

You can see the colors are better separated than the other version.

Rick
  • 1,784
  • 3
  • 15
  • 25