emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [O] [babel, bug?] colnames with a list of columns does not work


From: Aaron Ecay
Subject: Re: [O] [babel, bug?] colnames with a list of columns does not work
Date: Fri, 23 Jan 2015 14:49:32 -0500
User-agent: Notmuch/0.19+20~g2bbe5e0 (http://notmuchmail.org) Emacs/25.0.50.2 (x86_64-unknown-linux-gnu)

Hi Sebastien,

2015ko urtarrilak 23an, Sebastien Vauban-ek idatzi zuen:
> Yes, you just show that the documentation is not up-to-date, as that
> functionality *is* implemented for most languages.
> 
> Doing some bit of archeology, I just found out that:
> 
> - Eric wrote a patch to support the above (but it hasn't be applied),
> 
> - I (!) even wrote a test of that functionality (for a shell block) in
>   `testing/lisp/test-ob.el'.
> 
> See https://lists.gnu.org/archive/html/emacs-orgmode/2013-04/msg00527.html:

Thanks for bringing this back to the surface.  I reworked Eric’s patch
(which no longer applied cleanly).  The result is attached.  With this
patch:
- Both row/colnames set from R and from Org should work
- If they are set in both ways, the Org ones will win

There are tests for this behavior.

There is a slight mismatch between R and Org.  R considers the colnames
(C) and rownames (R) separate from the table (X):

  CCCC
R XXXX
R XXXX
R XXXX

On the other hand, Babel assigns a colname to the column of rownames:

C CCCC
R XXXX
R XXXX
R XXXX

So, R users will need to watch out for this.  I wasn’t sure where to
document it – maybe Worg?  (There’s a comment about it in the test
suite, but that hardly counts.)

>From e192ad71b61fd6ddf034a15c1012a99de00e5865 Mon Sep 17 00:00:00 2001
From: Aaron Ecay <address@hidden>
Date: Fri, 23 Jan 2015 12:33:51 -0500
Subject: [PATCH] ob-R: Fix table row/colname processing.

* lisp/ob-R.el (org-babel-execute:R): Use babel-standard row/colname
processing.  Remove graphics-specific R code from here.
(org-babel-R-construct-graphics-device-call): Absorb graphics-specific
code.
(org-babel-R-process-value-result): Remove function.
(org-babel-R-evaluate-external-process):
(org-babel-R-evaluate-session): Adapt callers.

This is in line with a patch proposed by Eric Schulte:
<http://mid.gmane.org/address@hidden>.  Thanks to Sebastien
for bringing it up again.

* testing/lisp/test-ob-R.el (test-ob-R/colnames-from-r):
(test-ob-R/colnames-from-org):
(test-ob-R/rownames-from-r):
(test-ob-R/rownames-from-org):
(test-ob-R/row-and-colnames-from-r):
(test-ob-R/row-and-colnames-from-org): New tests.
---
 lisp/ob-R.el              | 108 ++++++++++++++++++++++++----------------------
 testing/lisp/test-ob-R.el |  72 +++++++++++++++++++++++++++++++
 2 files changed, 128 insertions(+), 52 deletions(-)

diff --git a/lisp/ob-R.el b/lisp/ob-R.el
index 639b4f8..68aba30 100644
--- a/lisp/ob-R.el
+++ b/lisp/ob-R.el
@@ -155,36 +155,47 @@ This function is used when the table does not contain a 
header.")
   "Execute a block of R code.
 This function is called by `org-babel-execute-src-block'."
   (save-excursion
-    (let* ((result-params (cdr (assoc :result-params params)))
-          (result-type (cdr (assoc :result-type params)))
+    (let* ((result-params (cdr (assq :result-params params)))
+          (result-type (cdr (assq :result-type params)))
            (session (org-babel-R-initiate-session
-                    (cdr (assoc :session params)) params))
-          (colnames-p (cdr (assoc :colnames params)))
-          (rownames-p (cdr (assoc :rownames params)))
-          (graphics-file (and (member "graphics" (assq :result-params params))
+                    (cdr (assq :session params)) params))
+          (graphics-file (and (member "graphics" result-params)
                               (org-babel-graphical-output-file params)))
+          (colnames (cdr (assq :colnames params)))
+          (rownames (cdr (assq :rownames params)))
+          (inside (org-babel-expand-body:R body params graphics-file))
           (full-body
-           (let ((inside
-                  (list (org-babel-expand-body:R body params graphics-file))))
-             (mapconcat 'identity
-                        (if graphics-file
-                            (append
-                             (list (org-babel-R-construct-graphics-device-call
-                                    graphics-file params))
-                             inside
-                             (list "},error=function(e){plot(x=-1:1, y=-1:1, 
type='n', xlab='', ylab='', axes=FALSE); text(x=0, y=0, labels=e$message, 
col='red'); paste('ERROR', e$message, sep=' : ')}); dev.off()"))
-                          inside)
-                        "\n")))
+           (if graphics-file
+               (org-babel-R-construct-graphics-device-call
+                graphics-file params inside)
+             inside))
           (result
            (org-babel-R-evaluate
             session full-body result-type result-params
-            (or (equal "yes" colnames-p)
-                (org-babel-pick-name
-                 (cdr (assoc :colname-names params)) colnames-p))
-            (or (equal "yes" rownames-p)
-                (org-babel-pick-name
-                 (cdr (assoc :rowname-names params)) rownames-p)))))
-      (if graphics-file nil result))))
+            (equal "yes" colnames)
+            (equal "yes" rownames))))
+      (unless graphics-file
+       (org-babel-reassemble-table
+        result
+        (org-babel-pick-name
+         ;; In most cases, the original colnames have been passed
+         ;; into R and are coming back from there, thus we don't need
+         ;; the copy that babel stashed in the :colname-names entry.
+         ;; However, if :colnames nil is specified babel does not
+         ;; pass along the colnames to R, but is expected to reapply
+         ;; them to the table.  ("nil" is a confusing name for this
+         ;; semantics, but that's how it is documented in the
+         ;; manual.)  Only n this case must we permit access to
+         ;; babel's stored colnames.  These remarks also apply to the
+         ;; rownames immediately below.
+         (when (equal colnames "nil")
+           (cdr (assq :colname-names params)))
+         colnames)
+        (org-babel-pick-name
+         ;; See above.
+         (when (equal rownames "nil")
+           (cdr (assq :rowname-names params)))
+         rownames))))))
 
 (defun org-babel-prep-session:R (session params)
   "Prepare SESSION according to the header arguments specified in PARAMS."
@@ -309,19 +320,20 @@ Each member of this list is a list with three members:
 3. the name of the argument to this function which specifies the
    file to write to (typically \"file\" or \"filename\")")
 
-(defun org-babel-R-construct-graphics-device-call (out-file params)
+(defun org-babel-R-construct-graphics-device-call (out-file params code)
   "Construct the call to the graphics device."
   (let* ((allowed-args '(:width :height :bg :units :pointsize
                                :antialias :quality :compression :res
                                :type :family :title :fonts :version
                                :paper :encoding :pagecentre :colormodel
                                :useDingbats :horizontal))
-        (device (and (string-match ".+\\.\\([^.]+\\)" out-file)
-                     (match-string 1 out-file)))
-        (device-info (or (assq (intern (concat ":" device))
+        (device-name (and (string-match ".+\\.\\([^.]+\\)" out-file)
+                          (match-string 1 out-file)))
+        (device-info (or (assq (intern (concat ":" device-name))
                                org-babel-R-graphics-devices)
                           (assq :png org-babel-R-graphics-devices)))
-        (extra-args (cdr (assq :R-dev-args params))) filearg args)
+        (extra-args (cdr (assq :R-dev-args params)))
+        filearg args device)
     (setq device (nth 1 device-info))
     (setq filearg (nth 2 device-info))
     (setq args (mapconcat
@@ -331,9 +343,10 @@ Each member of this list is a list with three members:
                              (substring (symbol-name (car pair)) 1)
                              (cdr pair)) ""))
                params ""))
-    (format "%s(%s=\"%s\"%s%s%s); tryCatch({"
+    (format "%s(%s=\"%s\"%s%s%s); tryCatch({%s},error=function(e){plot(x=-1:1, 
y=-1:1, type='n', xlab='', ylab='', axes=FALSE); text(x=0, y=0, 
labels=e$message, col='red'); paste('ERROR', e$message, sep=' : ')}); dev.off()"
            device filearg out-file args
-           (if extra-args "," "") (or extra-args ""))))
+           (if extra-args "," "") (or extra-args "")
+           code)))
 
 (defconst org-babel-R-eoe-indicator "'org_babel_R_eoe'")
 (defconst org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"")
