forked from mirrors/org-mode
org-plot.el: add utility functions for range,ticks
* lisp/org-plot.el (org-plot/add-options-to-plist): Add the options :ymin :ymax :xmin :xmax, as well as :min and :max as aliases to the y{min,max} options. The :ticks option is also added, for specifying how many ticks should be used. (org--plot/values-stats, org--plot/sensible-tick-num, org--plot/nice-frequency-pick, org--plot/merge-alists, org--plot/item-frequencies, org--plot/prime-factors): New utility functions added to allow for somewhat sensible determination of a :ticks value when none is provided. This turns out to be harder than expected, and so a number of functions are used to attempt to do so. The essence of the method used, is to round values and find their prime decompositions. From this we try to select the most common components to give a reasonable step size. We also add a 'ticks' parameter for manually setting the number of ticks, and (y)min/max parameters similarly.
This commit is contained in:
parent
bee7ef6f46
commit
73c99bf42d
100
lisp/org-plot.el
100
lisp/org-plot.el
|
@ -64,6 +64,11 @@ Returns the resulting property list."
|
||||||
("map" . :map)
|
("map" . :map)
|
||||||
("timeind" . :timeind)
|
("timeind" . :timeind)
|
||||||
("timefmt" . :timefmt)
|
("timefmt" . :timefmt)
|
||||||
|
("min" . :ymin)
|
||||||
|
("max" . :ymax)
|
||||||
|
("ymin" . :ymin)
|
||||||
|
("xmax" . :xmax)
|
||||||
|
("ticks" . :ticks)
|
||||||
("trans" . :transpose)
|
("trans" . :transpose)
|
||||||
("transpose" . :transpose)))
|
("transpose" . :transpose)))
|
||||||
(multiples '("set" "line"))
|
(multiples '("set" "line"))
|
||||||
|
@ -182,6 +187,101 @@ and dependent variables."
|
||||||
(setf back-edge "") (setf front-edge ""))))
|
(setf back-edge "") (setf front-edge ""))))
|
||||||
row-vals))
|
row-vals))
|
||||||
|
|
||||||
|
(defun org--plot/values-stats (nums &optional hard-min hard-max)
|
||||||
|
"From a list of NUMS return a plist containing some rudamentry statistics on the
|
||||||
|
values, namely regarding the range."
|
||||||
|
(let* ((minimum (or hard-min (apply #'min nums)))
|
||||||
|
(maximum (or hard-max (apply #'max nums)))
|
||||||
|
(range (- maximum minimum))
|
||||||
|
(rangeOrder (ceiling (- 1 (log10 range))))
|
||||||
|
(range-factor (expt 10 rangeOrder))
|
||||||
|
(nice-min (/ (float (floor (* minimum range-factor))) range-factor))
|
||||||
|
(nice-max (/ (float (ceiling (* maximum range-factor))) range-factor)))
|
||||||
|
`(:min ,minimum :max ,maximum :range ,range
|
||||||
|
:range-factor ,range-factor
|
||||||
|
:nice-min ,nice-min :nice-max ,nice-max :nice-range ,(- nice-max nice-min))))
|
||||||
|
|
||||||
|
(defun org--plot/sensible-tick-num (table &optional hard-min hard-max)
|
||||||
|
"From a the values in a TABLE of data, attempt to guess an appropriate number of ticks."
|
||||||
|
(let* ((row-data
|
||||||
|
(mapcar (lambda (row) (org--plot/values-stats
|
||||||
|
(mapcar #'string-to-number (cdr row))
|
||||||
|
hard-min
|
||||||
|
hard-max)) table))
|
||||||
|
(row-normalised-ranges (mapcar (lambda (r-data)
|
||||||
|
(let ((val (round (*
|
||||||
|
(plist-get r-data :range-factor)
|
||||||
|
(plist-get r-data :nice-range)))))
|
||||||
|
(if (= (% val 10) 0) (/ val 10) val)))
|
||||||
|
row-data))
|
||||||
|
(range-prime-decomposition (mapcar #'org--plot/prime-factors row-normalised-ranges))
|
||||||
|
(weighted-factors (sort (apply #'org--plot/merge-alists #'+ 0
|
||||||
|
(mapcar (lambda (factors) (org--plot/item-frequencies factors t))
|
||||||
|
range-prime-decomposition))
|
||||||
|
(lambda (a b) (> (cdr a) (cdr b))))))
|
||||||
|
(apply #'* (org--plot/nice-frequency-pick weighted-factors))))
|
||||||
|
|
||||||
|
(defun org--plot/nice-frequency-pick (frequencies)
|
||||||
|
"From a list of frequences, try to sensibly pick a sample of the most frequent."
|
||||||
|
;; TODO this mosly works decently, but counld do with some tweaking to work more consistently.
|
||||||
|
(case (length frequencies)
|
||||||
|
(1 (list (car (nth 0 frequencies))))
|
||||||
|
(2 (if (<= 3 (/ (cdr (nth 0 frequencies))
|
||||||
|
(cdr (nth 1 frequencies))))
|
||||||
|
(make-list 2
|
||||||
|
(car (nth 0 frequencies)))
|
||||||
|
(list (car (nth 0 frequencies))
|
||||||
|
(car (nth 1 frequencies)))))
|
||||||
|
(t
|
||||||
|
(let* ((total-count (apply #'+ (mapcar #'cdr frequencies)))
|
||||||
|
(n-freq (mapcar (lambda (freq) `(,(car freq) . ,(/ (float (cdr freq)) total-count))) frequencies))
|
||||||
|
(f-pick (list (car (car n-freq))))
|
||||||
|
(1-2-ratio (/ (cdr (nth 0 n-freq))
|
||||||
|
(cdr (nth 1 n-freq))))
|
||||||
|
(2-3-ratio (/ (cdr (nth 1 n-freq))
|
||||||
|
(cdr (nth 2 n-freq))))
|
||||||
|
(1-3-ratio (* 1-2-ratio 2-3-ratio))
|
||||||
|
(1-val (car (nth 0 n-freq)))
|
||||||
|
(2-val (car (nth 1 n-freq)))
|
||||||
|
(3-val (car (nth 2 n-freq))))
|
||||||
|
(when (> 1-2-ratio 4) (push 1-val f-pick))
|
||||||
|
(when (and (< 1-2-ratio 2-val)
|
||||||
|
(< (* (apply #'* f-pick) 2-val) 30))
|
||||||
|
(push 2-val f-pick))
|
||||||
|
(when (and (< 1-3-ratio 3-val)
|
||||||
|
(< (* (apply #'* f-pick) 3-val) 30))
|
||||||
|
(push 3-val f-pick))
|
||||||
|
f-pick))))
|
||||||
|
|
||||||
|
(defun org--plot/merge-alists (function default alist1 alist2 &rest alists)
|
||||||
|
"Using FUNCTION, combine the elements of all given ALISTS. When an element is
|
||||||
|
only present in one alist, DEFAULT is used as the second argument for the FUNCTION."
|
||||||
|
(when (> (length alists) 0)
|
||||||
|
(setq alist2 (apply #'org--plot/merge-alists function default alist2 alists)))
|
||||||
|
(flet ((keys (alist) (mapcar #'car alist))
|
||||||
|
(lookup (key alist) (or (cdr (assoc key alist)) default)))
|
||||||
|
(loop with keys = (union (keys alist1) (keys alist2) :test 'equal)
|
||||||
|
for k in keys collect
|
||||||
|
(cons k (funcall function (lookup k alist1) (lookup k alist2))))))
|
||||||
|
|
||||||
|
(defun org--plot/item-frequencies (values &optional normalise)
|
||||||
|
"Return an alist indicating the frequency of values in VALUES list."
|
||||||
|
(let ((normaliser (if normalise (float (length values)) 1)))
|
||||||
|
(cl-loop for (n . m) in (seq-group-by #'identity values)
|
||||||
|
collect (cons n (/ (length m) normaliser)))))
|
||||||
|
|
||||||
|
(defun org--plot/prime-factors (value)
|
||||||
|
"Return the prime decomposition of VALUE, e.g. for 12, '(3 2 2)"
|
||||||
|
(let ((factors '(1)) (i 1))
|
||||||
|
(while (/= 1 value)
|
||||||
|
(setq i (1+ i))
|
||||||
|
(when (eq 0 (% value i))
|
||||||
|
(push i factors)
|
||||||
|
(setq value (/ value i))
|
||||||
|
(setq i (1- i))
|
||||||
|
))
|
||||||
|
(subseq factors 0 -1)))
|
||||||
|
|
||||||
(defcustom org-plot/gnuplot-script-preamble ""
|
(defcustom org-plot/gnuplot-script-preamble ""
|
||||||
"String or function which provides content to be inserted into the GNUPlot
|
"String or function which provides content to be inserted into the GNUPlot
|
||||||
script before the plot command. Not that this is in addition to, not instead of
|
script before the plot command. Not that this is in addition to, not instead of
|
||||||
|
|
Loading…
Reference in a new issue