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