@@ -395,13 +408,12 @@ last statement in BODY, as elisp."
                                 "FALSE")
                               (format "{function ()\n{\n%s\n}}()" body)
                               (org-babel-process-file-name tmp-file 'noquote)))
-       (org-babel-R-process-value-result
-       (org-babel-result-cond result-params
-         (with-temp-buffer
-           (insert-file-contents tmp-file)
-           (buffer-string))
-         (org-babel-import-elisp-from-file tmp-file '(16)))
-       column-names-p)))
+       (org-babel-result-cond result-params
+        (with-temp-buffer
+          (insert-file-contents tmp-file)
+          (buffer-string))
+        (org-babel-import-elisp-from-file tmp-file '(16)))
+       column-names-p))
     (output (org-babel-eval org-babel-R-command body))))
 
 (defvar ess-eval-visibly-p)
@@ -429,13 +441,12 @@ last statement in BODY, as elisp."
                    (if row-names-p "NA" "TRUE")
                  "FALSE")
                ".Last.value" (org-babel-process-file-name tmp-file 'noquote)))
-       (org-babel-R-process-value-result
-       (org-babel-result-cond result-params
-         (with-temp-buffer
-           (insert-file-contents tmp-file)
-           (buffer-string))
-         (org-babel-import-elisp-from-file tmp-file '(16)))
-       column-names-p)))
+       (org-babel-result-cond result-params
+        (with-temp-buffer
+          (insert-file-contents tmp-file)
+          (buffer-string))
+        (org-babel-import-elisp-from-file tmp-file '(16)))
+       column-names-p))
     (output
      (mapconcat
       'org-babel-chomp
@@ -455,13 +466,6 @@ last statement in BODY, as elisp."
                                    "\n"))
                 (inferior-ess-send-input)))))) "\n"))))
 
