aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/org/ob-R.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/org/ob-R.el')
-rw-r--r--lisp/org/ob-R.el218
1 files changed, 120 insertions, 98 deletions
diff --git a/lisp/org/ob-R.el b/lisp/org/ob-R.el
index d990d69b357..2be79926a44 100644
--- a/lisp/org/ob-R.el
+++ b/lisp/org/ob-R.el
@@ -5,7 +5,7 @@
5;; Author: Eric Schulte, Dan Davison 5;; Author: Eric Schulte, Dan Davison
6;; Keywords: literate programming, reproducible research, R, statistics 6;; Keywords: literate programming, reproducible research, R, statistics
7;; Homepage: http://orgmode.org 7;; Homepage: http://orgmode.org
8;; Version: 7.01 8;; Version: 7.3
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
11 11
@@ -36,6 +36,9 @@
36(declare-function orgtbl-to-tsv "org-table" (table params)) 36(declare-function orgtbl-to-tsv "org-table" (table params))
37(declare-function R "ext:essd-r" (&optional start-args)) 37(declare-function R "ext:essd-r" (&optional start-args))
38(declare-function inferior-ess-send-input "ext:ess-inf" ()) 38(declare-function inferior-ess-send-input "ext:ess-inf" ())
39(declare-function ess-make-buffer-current "ext:ess-inf" ())
40(declare-function ess-eval-buffer "ext:ess-inf" (vis))
41(declare-function org-number-sequence "org-compat" (from &optional to inc))
39 42
40(defconst org-babel-header-arg-names:R 43(defconst org-babel-header-arg-names:R
41 '(width height bg units pointsize antialias quality compression 44 '(width height bg units pointsize antialias quality compression
@@ -48,21 +51,11 @@
48(defvar org-babel-R-command "R --slave --no-save" 51(defvar org-babel-R-command "R --slave --no-save"
49 "Name of command to use for executing R code.") 52 "Name of command to use for executing R code.")
50 53
51(defun org-babel-expand-body:R (body params &optional processed-params) 54(defun org-babel-expand-body:R (body params)
52 "Expand BODY according to PARAMS, return the expanded body." 55 "Expand BODY according to PARAMS, return the expanded body."
53 (let* ((processed-params (or processed-params 56 (let ((out-file (cdr (assoc :file params))))
54 (org-babel-process-params params))) 57 (mapconcat
55 (vars (mapcar 58 #'identity
56 (lambda (i)
57 (cons (car (nth i (nth 1 processed-params)))
58 (org-babel-reassemble-table
59 (cdr (nth i (nth 1 processed-params)))
60 (cdr (nth i (nth 4 processed-params)))
61 (cdr (nth i (nth 5 processed-params))))))
62 (number-sequence 0 (1- (length (nth 1 processed-params))))))
63 (out-file (cdr (assoc :file params))))
64 (mapconcat ;; define any variables
65 #'org-babel-trim
66 ((lambda (inside) 59 ((lambda (inside)
67 (if out-file 60 (if out-file
68 (append 61 (append
@@ -70,49 +63,36 @@
70 inside 63 inside
71 (list "dev.off()")) 64 (list "dev.off()"))
72 inside)) 65 inside))
73 (append 66 (append (org-babel-variable-assignments:R params)
74 (mapcar 67 (list body))) "\n")))
75 (lambda (pair)
76 (org-babel-R-assign-elisp
77 (car pair) (cdr pair)
78 (equal "yes" (cdr (assoc :colnames params)))
79 (equal "yes" (cdr (assoc :rownames params)))))
80 vars)
81 (list body))) "\n")))
82 68
83(defun org-babel-execute:R (body params) 69(defun org-babel-execute:R (body params)
84 "Execute a block of R code. 70 "Execute a block of R code.
85This function is called by `org-babel-execute-src-block'." 71This function is called by `org-babel-execute-src-block'."
86 (save-excursion 72 (save-excursion
87 (let* ((processed-params (org-babel-process-params params)) 73 (let* ((result-type (cdr (assoc :result-type params)))
88 (result-type (nth 3 processed-params))
89 (session (org-babel-R-initiate-session 74 (session (org-babel-R-initiate-session
90 (first processed-params) params)) 75 (cdr (assoc :session params)) params))
91 (colnames-p (cdr (assoc :colnames params))) 76 (colnames-p (cdr (assoc :colnames params)))
92 (rownames-p (cdr (assoc :rownames params))) 77 (rownames-p (cdr (assoc :rownames params)))
93 (out-file (cdr (assoc :file params))) 78 (out-file (cdr (assoc :file params)))
94 (full-body (org-babel-expand-body:R body params processed-params)) 79 (full-body (org-babel-expand-body:R body params))
95 (result 80 (result
96 (org-babel-R-evaluate 81 (org-babel-R-evaluate
97 session full-body result-type 82 session full-body result-type
98 (or (equal "yes" colnames-p) 83 (or (equal "yes" colnames-p)
99 (org-babel-pick-name (nth 4 processed-params) colnames-p)) 84 (org-babel-pick-name
85 (cdr (assoc :colname-names params)) colnames-p))
100 (or (equal "yes" rownames-p) 86 (or (equal "yes" rownames-p)
101 (org-babel-pick-name (nth 5 processed-params) rownames-p))))) 87 (org-babel-pick-name
88 (cdr (assoc :rowname-names params)) rownames-p)))))
102 (message "result is %S" result) 89 (message "result is %S" result)
103 (or out-file result)))) 90 (or out-file result))))
104 91
105(defun org-babel-prep-session:R (session params) 92(defun org-babel-prep-session:R (session params)
106 "Prepare SESSION according to the header arguments specified in PARAMS." 93 "Prepare SESSION according to the header arguments specified in PARAMS."
107 (let* ((session (org-babel-R-initiate-session session params)) 94 (let* ((session (org-babel-R-initiate-session session params))
108 (vars (org-babel-ref-variables params)) 95 (var-lines (org-babel-variable-assignments:R params)))
109 (var-lines
110 (mapcar
111 (lambda (pair) (org-babel-R-assign-elisp
112 (car pair) (cdr pair)
113 (equal (cdr (assoc :colnames params)) "yes")
114 (equal (cdr (assoc :rownames params)) "yes")))
115 vars)))
116 (org-babel-comint-in-buffer session 96 (org-babel-comint-in-buffer session
117 (mapc (lambda (var) 97 (mapc (lambda (var)
118 (end-of-line 1) (insert var) (comint-send-input nil t) 98 (end-of-line 1) (insert var) (comint-send-input nil t)
@@ -130,6 +110,24 @@ This function is called by `org-babel-execute-src-block'."
130 110
131;; helper functions 111;; helper functions
132 112
113(defun org-babel-variable-assignments:R (params)
114 "Return list of R statements assigning the block's variables"
115 (let ((vars (mapcar #'cdr (org-babel-get-header params :var))))
116 (mapcar
117 (lambda (pair)
118 (org-babel-R-assign-elisp
119 (car pair) (cdr pair)
120 (equal "yes" (cdr (assoc :colnames params)))
121 (equal "yes" (cdr (assoc :rownames params)))))
122 (mapcar
123 (lambda (i)
124 (cons (car (nth i vars))
125 (org-babel-reassemble-table
126 (cdr (nth i vars))
127 (cdr (nth i (cdr (assoc :colname-names params))))
128 (cdr (nth i (cdr (assoc :rowname-names params)))))))
129 (org-number-sequence 0 (1- (length vars)))))))
130
133(defun org-babel-R-quote-tsv-field (s) 131(defun org-babel-R-quote-tsv-field (s)
134 "Quote field S for export to R." 132 "Quote field S for export to R."
135 (if (stringp s) 133 (if (stringp s)
@@ -139,23 +137,25 @@ This function is called by `org-babel-execute-src-block'."
139(defun org-babel-R-assign-elisp (name value colnames-p rownames-p) 137(defun org-babel-R-assign-elisp (name value colnames-p rownames-p)
140 "Construct R code assigning the elisp VALUE to a variable named NAME." 138 "Construct R code assigning the elisp VALUE to a variable named NAME."
141 (if (listp value) 139 (if (listp value)
142 (let ((transition-file (make-temp-file "org-babel-R-import"))) 140 (let ((transition-file (org-babel-temp-file "R-import-")))
143 ;; ensure VALUE has an orgtbl structure (depth of at least 2) 141 ;; ensure VALUE has an orgtbl structure (depth of at least 2)
144 (unless (listp (car value)) (setq value (list value))) 142 (unless (listp (car value)) (setq value (list value)))
145 (with-temp-file (org-babel-maybe-remote-file transition-file) 143 (with-temp-file transition-file
146 (insert (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field))) 144 (insert (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field)))
147 (insert "\n")) 145 (insert "\n"))
148 (format "%s <- read.table(\"%s\", header=%s, row.names=%s, sep=\"\\t\", as.is=TRUE)" 146 (format "%s <- read.table(\"%s\", header=%s, row.names=%s, sep=\"\\t\", as.is=TRUE)"
149 name transition-file 147 name (org-babel-process-file-name transition-file 'noquote)
150 (if (or (eq (nth 1 value) 'hline) colnames-p) "TRUE" "FALSE") 148 (if (or (eq (nth 1 value) 'hline) colnames-p) "TRUE" "FALSE")
151 (if rownames-p "1" "NULL"))) 149 (if rownames-p "1" "NULL")))
152 (format "%s <- %s" name (org-babel-R-quote-tsv-field value)))) 150 (format "%s <- %s" name (org-babel-R-quote-tsv-field value))))
153 151
152(defvar ess-ask-for-ess-directory nil)
154(defun org-babel-R-initiate-session (session params) 153(defun org-babel-R-initiate-session (session params)
155 "If there is not a current R process then create one." 154 "If there is not a current R process then create one."
156 (unless (string= session "none") 155 (unless (string= session "none")
157 (let ((session (or session "*R*")) 156 (let ((session (or session "*R*"))
158 (ess-ask-for-ess-directory (not (cdr (assoc :dir params))))) 157 (ess-ask-for-ess-directory
158 (and ess-ask-for-ess-directory (not (cdr (assoc :dir params))))))
159 (if (org-babel-comint-buffer-livep session) 159 (if (org-babel-comint-buffer-livep session)
160 session 160 session
161 (save-window-excursion 161 (save-window-excursion
@@ -168,6 +168,15 @@ This function is called by `org-babel-execute-src-block'."
168 (buffer-name)))) 168 (buffer-name))))
169 (current-buffer)))))) 169 (current-buffer))))))
170 170
171(defvar ess-local-process-name nil)
172(defun org-babel-R-associate-session (session)
173 "Associate R code buffer with an R session.
174Make SESSION be the inferior ESS process associated with the
175current code buffer."
176 (setq ess-local-process-name
177 (process-name (get-buffer-process session)))
178 (ess-make-buffer-current))
179
171(defun org-babel-R-construct-graphics-device-call (out-file params) 180(defun org-babel-R-construct-graphics-device-call (out-file params)
172 "Construct the call to the graphics device." 181 "Construct the call to the graphics device."
173 (let ((devices 182 (let ((devices
@@ -205,65 +214,78 @@ This function is called by `org-babel-execute-src-block'."
205 214
206(defvar org-babel-R-eoe-indicator "'org_babel_R_eoe'") 215(defvar org-babel-R-eoe-indicator "'org_babel_R_eoe'")
207(defvar org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"") 216(defvar org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"")
208(defvar org-babel-R-wrapper-method "main <- function ()\n{\n%s\n} 217(defvar org-babel-R-write-object-command "{function(object, transfer.file) {invisible(if(inherits(try(write.table(object, file=transfer.file, sep=\"\\t\", na=\"nil\",row.names=%s, col.names=%s, quote=FALSE), silent=TRUE),\"try-error\")) {if(!file.exists(transfer.file)) file.create(transfer.file)})}}(object=%s, transfer.file=\"%s\")")
209write.table(main(), file=\"%s\", sep=\"\\t\", na=\"nil\",row.names=%s, col.names=%s, quote=FALSE)")
210(defvar org-babel-R-wrapper-lastvar "write.table(.Last.value, file=\"%s\", sep=\"\\t\", na=\"nil\",row.names=%s, col.names=%s, quote=FALSE)")
211 218
212(defun org-babel-R-evaluate 219(defun org-babel-R-evaluate
213 (session body result-type column-names-p row-names-p) 220 (session body result-type column-names-p row-names-p)
214 "Pass BODY to the R process in SESSION. 221 "Evaluate R code in BODY."
215If RESULT-TYPE equals 'output then return a list of the outputs 222 (if session
216of the statements in BODY, if RESULT-TYPE equals 'value then 223 (org-babel-R-evaluate-session
217return the value of the last statement in BODY, as elisp." 224 session body result-type column-names-p row-names-p)
218 (if (not session) 225 (org-babel-R-evaluate-external-process
219 ;; external process evaluation 226 body result-type column-names-p row-names-p)))
220 (case result-type 227
221 (output (org-babel-eval org-babel-R-command body)) 228(defun org-babel-R-evaluate-external-process
222 (value 229 (body result-type column-names-p row-names-p)
223 (let ((tmp-file (make-temp-file "org-babel-R-results-"))) 230 "Evaluate BODY in external R process.
224 (org-babel-eval org-babel-R-command 231If RESULT-TYPE equals 'output then return standard output as a
225 (format org-babel-R-wrapper-method 232string. If RESULT-TYPE equals 'value then return the value of the
226 body tmp-file 233last statement in BODY, as elisp."
227 (if row-names-p "TRUE" "FALSE") 234 (case result-type
228 (if column-names-p 235 (value
229 (if row-names-p "NA" "TRUE") 236 (let ((tmp-file (org-babel-temp-file "R-")))
230 "FALSE"))) 237 (org-babel-eval org-babel-R-command
231 (org-babel-R-process-value-result 238 (format org-babel-R-write-object-command
232 (org-babel-import-elisp-from-file 239 (if row-names-p "TRUE" "FALSE")
233 (org-babel-maybe-remote-file tmp-file)) column-names-p)))) 240 (if column-names-p
234 ;; comint session evaluation 241 (if row-names-p "NA" "TRUE")
235 (case result-type 242 "FALSE")
236 (value 243 (format "{function ()\n{\n%s\n}}()" body)
237 (let ((tmp-file (make-temp-file "org-babel-R")) 244 (org-babel-process-file-name tmp-file 'noquote)))
238 broke) 245 (org-babel-R-process-value-result
239 (org-babel-comint-with-output (session org-babel-R-eoe-output) 246 (org-babel-import-elisp-from-file tmp-file '(16)) column-names-p)))
240 (insert (mapconcat 247 (output (org-babel-eval org-babel-R-command body))))
241 #'org-babel-chomp 248
242 (list 249(defun org-babel-R-evaluate-session
243 body 250 (session body result-type column-names-p row-names-p)
244 (format org-babel-R-wrapper-lastvar 251 "Evaluate BODY in SESSION.
245 tmp-file 252If RESULT-TYPE equals 'output then return standard output as a
246 (if row-names-p "TRUE" "FALSE") 253string. If RESULT-TYPE equals 'value then return the value of the
247 (if column-names-p 254last statement in BODY, as elisp."
248 (if row-names-p "NA" "TRUE") 255 (case result-type
249 "FALSE")) 256 (value
250 org-babel-R-eoe-indicator) "\n")) 257 (with-temp-buffer
251 (inferior-ess-send-input)) 258 (insert (org-babel-chomp body))
252 (org-babel-R-process-value-result 259 (let ((ess-local-process-name
253 (org-babel-import-elisp-from-file 260 (process-name (get-buffer-process session))))
254 (org-babel-maybe-remote-file tmp-file)) column-names-p))) 261 (ess-eval-buffer nil)))
255 (output 262 (let ((tmp-file (org-babel-temp-file "R-")))
256 (mapconcat 263 (org-babel-comint-eval-invisibly-and-wait-for-file
257 #'org-babel-chomp 264 session tmp-file
258 (butlast 265 (format org-babel-R-write-object-command
259 (delq nil 266 (if row-names-p "TRUE" "FALSE")
260 (mapcar 267 (if column-names-p
261 #'identity 268 (if row-names-p "NA" "TRUE")
262 (org-babel-comint-with-output (session org-babel-R-eoe-output) 269 "FALSE")
263 (insert (mapconcat #'org-babel-chomp 270 ".Last.value" (org-babel-process-file-name tmp-file 'noquote)))
264 (list body org-babel-R-eoe-indicator) 271 (org-babel-R-process-value-result
265 "\n")) 272 (org-babel-import-elisp-from-file tmp-file '(16)) column-names-p)))
266 (inferior-ess-send-input)))) 2) "\n"))))) 273 (output
274 (mapconcat
275 #'org-babel-chomp
276 (butlast
277 (delq nil
278 (mapcar
279 (lambda (line) ;; cleanup extra prompts left in output
280 (if (string-match
281 "^\\([ ]*[>+][ ]?\\)+\\([[0-9]+\\|[ ]\\)" line)
282 (substring line (match-end 1))
283 line))
284 (org-babel-comint-with-output (session org-babel-R-eoe-output)
285 (insert (mapconcat #'org-babel-chomp
286 (list body org-babel-R-eoe-indicator)
287 "\n"))
288 (inferior-ess-send-input)))) 2) "\n"))))
267 289
268(defun org-babel-R-process-value-result (result column-names-p) 290(defun org-babel-R-process-value-result (result column-names-p)
269 "R-specific processing of return value. 291 "R-specific processing of return value.