| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846 | ;;; org-R.el --- Numerical computation and data visualisation for org-mode using R;; Copyright (C) 2009;;   Free Software Foundation, Inc.;; Author: Dan Davison <davison@stats.ox.ac.uk>;; Keywords: org, R, ESS, tables, graphics;; Homepage: http://www.stats.ox.ac.uk/~davison/software/org-R;; Version: 0.05 2009-02-05;;;; This file is not part of GNU Emacs.;;;; This file is free software; you can redistribute it and/or modify;; it under the terms of the GNU General Public License as published by;; the Free Software Foundation; either version 3, or (at your option);; any later version.;; This file is distributed in the hope that it will be useful,;; but WITHOUT ANY WARRANTY; without even the implied warranty of;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the;; GNU General Public License for more details.;; You should have received a copy of the GNU General Public License;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Commentary:;; This file allows R (http://www.r-project.org) code to be applied to;; emacs org-mode (http://orgmode.org) tables. When the;; result of the analysis is a vector or matrix, it is output back;; into the org-mode buffer as a new org table. Alternatively the R;; code may be used to plot the data in the org table. It requires R to be;; running in an inferior-ess-mode buffer (install Emacs Speaks;; Statistics http://ess.r-project.org and issue M-x R). ;; ;;;; The user interface is via two different options lines in the org;; buffer. As is conventional in org-mode, these are lines starting;; with `#+'. Lines starting with #+R: specify options in the;; standard org style (option:value) and are used to specify certain;; off-the-shelf transformations and plots of the table data. The;; #+R: line is also used to specify the data to be analysed;; (either an org table or a csv file), and to restrict the analysis;; to certain columns etc. In lines starting #+RR: you can supply;; literal R code, giving you full control over what you do with the;; table. With point in the first #+R line, M-x org-R-apply;; makes happen whatever has been specified in those lines. ;; The best documentation is currently the Worg tutorial:;;;; http://orgmode.org/worg/org-tutorials/org-R/org-R.php(defconst org-R-skeleton-funcall-1-arg  "%s(x[%s]%s)"  "Skeleton of a call to an R function.E.g. barplot(x[,3:5], names.arg=rownames(x))")(defconst org-R-skeleton-funcall-2-args  "%s(x[,%s], x[,%s]%s)"  "Skeleton of a call to an R function which can take x and y  args.")(defconst org-R-write-org-table-def  "write.org.table <- function (x, write.rownames = TRUE) {    if(!is.null(dim(x)) && length(dim(x)) > 2)        stop(\"Object must be 1- or 2-dimensional\") ;    if(is.vector(x) || is.table(x) || is.factor(x) || is.array(x))         x <- as.matrix(x) ;    if(!(is.matrix(x) || inherits(x, c('matrix', 'data.frame')))) {       invisible() ;       print(x) ;       stop(\"Object not recognised as 1- or 2-dimensional\") ;    } ;    if(is.null(colnames(x)))         colnames(x) <- rep('', ncol(x)) ;    if(write.rownames)         x <- cbind(rownames(x), x) ;    cat('|', paste(colnames(x), collapse = ' | '), '|\\n') ;    cat('|', paste(rep('----', ncol(x)), collapse = '+'), '|\\n', sep = '') ;    invisible(apply(x, 1, function(row) cat('|', paste(row, collapse = ' | '), '|\\n'))) ;}"  "Definition of R function to write org table representation of R objects.To see a more human-readable version of this, look at the code,or type dput(write.org.table) RET at the R (inferior-ess-modebuffer) prompt.")(defun org-R-apply ()  "Construct and evaluate an R function call.Construct an R function corresponding to the #+R: and #+RR:lines. R must be currently running in an inferior-ess-modebuffer. The function evaluates any user-supplied R code in the#+RR: line before the off-the-shelf actions specified in the #+R:line. The user-supplied R code can operate on a variable called xthat is the org table represented as a data frame in R. Textoutput from the R process may be inserted into the org buffer, asan org table where appropriate."  (interactive)  (require 'ess)  (save-excursion     (beginning-of-line)    (unless (looking-at "#\\+RR?:") (error "Point must be in a #+R or #+RR line"))    (while (looking-at "#\\+RR?:") (forward-line -1))    (forward-line)    ;; For the rest of the code in this file we are based at the    ;; beginning of the first #+R line    ;; FIXME: if point is at the beginning of the #+RR? lines when    ;; this function is called, then tabular output gets inserted,    ;; leaving point up at the top of the tabular output.    (let* ((options (org-R-get-options))	   (code (org-R-construct-code options))	   (infile (plist-get options :infile))	   (ext (if infile (file-name-extension infile)))	   csv-file)      (if (string-equal ext "csv")	  (setq csv-file infile)	(setq csv-file	      (org-R-export-to-csv	       (make-temp-file "org-R-tmp" nil ".csv") options)))            (org-R-eval code csv-file options)      (delete-other-windows) ;; FIXME      (if (plist-get options :showcode) (org-R-showcode code)))))(defun org-R-apply-to-subtree ()  "Call org-R-apply in every org-R block in current subtree."  ;; This currently relies on re-search-forward leaving point after  ;; the #+RR?: If point were at the beginning of the line, then  ;; tabular input would get inserted leaving point above the #+RR?:,  ;; and this would loop infinitely. Same for org-R-apply-to-buffer.  (interactive)  (save-excursion    (org-back-to-heading)    (while (re-search-forward 	    "^#\\+RR?:"	    (save-excursion (org-end-of-subtree)) t)      (org-R-apply)      (forward-line)      (while (looking-at "#\\+RR?")	(forward-line)))))(defun org-R-apply-to-buffer ()  "Call org-R-apply in every org-R block in the buffer."  (interactive)  (save-excursion    (goto-char (point-min))    (while (re-search-forward "^#\\+RR?:" nil t)      (org-R-apply)      (forward-line)      (while (looking-at "#\\+RR?")	(forward-line)))))  (defun org-R-construct-code (options)  "Construct the R function that implements the requestedbehaviour. The body of this function derives from two sources:1. Explicit R code which is read from lines starting with#+RR: by org-R-get-user-code, and2. Off-the-shelf code corresponding to options specified in the#+R: line. This code is constructed byorg-R-off-the-shelf-code."  (let ((user-code (org-R-get-user-code))	(action (plist-get options :action)))    (if (or (eq action 'tabulate) (eq action 'transpose))	(setq options (plist-put options :output-to-buffer t)))    (format "function(x){%sx}"	    (concat	     (when user-code (concat user-code ";"))	     (when action (concat (org-R-off-the-shelf-code options) ";"))))))(defun org-R-get-user-code (&optional R)  "Read user-supplied R code from #+RR: lines"  (let ((case-fold-search t))    (save-excursion      (while (looking-at "^#\\+\\(RR?:\\) *\\(.*\\)")	(if (string= "RR:" (match-string 1))	    (setq R (concat R (when R ";") (match-string 2))))	(forward-line))))  R)(defun org-R-off-the-shelf-code (options)  "Return R code implementing the actions requested in the#+R: lines."    ;; This is a somewhat long function as it deals with several  ;; different cases, corresponding to all the off-the-shelf actions  ;; that have been implemented.    (let* ((action (plist-get options :action))	 (cols (plist-get options :columns))	 (ncols (org-R-number-of-columns cols))	 (nxcols (nth 0 ncols))	 (nycols (nth 1 ncols))	 (cols-R (org-R-make-index-vectors cols))	 (xcols-R (nth 0 cols-R))	 (ycols-R (nth 1 cols-R))	 seq args largs extra-code title colour matrix-index)        ;; I want this to affect options outside this function.  Will it    ;; necessarily do so? (not if plist-put adds to head of the    ;; plist?)    (setq options (plist-put options :nxcols nxcols))        (cond ((eq action 'points)	   (setq action 'plot)	   (setq options (plist-put options :lines nil)))	  ((eq action 'lines)	   (setq action 'plot)	   (setq options (plist-put options :lines t))))        (if (and (setq title (plist-get options :title)) (symbolp title))	(setq title symbol-name title))        (setq args (plist-put args :main (concat "\"" title "\"")))        (if (setq colour (or (plist-get options :colour)			 (plist-get options :color)			 (plist-get options :col)))	(setq args	      (plist-put args :col			 (concat "\"" (if (symbolp colour) (symbol-name colour) colour) "\""))))          (setq largs	  (if (setq legend (plist-get options :legend))	      (plist-put largs :x			 (concat "\"" (if (symbolp legend) (symbol-name legend) legend) "\""))	    (plist-put largs :x "\"topright\"")))    (cond     ((null ycols-R)      ;; single set of columns; implicit x values      (if (null xcols-R)	  (setq xcols-R "" matrix-index "")	(setq matrix-index (concat "," xcols-R)))      (cond       ;;----------------------------------------------------------------------       ((eq action 'barplot)	(if (eq nxcols 1)	    (progn	      (setq args (plist-put args :names.arg "rownames(x)"))	      (setq args (org-R-set-user-supplied-args args (plist-get options :args)))	      (format org-R-skeleton-funcall-1-arg		      "barplot" xcols-R		      (concat ", " (org-R-plist-to-R-args args))))	  	  (setq args (plist-put args :names.arg "colnames(x)"))	  (setq args (plist-put args :col "seq(nrow(x))"))	  (setq args (plist-put args :beside "TRUE"))	  (setq largs (plist-put largs :bty "\"n\""))	  ;; (setq largs (plist-put largs :lwd 10))	  (setq largs (plist-put largs :col "seq(nrow(x))"))	  (setq largs (plist-put largs :legend "rownames(x)"))	  	  (setq args (org-R-set-user-supplied-args args (plist-get options :args)))	  	  (concat (format org-R-skeleton-funcall-1-arg 			  "barplot(as.matrix" matrix-index			  (concat "), " (org-R-plist-to-R-args args)))		  "; legend(" (org-R-plist-to-R-args largs) ")")))       ;;----------------------------------------------------------------------       ((eq action 'density)	(if (and nxcols (> nxcols 1))	  (error "Multiple columns not implemented for action:%s" action))	(setq args (plist-put args :xlab (concat "colnames(x)["xcols-R"]")))	(setq args (org-R-set-user-supplied-args args (plist-get options :args)))	(format org-R-skeleton-funcall-1-arg		"plot(density" matrix-index		(concat "), " (org-R-plist-to-R-args args))))       ;;----------------------------------------------------------------------       ((eq action 'hist)	(if (and nxcols (> nxcols 1))	  (error "Multiple columns not implemented for action:%s" action))	(setq args (plist-put args :xlab (concat "colnames(x)["xcols-R"]")))	(setq args (org-R-set-user-supplied-args args (plist-get options :args)))	(setq args (concat ", " (org-R-plist-to-R-args args)))	(format org-R-skeleton-funcall-1-arg "hist" matrix-index args))              ;;----------------------------------------------------------------------       ((eq action 'image)	(format org-R-skeleton-funcall-1-arg "image(as.matrix" matrix-index ")"))       ;;----------------------------------------------------------------------       ((eq action 'plot)	(setq seq (concat "seq_along("xcols-R")"))	(setq args (plist-put args :type (if (plist-get options :lines) "\"l\"" "\"p\"")))	(setq args (plist-put args :ylab (concat "colnames(x)["xcols-R"]")))	(setq args (concat ", " (org-R-plist-to-R-args args)))	(concat (format org-R-skeleton-funcall-1-arg			(if (eq nxcols 1) "plot" "matplot") matrix-index args)		extra-code))	       ;;----------------------------------------------------------------------       ((eq action 'tabulate)	(concat	 (if (plist-get options :sort)	     (format org-R-skeleton-funcall-1-arg		     "x <- sort(table" xcols-R "), decreasing=TRUE")	   (format org-R-skeleton-funcall-1-arg "x <- table" matrix-index ""))	 (if (eq nxcols 1) "; x <- data.frame(value=names(x), count=x[])")))              ;;----------------------------------------------------------------------       ((eq action 'transpose)	(format org-R-skeleton-funcall-1-arg "x <- t" matrix-index ""))            ;;----------------------------------------------------------------------              ;; Don't recognise action: option, try applying it as the name of an R function.              (t (format org-R-skeleton-funcall-1-arg		  (concat "x <- " (symbol-name action)) matrix-index ""))))          ;;----------------------------------------------------------------------     (ycols-R      ;; x and y columns specified      (cond              ;;----------------------------------------------------------------------              ((eq action 'plot)	(unless (eq nxcols 1) (error "Multiple x-columns not implemented for action:plot"))		(setq args	      (plist-put	       args :ylab	       (concat "if(length("ycols-R") == 1) colnames(x)["ycols-R"] else ''")))	(setq args (plist-put args :xlab (concat "colnames(x)["xcols-R"]")))	(setq args (plist-put args :type (if (plist-get options :lines) "\"l\"" "\"p\"")))		(setq args (concat ", " (org-R-plist-to-R-args args)))	(setq seq (concat "seq_along("ycols-R")"))	(setq largs (plist-put largs :col seq))	(setq largs (plist-put largs :lty seq))	(setq largs (plist-put largs :bty "\"n\""))	(setq largs (plist-put largs :legend (concat "colnames(x)["ycols-R"]")))		(setq extra-code	      (concat "; "		      "if(length("ycols-R") > 1) "		      "legend(" (org-R-plist-to-R-args largs) ")"))		(concat (format org-R-skeleton-funcall-2-args			(if (and (eq nxcols 1) (eq nycols 1)) "plot" "matplot")			xcols-R ycols-R args)		extra-code))              ;;----------------------------------------------------------------------              (t (error "action:%s requires a single set of columns" (symbol-name action))))))))(defun org-R-set-user-supplied-args (args user-args)  "Set user-supplied values in arguments plist."  (while (setq prop (pop user-args))    (setq args (plist-put args prop (pop user-args))))  args)  (defun org-R-plist-to-R-args (plist)  "Convert a plist into a string of R arguments."  (let (arg-string arg)    (while (setq arg (pop plist))      (string-match ":\\(\.*\\)" (symbol-name arg))      (setq arg (match-string 1 (symbol-name arg)))      (setq arg-string	    (concat	     (if arg-string (concat arg-string ", "))	     (format "%s=%s" arg (pop plist)))))    arg-string))(defun org-R-alist-to-R-args (alist)  "Convert an alist of (argument . val) pairs into a string of R arguments.The alist is something like      '((arg1 . 1)	(arg2 . a)) This isn't used, but it seems much nicer thanmy plist equivalent. Is there a better way to write the plistversion?"  (mapconcat   'identity   (mapcar (lambda(pair) (format "%s = %s" (car pair) (cdr pair))) alist)   ", "))(defun org-R-make-index-vectors (cols)  "COLS is the lisp form given by the `columns:' option. It maytake the following forms:1. integer atom        - the number of the column2. symbol/string atom  - the name of the column3. list of length 1    - same as 1 or 2 above4. list of length > 1  - specification of multiple columns as 1 or 2 above, unless it is5. list of 2 lists     - each list specifies (possibly multiple) columnsIn cases 1-4 this function returns a list of length 1, containingthe R index vector as a string. In case 5 this function returns alist of two such index vectors.In cases 1 - 4, when a bivariate plot is requested such as by`action:lines', the x values are implicit, i.e1,2,...,number-of-rows.In case 4, an attempt is made to do something sensible with themultiple columns, e.g. for `action:lines' they will be plottedtogether on the same graph against the implicit x values, and for`action:barplot' the bars corresponding to a single row will bestacked on top of each other, or placed side by side, dependingon the value of the `beside' option.For `action:tabulate', if 2 columns are selected, atwo-dimensional table is created. If more than 2, then theappropriately dimensioned table is computed and inserted usingthe standard text representation of multi-dimensional arrays usedby R (as org does not currently have tables of dimension > 2).The straightforward case of case 5 is that both lists are oflength 1. For `action:plot' and `action:lines' these specify they and x coordinates of the points to be plotted or joined bylines. The intention is that `org-R-apply' does somethingcorresponding to what would happen if you did the following in R:fun(x=tab[,xcols], y=tab[,ycols])where fun is the R function implementing the desiredaction (plotting/computation), tab is the org table, xcols arethe columns specified in cases 1-4 above, and ycols are thesecond set of columns which might have been specified under case5 above. For relevant R documentation see the help pageassociated with the function xy.coords, e.g. by typing ?xy.coordsat the R prompt.The following won't work with case 5: `tabulate'"  (defun org-R-make-index-vector (cols)    "Return the R indexing vector (as a string) corresponding tothe lisp form COLS. In this function, COLS is a either a list ofatoms, or an atom, i.e. in the form of cases 1-4"    (when cols      (let (to-stringf)	(unless (listp cols) (setq cols (list cols)))	(setq to-stringf 	      (cond ((car (mapcar 'symbolp cols))		     (lambda (symbol) (concat "\"" (symbol-name symbol) "\"")))		    ((car (mapcar 'integerp cols))		     'int-to-string)		    ((car (mapcar 'stringp cols))		     (lambda (string) (concat "\"" string "\"")))		    (t (error "Column selection should be symbol, integer or string: %S" cols))))	(concat (when (> (length cols) 1) "c(")		(mapconcat to-stringf cols ",")		(when (> (length cols) 1) ")")))))  (if (and (listp cols) (listp (car cols)))      (mapcar 'org-R-make-index-vector cols) ;; case 5    (list (org-R-make-index-vector cols))))  ;; other cases(defun org-R-number-of-columns (cols)  (defun f (c) (if (listp c) (length c) 1))  (if (and (listp cols) (listp (car cols)))      (mapcar 'f cols)    (list (f cols))))  (defun org-R-eval (R-function csv-file options)  "Apply an R function to tabular data and receive output as an org table.R-FUNCTION is a string; it may be simply the name of anappropriate R function (e.g. \"summary\", \"plot\"), or auser-defined anonymous function of the form\"(function(data.frame) {...})\". It will receive as its firstargument the org table as an R 'data frame' -- a table-likestructure which can have columns containing different types ofdata -- numeric, character etc.The R function may produce graphical and/or text output. If itproduces text output, and the replace:t is specified, and ifthere is a table immediately above the #+R lines, then it isreplaced by the text output. Otherwise the text output isinserted above the #+R lines."  (let ((transit-buffer "org-R-transit")	(infile (plist-get options :infile))	(output-file (plist-get options :outfile))	(title (plist-get options :title))	output-format graphics-output-file width height)        (unless (not output-file)      ;; We are writing output to file. Determine file format and      ;; location, and open graphics device if necessary.      (if (string-match	   "\\(.*\.\\)?\\(org\\|png\\|jpg\\|jpeg\\|pdf\\|ps\\|bmp\\|tiff\\)$"	   output-file)	  (setq output-format (match-string 2 output-file))	(error "Did not recognise file name suffix %s as available output format"	       (match-string 2 output-file)))      (unless (match-string 1 output-file)	;; only suffix provided: store in org-attach dir	(require 'org-attach)	(let ((temporary-file-directory (org-attach-dir t)))	  (setq output-file		(make-temp-file		 "org-R-output-" nil (concat "." output-format)))))      (if (eq output-format "jpg") (setq output-format "jpeg"))      (setq graphics-output-file (not (string-equal output-format "org")))      (if graphics-output-file ;; open the graphics device	  (ess-execute	   (concat output-format "(file=\"" output-file "\""		   (if (setq width (plist-get options :width))		       (format ", width=%d" width))		   (if (setq height (plist-get options :height))		       (format ", height=%d" height)) ")"))))        ;; Apply R code to table (which is now stored as a csv file)    ;; does it matter whether this uses ess-command or ess-execute?        ;; First evaluate function definition for R -> org table conversion    (ess-execute (replace-regexp-in-string "\n" " " org-R-write-org-table-def)		 nil transit-buffer)    ;; FIXME: why not eval the function def together with the function call    ;; as in the commented out line below (it didn't work for some reason)    (ess-execute     (concat      ;; (replace-regexp-in-string "\n" " " org-R-write-org-table-def) ";"      (org-R-make-expr R-function csv-file options)) nil transit-buffer)    (save-excursion      (set-buffer (concat "*" transit-buffer "*"))      (unless (or (looking-at "$")		  (string-equal (buffer-substring-no-properties 1 2) "|"))	(error "Error in R evaluation:\n%s" (buffer-string))))            (if csv-file	(unless (and infile		     (string-equal (file-name-extension infile) "csv"))	  (delete-file csv-file)))      (if graphics-output-file (ess-execute "dev.off()")) ;; Close graphics device        (unless (or graphics-output-file		(not (plist-get options :output-to-buffer)))      ;; Send tabular output to a org buffer as new org      ;; table. Recall that we are currently at the beginning of the      ;; first #+R line      (if (and output-file graphics-output-file)	  (error "output-to-buffer and graphics-output-file both t"))		      (save-excursion	(if output-file	    (progn (set-buffer (find-file-noselect output-file))		   (delete-region (point-min) (point-max)))	  (if (plist-get options :replace)	      (progn ;; kill a table iff in one or one ends on the previous line		(delete-region (org-table-begin) (org-table-end))		(save-excursion 		  (forward-line -1)		  (if (looking-at "#\\+TBLNAME")		      (delete-region (point) (1+ (point-at-eol))))))))	(if title (insert "#+TBLNAME:" title "\n"))	(insert-buffer-substring (concat "*" transit-buffer "*"))	(org-table-align)	(if output-file (save-buffer))))    ;; We might be linking to graphical output, or to org output in    ;; another file. Either way, point is still at the beginning of    ;; the first #+R line.    (unless (not output-file)      (save-excursion 	(forward-line -1)	(if (looking-at "\\[\\[file:")	    (delete-region (point) (1+ (point-at-eol)))))      (insert (org-make-link-string 	       (concat "file:" output-file)	       (unless (plist-get options :inline)		 (or title (concat output-format " output")))) "\n"))    (kill-buffer (concat "*" transit-buffer "*"))))(defun org-R-export-to-csv (csv-file options)  "Find and export org table to csv.If the intable: option has not been supplied, then the table mustend on the line immediately above the #+R lines. Otherwise,the remote table referenced by the intable: option is found usingorg-R-find-table. If options:infile has been set then this is theorg file containing the table. See the docstring oforg-R-find-table for details."  (let ((tbl-name-or-id (plist-get options :intable))	(org-file (plist-get options :infile)) tbl-marker)        (if (and org-file	     (not (string-equal (file-name-extension org-file) "org")))	(error "File %s extension is not .csv so should be .org"))    (save-excursion      (if tbl-name-or-id	  ;; a remote table has been specified -- move into it	  (progn	    (if org-file (set-buffer (find-file-noselect org-file)))	    (setq tbl-marker (org-R-find-table tbl-name-or-id 'marker))	    (set-buffer (marker-buffer tbl-marker))	    (goto-char (marker-position tbl-marker)))	(forward-line -1)) ;; move into table above      (if (looking-at "[ \t]*|")	  (progn (org-table-export csv-file "orgtbl-to-csv") csv-file)	nil))))(defun org-R-find-table (name-or-id &optional markerp)  "Return location of a table.NAME-OR-ID may be the name of atable in the current file as set by a \"#+TBLNAME:\" directive.The first table following this line will then be used.Alternatively, it may be an ID referring to any entry, perhaps ina different file.  In this case, the first table in that entrywill be referenced. The location is returned as a marker pointingto the beginning of the first line of the table.This is taken from the first part of org-table-get-remote-rangein org-table.el."  (cond   ((symbolp name-or-id) (setq name-or-id (symbol-name name-or-id)))   ((numberp name-or-id) (setq name-or-id (number-to-string name-or-id))))  (save-match-data    (let ((id-loc nil) (case-fold-search t) buffer loc)      (save-excursion	(save-restriction	  (widen)	  (save-excursion	    (goto-char (point-min))	    (if (re-search-forward		 (concat "^#\\+TBLNAME:[ \t]*" (regexp-quote name-or-id) "[ \t]*$")		 nil t)		;; OK, we've found a matching table name in this buffer.		(setq buffer (current-buffer) loc (match-beginning 0))	      ;; It's not a table name in this buffer. It must be an entry id.	      ;; obtain a marker pointing to it.	      (setq id-loc (org-id-find name-or-id 'marker)		    buffer (marker-buffer id-loc)		    loc (marker-position id-loc))	      (move-marker id-loc nil))) ;; disable the marker	  ;; (switch-to-buffer buffer)	  (set-buffer buffer)	  ;; OK, so now we're in the right buffer, and loc is either	  ;; the beginning of the #+TBLNAME line, or the location of the entry	  ;; either way we need to search forward to get to the beginning of the table	  (save-excursion	    (save-restriction	      (widen)	      (goto-char loc)	      (forward-char 1)	      ;; The following regexp search finds the beginning of	      ;; the next table in this entry. If it gets to the next	      ;; entry before the next table, then it signals failure.	      (unless (and (re-search-forward "^\\(\\*+ \\)\\|[ \t]*|" nil t)			   (not (match-beginning 1)))		(error "Cannot find a table at NAME or ID %s" name-or-id))	      (if markerp		  (move-marker (make-marker) (point-at-bol) (current-buffer))		(error "Option to return cons cell not implemented.                        It should return (file-name . position) to be                         consistent with functions in org-id.el")))))))))(defun org-R-make-expr (R-function csv-file options)  "Construct R code to read data, analyse it and write output."  (let ((rownames (plist-get options :rownames))	(colnames (plist-get options :colnames))	(action (plist-get options :action))	(replace (plist-get options :replace)))    (if (and csv-file (symbolp csv-file))	(setq csv-file (symbol-name csv-file)))    (format "write.org.table((%s)(%s), write.rownames=%s)"	    R-function	    (if csv-file		(format		 "read.csv(\"%s\", header=%s, row.names=%s)"		 csv-file		  		 ;; Do we treat first row as colnames? Yes by default		 ;; FIXME: should really check for hline		 (if colnames "TRUE" "FALSE")		 ;; Do we use a column as rownames? Not unless rownames: is specified		 (if rownames "1" "NULL"))	      "NULL")	    ;; Do we write rownames into org table?	    (cond ((eq action 'tabulate)		   (if (eq (plist-get options :nxcols) 1) "FALSE" "TRUE"))		  ((eq action 'transpose) (if colnames "TRUE" "FALSE"))		  (rownames "TRUE")		  (t "TRUE")))))	    (defun org-R-get-options ()  "Parse the #+R: lines and return the options and values as a p-list."  (let ((opts '(		(:infile . "infile")		(:intable . "intable")		(:rownames . "rownames")		(:colnames . "colnames")		(:columns . "columns")		(:action . "action")		(:args . "args")				(:outfile . "outfile")		(:replace . "replace")		(:title . "title")		(:legend . "legend")		(:colour . "colour")		(:color . "color")		(:col . "col")		(:height . "height")		(:width . "width")		(:lines . "lines")		(:sort . "sort")		(:inline . "inline")		(:output-to-buffer . "output-to-buffer")				(:showcode . "showcode")))	(regexp ":\\(\"[^\"]*\"\\|(([^)]*) *([^)]*))\\|([^)]*)\\|[^ \t\n\r;,.]*\\)")	(case-fold-search t) p)    ;; FIXME: set default options properly    (setq p (plist-put p :output-to-buffer t)) ;; FIXME: hack: null options plist is bad news    (setq p (plist-put p :replace t))    (setq p (plist-put p :rownames nil))    (setq p (plist-put p :colnames t))    (setq p (plist-put p :inline nil))    (save-excursion      (while (looking-at "^#\\+\\(RR?:+\\) *\\(.*\\)")	(if (string= "R:" (match-string 1))	    (setq p (org-R-add-options-to-plist p (match-string 2) opts regexp)))	(forward-line)))    p))(defun org-R-add-options-to-plist (p opt-string op regexp)  "Parse a #+R: line and set values in the property listp. This function is adapted from similar functions in org-exp.eland org-plot.el. It might be a good idea to have a singlefunction serving these three files' needs."  ;; Adapted from org-exp.el and org-plot.el  (let (o)    (when opt-string      (while (setq o (pop op))	(if (string-match 	     (concat (regexp-quote (cdr o)) regexp)	     opt-string)	    (setq p (plist-put p (car o)			       (car (read-from-string				     (match-string 1 opt-string)))))))))  p)(defun org-R-sanitise-options (options)  (error "not used yet")  (let (should-be-strings '(title legend colour color col csv)))  )(defun org-R-showcode (R)  "Display R function constructed by org-R in a new R-modebuffer"  (split-window-vertically)  (switch-to-buffer "*org-table.R*")  (kill-region (point-min) (point-max))  (R-mode)  (insert (replace-regexp-in-string 	   ";" "\n" (replace-regexp-in-string "\\([{}]\\)" "\n\\1\n" R)))  ;; (mark-whole-buffer)  ;; (indent-region)  ;; why doesn't that do what I hoped?  )(defun org-R-get-remote-range (name-or-id form)  "This is a refactoring of Carsten's original version. I haveextracted the first bit of his function and named itorg-R-find-table (which would presumably be calledsomething like org-table-find-table or org-id-find-table if thiswere accepted).Get a field value or a list of values in a range from table at ID.NAME-OR-ID may be the name of a table in the current file as set bya \"#+TBLNAME:\" directive.  The first table following this linewill then be used.  Alternatively, it may be an ID referring toany entry, possibly in a different file.  In this case, the first tablein that entry will be referenced.FORM is a field or range descriptor like \"@2$3\" or or \"B3\" or\"@I$2..@II$2\".  All the references must be absolute, not relative.The return value is either a single string for a single field, or alist of the fields in the rectangle."  (let ((tbl-marker (org-R-find-table name-or-id 'marker))	org-table-column-names org-table-column-name-regexp	org-table-local-parameters org-table-named-field-locations	org-table-current-line-types org-table-current-begin-line	org-table-current-begin-pos org-table-dlines	org-table-hlines org-table-last-alignment	org-table-last-column-widths org-table-last-alignment	org-table-last-column-widths tbeg)    (save-excursion      (set-buffer (marker-buffer tbl-marker))      (goto-char (marker-position tbl-marker))      (org-table-get-specials)      (setq form (org-table-formula-substitute-names form))      (if (and (string-match org-table-range-regexp form)	       (> (length (match-string 0 form)) 1))	  (save-match-data	    (org-table-get-range (match-string 0 form) (point) 1))	form))))(provide 'org-R)
 |