-(defun org-babel-R-process-value-result (result column-names-p)
-  "R-specific processing of return value.
-Insert hline if column names in output have been requested."
-  (if column-names-p
-      (cons (car result) (cons 'hline (cdr result)))
-    result))
-
 (provide 'ob-R)
 
 
diff --git a/testing/lisp/test-ob-R.el b/testing/lisp/test-ob-R.el
index e3f13f1..16bdd62 100644
--- a/testing/lisp/test-ob-R.el
+++ b/testing/lisp/test-ob-R.el
@@ -79,6 +79,78 @@ x
     (should (equal '(("col") ("a") ("b"))
                   (org-babel-execute-src-block)))))
 
+(ert-deftest test-ob-R/colnames-from-r ()
+  (org-test-with-temp-text "
+#+header: :colnames yes
+#+begin_src R
+y <- data.frame(x = c(1,2,3))
+y
+#+end_src"
+    (org-babel-next-src-block)
+    (should (equal '(("x") hline (1) (2) (3))
+                  (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-R/colnames-from-org ()
+  (org-test-with-temp-text "
+#+header: :colnames '(\"foo\")
+#+begin_src R
+y <- data.frame(x = c(1,2,3))
+y
+#+end_src"
+    (org-babel-next-src-block)
+    (should (equal '(("foo") hline (1) (2) (3))
+                  (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-R/rownames-from-r ()
+  (org-test-with-temp-text "
+#+header: :rownames yes
+#+begin_src R
+x <- data.frame(x = c(1,2,3))
+rownames(x) <- c(\"A\",\"B\",\"C\")
+x
+#+end_src"
+    (org-babel-next-src-block)
+    (should (equal '(("A" 1) ("B" 2) ("C" 3))
+                  (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-R/rownames-from-org ()
+  (org-test-with-temp-text "
+#+header: :rownames '(\"D\" \"E\" \"F\")
+#+begin_src R
+x <- data.frame(x = c(1,2,3))
+rownames(x) <- c(\"A\",\"B\",\"C\")
+x
+#+end_src"
+    (org-babel-next-src-block)
+    (should (equal '(("D" 1) ("E" 2) ("F" 3))
+                  (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-R/row-and-colnames-from-r ()
+  (org-test-with-temp-text "
+#+header: :rownames yes :colnames yes
+#+begin_src R
+y <- data.frame(x = c(1,2,3))
+rownames(y) <- c(\"A\",\"B\",\"C\")
+y
+#+end_src"
+    (org-babel-next-src-block)
+    (should (equal '(("" "x") hline ("A" 1) ("B" 2) ("C" 3))
+                  (org-babel-execute-src-block)))))
+
+(ert-deftest test-ob-R/row-and-colnames-from-org ()
+  ;; NB: For R, the column of rownames doesn't itself have a colname,
+  ;; whereas for Org it must.
+  (org-test-with-temp-text "
+#+header: :rownames '(\"D\" \"E\" \"F\") :colnames '(\"colnames\" \"foo\")
+#+begin_src R
+y <- data.frame(x = c(1,2,3))
+rownames(y) <- c(\"A\",\"B\",\"C\")
+y
+#+end_src"
+    (org-babel-next-src-block)
+    (should (equal '(("colnames" "foo") hline ("D" 1) ("E" 2) ("F" 3))
+                  (org-babel-execute-src-block)))))
+
 (provide 'test-ob-R)
 
 ;;; test-ob-R.el ends here
-- 
2.2.2

Thanks,

-- 
Aaron Ecay

reply via email to

[Prev in Thread] Current Thread [Next in Thread]