Search code examples
common-lisp

Heatmap a hidden non-feature of :vgplot?


Not being a gnuplot expert (last time I used it was like 25 years ago) and having had some useful applications of Common Lisps :vgplot package, this time I wanted to create a heatmap and - to my surprise - I could not find out, how to do it - even after 2 hours of googling and tinkering.

I use sbcl in the usual emacs-slime combo on a debian system.

Loading vgplot:

(ql:quickload :vgplot)

From reading the sources on github and from googling for heatmap examples for "naked" gnuplot, I deduced, that I probably have to use the (vgplot:surf) function, which uses splot under the hood. And indeed - I see a 3d mesh plot, when running the function bla below:

(defun bla ()
       (let* ((xy-range (vgplot:range 0 10))
          (xx (vgplot:meshgrid-x xy-range xy-range))
          (yy (vgplot:meshgrid-y xy-range xy-range))
          (zz (vgplot:meshgrid-map #'+ xx yy)))
         (vgplot:surf xx yy zz)))

bla result

From reading the gnuplot google results, some "pm3d" thing showed up without explanation and I think I need to use (vgplot:format-plot nil "<something>") to turn the figure into a heatmap but I could not find out, what exactly I need to do.

Hoping I am not the only one using vgplot, I am optimistic, that it is an easy answer for anyone who uses this regularly.


Solution

  • You probably want to use the palette-mapped three-dimensional mode pm3d of gnuplot. This mode maps three-dimensional data to a color palette, and you can set the view to the map option to place the viewpoint above the surface.

    Here is an example from the vgplot documentation which has been adapted to display as a heatmap:

    ;; Adapted from vgplot documentation for SURF function.
    ;; Example 3: Plot a function z = f(x,y), e.g. the sombrero function:
    (defun example-3-heatmap ()
      (let* ((eps double-float-epsilon)
             (fun #'(lambda (x y)
                      (/ (sin (sqrt (+ (* x x) (* y y) eps)))
                         (sqrt (+ (* x x) (* y y) eps)))))
             (x (vgplot:range -8 8 0.2))
             (y (vgplot:range -8 8 0.2))
             (xx (vgplot:meshgrid-x x y))
             (yy (vgplot:meshgrid-y x y))
             (zz (vgplot:meshgrid-map fun xx yy)))
    
        ;; Set color palette:
        (vgplot:format-plot
         nil
         "set palette model RGB defined ( -1 'web-green', 0 'goldenrod', 1 'red' )")
    
        ;; Put gnuplot in pm3d mode:
        (vgplot:format-plot nil "set pm3d")
        (vgplot:format-plot nil "set hidden3d")  ; render opaque surface
        (vgplot:format-plot nil "set view map")  ; view from above
    
        (vgplot:surf xx yy zz)))
    

    Here format-plot is used to send a command to gnuplot that sets up a palette, then format-plot is used to put gnuplot into pm3d mode, rendering an opaque surface viewed from above.

    Philipp Janert has a good book called Gnuplot in Action with a section about creating false-color plots (which are heatmaps).

    Here is sample output from the above example code:

    Example 3 Heatmap