0

I have an array of temperatures given a certain position.

EG:

20 21 22 23 20
20 22 21 23 20
20 21 20 20 20
20 21 23 23 23
21 21 22 23 22

The top right data point represents the temperature in the top right heatmap, while the bottom left - represents the bottom left temperature of the heatmap I want to produce.

So taken these data points how would I produce a HeatMap where higher temperatures are more RED and cooler are more blue.

Should I first convert the temperatures into RGB and then somehow plot the RGB? how can I do this?

EDIT: Top right (in this case 20) of the array or [0,0] represents the temperature at [0,0] in space. The 2d heatmap I want to produce represents an image infront of a temperature camera. The data points are the temperatures at certain locations. The posting is not too helpful because it is based on frequency and not converting temperature to color and plotting it based on position.

martineau
  • 119,623
  • 25
  • 170
  • 301
sci-guy
  • 2,394
  • 4
  • 25
  • 46

1 Answers1

3

You could use Python's built-in tkinter graphics module to do something like the following. The pseudocolor() function maps a value in given range to a color interpolated along those in a palette of arbitrary color values. The auxiliary colorize() function exists to convert color values returned from it — which consist of three floating-point values in the range 0 to 1 — into the single hex-string form required by tkinter.

Since the selection of colors is controlled by a list of color values, it's fairly easy to tweak how the output looks. The example heatmap is relatively small as is the range of values in it, so the image produced is a little "chunky" looking — but the approach scales well and more attractive results would likely be derived from larger and more varied datasets.

try:
    from Tkinter import *
except ModuleNotFoundError:
    from tkinter import *  # Python 3

heat_map = [[20, 21, 22, 23, 20],
            [20, 22, 21, 23, 20],
            [20, 21, 20, 20, 20],
            [20, 21, 23, 23, 23],
            [21, 21, 22, 23, 22]]

heat_min = min(min(row) for row in heat_map)
heat_max = max(max(row) for row in heat_map)

# Heatmap rgb colors in mapping order (ascending).
palette = (0, 0, 1), (0, .5, 0), (0, 1, 0), (1, .5, 0), (1, 0, 0)

def pseudocolor(value, minval, maxval, palette):
    """ Maps given value to a linearly interpolated palette color. """
    max_index = len(palette)-1
    # Convert value in range minval...maxval to the range 0..max_index.
    v = (float(value-minval) / (maxval-minval)) * max_index
    i = int(v); f = v-i  # Split into integer and fractional portions.
    c0r, c0g, c0b = palette[i]
    c1r, c1g, c1b = palette[min(i+1, max_index)]
    dr, dg, db = c1r-c0r, c1g-c0g, c1b-c0b
    return c0r+(f*dr), c0g+(f*dg), c0b+(f*db)  # Linear interpolation.

def colorize(value, minval, maxval, palette):
    """ Convert value to heatmap color and convert it to tkinter color. """
    color = (int(c*255) for c in pseudocolor(value, minval, maxval, palette))
    return '#{:02x}{:02x}{:02x}'.format(*color)  # Convert to hex string.

root = Tk()
root.title('Heatmap')

# Create and fill canvas with rectangular cells.
width, height = 400, 400  # Canvas size.
rows, cols = len(heat_map), len(heat_map[0])
rect_width, rect_height = width // rows, height // cols
border = 1  # Pixel width of border around each.

canvas = Canvas(root, width=width, height=height)
canvas.pack()
for y, row in enumerate(heat_map):
    for x, temp in enumerate(row):
        x0, y0 = x * rect_width, y * rect_height
        x1, y1 = x0 + rect_width-border, y0 + rect_height-border
        color = colorize(temp, heat_min, heat_max, palette)
        canvas.create_rectangle(x0, y0, x1, y1, fill=color, width=0)

root.mainloop()

Display:

screenshot of output window

martineau
  • 119,623
  • 25
  • 170
  • 301
  • thanks! Is there a way to remove the white spacing between the blocks though? – sci-guy Mar 31 '15 at 20:49
  • 1
    NEVERMIND - delete the -1 :) – sci-guy Mar 31 '15 at 21:00
  • Sorry, one last one. Is there a way to show a side scale of what data point blue corresponds to and what red does. Kinda like a gradient scale you see with temperature maps. thanks! – sci-guy Mar 31 '15 at 21:17
  • Sure, it's possible, except that generally speaking there could be more than two colors involved, plus you'd need some labeling information for each one. You could draw whatever was needed in a separate Canvas object and position that in the same Frame as the one with the heatmap in it. – martineau Mar 31 '15 at 21:26
  • Ya, but blue represents the lower values of data while red represents high. So a gradient would work best. How would I do this?? Not sure where to start. – sci-guy Mar 31 '15 at 21:39
  • There's a couple of ways to draw a color gradient in my answers to the question [_Range values to pseudocolor_](http://stackoverflow.com/q/10901085/355230). – martineau Mar 31 '15 at 22:55
  • thanks! Now the part I struggle with is plotting that into the same figure as the heatmap. How can i place that gradient below it? – sci-guy Mar 31 '15 at 23:23
  • Tkinter has several layout managers: [`grid`](http://effbot.org/tkinterbook/grid.htm), [`pack`](http://effbot.org/tkinterbook/pack.htm), and [`place`](http://effbot.org/tkinterbook/place.htm). In the code in my answer I used `pack` for simplicity. It's hard to say which is best, they each have strengths and weaknesses, and the best one depends on what you're doing. If you drew the gradient onto its own canvas, you could then `pack` it using a `side=` keyword option next to the canvas with the heatmap on it. – martineau Apr 01 '15 at 00:11
  • hmm, ya new to python so what you said is a little bit above me :( Do you think you can edit your code so I can copy paste? please !!! – sci-guy Apr 01 '15 at 01:56