Skip to content

Instantly share code, notes, and snippets.

@jefftrull
Created August 14, 2024 17:53
Show Gist options
  • Save jefftrull/eece0fd0bf85f2846bc6a58bf0f9de75 to your computer and use it in GitHub Desktop.
Save jefftrull/eece0fd0bf85f2846bc6a58bf0f9de75 to your computer and use it in GitHub Desktop.
Presentation on custom plot types for org-mode
#+TITLE: Creating a Custom Plot Type for org-mode
#+AUTHOR: Jeff Trull
* Introduction
- Motivated by last month's discussion of PLOT
- I wanted to understand the semantic meaning of PLOT for my Keynote backend
- Ihor pointed out the newly released "sectors" feature in gnuplot
- makes it theoretically possible to implement things like pie charts that Keynote has
* Basic PLOT usage
Let's grab some data from NOAA (US weather and climate agency):
#+PLOT: title:"Contiguous U.S. June Average Temperature" ind:1 deps:(3) set:"ytics 0.5"
#+PLOT: set:"xtics 5" set:"grid" with:"linespoints pointtype 19" file:"temps.png"
| Year | Fahrenheit | Celsius |
|------+------------+---------|
| 1970 | 68.72 | 20.4 |
| 1971 | 69.42 | 20.8 |
| 1972 | 68.18 | 20.1 |
| 1973 | 68.94 | 20.5 |
| 1974 | 68.47 | 20.3 |
| 1975 | 67.50 | 19.7 |
| 1976 | 67.78 | 19.9 |
| 1977 | 70.32 | 21.3 |
| 1978 | 68.92 | 20.5 |
| 1979 | 67.80 | 19.9 |
| 1980 | 68.72 | 20.4 |
| 1981 | 69.80 | 21.0 |
| 1982 | 66.31 | 19.1 |
| 1983 | 67.06 | 19.5 |
| 1984 | 68.59 | 20.3 |
| 1985 | 68.09 | 20.1 |
| 1986 | 70.39 | 21.3 |
| 1987 | 70.23 | 21.2 |
| 1988 | 70.84 | 21.6 |
| 1989 | 68.11 | 20.1 |
| 1990 | 70.21 | 21.2 |
| 1991 | 69.04 | 20.6 |
| 1992 | 67.41 | 19.7 |
| 1993 | 67.19 | 19.6 |
| 1994 | 70.93 | 21.6 |
| 1995 | 67.53 | 19.7 |
| 1996 | 69.84 | 21.0 |
| 1997 | 68.29 | 20.2 |
| 1998 | 68.11 | 20.1 |
| 1999 | 68.29 | 20.2 |
| 2000 | 69.01 | 20.6 |
| 2001 | 69.21 | 20.7 |
| 2002 | 70.84 | 21.6 |
| 2003 | 68.02 | 20.0 |
| 2004 | 68.05 | 20.0 |
| 2005 | 69.19 | 20.7 |
| 2006 | 70.74 | 21.5 |
| 2007 | 69.85 | 21.0 |
| 2008 | 69.58 | 20.9 |
| 2009 | 68.50 | 20.3 |
| 2010 | 70.47 | 21.4 |
| 2011 | 69.80 | 21.0 |
| 2012 | 70.52 | 21.4 |
| 2013 | 70.38 | 21.3 |
| 2014 | 69.57 | 20.9 |
| 2015 | 71.33 | 21.9 |
| 2016 | 71.74 | 22.1 |
| 2017 | 70.32 | 21.3 |
| 2018 | 71.53 | 22.0 |
| 2019 | 68.70 | 20.4 |
| 2020 | 70.27 | 21.3 |
| 2021 | 72.57 | 22.5 |
| 2022 | 70.79 | 21.6 |
| 2023 | 68.99 | 20.6 |
| 2024 | 71.82 | 22.1 |
#+TBLFM:$3=(($2-32)*5/9);%.1f
[[./temps.png]]
* Sector Support in gnuplot
The introduction of "sectors" in gnuplot 6 has made a rich variety of plot types possible. See [[http://www.gnuplot.info/demo/sectors.html]] for details.
The feature is still on a relatively low level, however. Users must supply for a sector (analogous to a rectangle in Cartesian coordinates) the initial and final radius, the initial angle, and the angular extent.
* Solution: Custom Plot Type
The "type" of the above plot is the default - /2d/. Special ELisp code associated with the plot's type is run when the plot is generated. We want to make our own type and code for pie charts, using sectors.
We can take values from a column in a table, and apportion fractions of a full circle to each.
** The gnuplot "stats" command
gnuplot can perform useful computations on a column of data. We are interested in the sum, for calculating fractions of a circle:
#+begin_src gnuplot
stats "data" using 2 nooutput; # calculate statistics on column 2 of the input
#+end_src
#+RESULTS:
This sets various global variables, including ~STATS_sum~
** The gnuplot "using" clause
gnuplot processes its input data through a ~using~ clause that allows for:
- arbitrary calculations
- variables that can be updated on each row of data
For example, we can calculate angular extents in a pie chart as a fraction of a column total, multiplied by 2*pi. We can sum each extent as it is produced into a variable, so we know where to start the next sector:
#+begin_src gnuplot
# for sectors "using" supplies the initial angle, the start radius, the angular extent,
# the final radius, and the color.
# annular_extent and last_angle are variables
# $0 gives the current line number and is used to produce different colors
using (last_angle):(0):(annular_extent=(2*pi*$2/STATS_sum),last_angle=last_angle+annular_extent,annular_extent):(1):($0+1)
#+end_src
* Putting it all Together
Custom plot types require adding a special structure to ~org-plot/preset-plot-types~. My pie chart implementation required creating two functions: one, ~jet/pie-chart-pre~, for general setup, and one to make the plot itself
** Plot Code
#+begin_src elisp
(defun jet/pie-chart-func (table data-file num-cols params plot-str)
(let ((ind (plist-get params :ind))
(deps (plist-get params :deps))
(labels (plist-get params :labels)))
(list
(concat
(format "\"%s\" " data-file)
(format "using (last_angle):(0):(annular_extent=(2*pi*$%d/STATS_sum),last_angle=last_angle+annular_extent,annular_extent):(1):($0+1) \\\n"
(car deps))
"with sectors units xx lc variable fill solid, \\\n"
"last_angle=0 "
(format "\"%s\" " data-file)
(format "using (annular_extent=(2*pi*$%d/STATS_sum),last_angle+annular_extent/2):(last_angle=last_angle+annular_extent, 1.1):%d \\\n"
(car deps) ind)
"with labels"))))
#+end_src
** Plot Setup
#+begin_src elisp
(defun jet/pie-chart-pre (table data-file num-cols params plot-str)
(let ((amounts-col (car (plist-get params :deps))))
(concat
(format "stats \"%s\" using %s nooutput;" data-file amounts-col)
"set yrange [-1.2 : 1.2];set polar;"
"unset raxis;set size ratio -1 1,1;unset rtics;unset border;unset xtics;unset ytics;unset key;"
"set theta clockwise top;"
"last_angle=0")))
#+end_src
** Creating the Plot Type
#+begin_src elisp :results none
(setq jet/pie-chart-plot-type
'(pie :plot-cmd "plot"
:plot-func (lambda (table data-file num-cols params plot-str)
(jet/pie-chart-func table data-file num-cols params plot-str))
:plot-pre (lambda (table data-file num-cols params plot-str)
(jet/pie-chart-pre table data-file num-cols params plot-str))))
(add-to-list 'org-plot/preset-plot-types jet/pie-chart-plot-type)
#+end_src
* Usage
#+PLOT: title:"Someone who is good at the economy help me budget this"
#+PLOT: type:pie ind:1 deps:(2) file:"dril.png"
| Category | Cost |
|----------+------|
| Food | 200 |
| Data | 150 |
| Rent | 800 |
| Candles | 3600 |
| Utility | 150 |
[[./dril.png]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment