Skip to content

Latest commit

 

History

History
754 lines (627 loc) · 31.2 KB

tables.org

File metadata and controls

754 lines (627 loc) · 31.2 KB

Org mode tables

1 Org mode and Emacs version information

(princ (concat (format "Emacs version: %s\n" (emacs-version))
               (format "org version: %s\n" (org-version))))
Emacs version: GNU Emacs 26.2 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.22.30)
 of 2019-04-14
org version: 9.3.7

2 Basic table examples

2.1 Basic syntax

Having used Org mode for a few years now, I notice that most of my tables look like the following

Namenumbercost per itemsumincl VAT
name131500.004500.004860.00
name294000.0036000.0038880.00
name342800.0011200.0012096.00
Total51700.0055836.00

Note:

  • The syntax @I..@II for defining the vertical range between the horizontal rulers is extremely useful. One does not need to provide row numbers. Regrettably this syntax can only be used on the right side of an equation (So this is forbidden: @I$2..@II$2=5).
  • The use of > to indicate the last row or column is useful, since one does not need to use explicit row and column numbers.
  • Unsurprisingly, vsum is the most frequent calc function used in my tables.
  • The number format is controlled by the %.2f specifiers at the end of the formulas. The syntax is similar to the one used in C format specifiers, e.g. %f for float.

Moving rows and columns in a table

  • When inserting rows or deleting rows always try to use M-up / M-down (org-table-move-row). It will adapt the formulas.
  • When moving columns around, always do it with M-right and M-left. It will adapt the formulas to match the changed structure.

2.2 Advanced table syntax

The same table using named columns, an advanced table feature. The column names get defined in the row containing ! in the first column.

Namenumbercost per itemsumincl VAT
!namenumperitemsum
name131500.004500.4860.00
name294000.0036000.38880.00
name342800.0011200.12096.00
Total51700.0055836.00

Using a column formula instead of the range formula for column 5. In order to use the other advanced features like the naming of fields, it is necessary to mark the rows to be affected by the column formula with a “*” in the first column. All other rows will be unaffected.

Namenumbercost per itemsumincl VAT
!namenumperitemsum
*name131500.004500.4860.00
^varname
*name294000.0036000.38880.00
*name342800.0011200.12096.00
Total51700.0055836.00

3 Table formulas

3.1 using lisp functions in table formulas

3.1.1 basic usage

Referenced fields in a lisp formula are read as strings, except if the modifiers ;N for numeric or ;L for using literal values are given at the end of the formula.

Tip: try using the table debugger (C-c {) if something goes wrong.

Rowc2c3AddAdd2
!c2c3
#A1233
#B471111
#C4str44

For the next example, this lisp function must be defined in the present emacs session (do C-x C-e with the cursor at the end of the expression or use C-c C-c on the source block.)

     (defun my-colsum-if (keylist vallist keymatch)
     "sum values in vallist if the corresponding key matches the keymatch argument"
	(cl-loop for key in keylist
		 for val in vallist
		 when (equal key keymatch)
		 sum (string-to-number val)))

The values in the following table are random generated. In the lower section of the table we use the above function to only add values with the class matching the specification.

Classvalue
!classvalue
#A3
#B8
#C2
#A3
#B5
#C9
#all values3 8 2 3 5 9
#sum30
#sum if A6
#sum if B13

3.1.2 reading input fields as literal lisp values

Using the ;L modifier, one can have the references be interpreted as literal lisp values. In this example I am using this feature for displaying the lisp types of various arguments in the first column.

expressionlisp type
‘mapconcatsymbol
#’mapconcatsymbol
“text”string
(concat “hello” ” world”)string
1integer
(+ 3 4)integer
?ainteger
1.0float
‘(1 2 3)cons
[1 2 3 4]vector
nilsymbol

3.2 Using src block functions in table formulas

3.2.1 calling a source block from a lisp formula with org-sbe

The org-sbe macro (warning: it was called sbe in earlier org versions) allows calling the previously defined src blocks from within table formulas and feeding them then named arguments.

I first define two example source block functions mydouble and mydivide.

(* 2 x)
(/ x y)
Acalc doublelisp doublelisp divide
!colAcolBcolCcolD
#1222
#3662
#918182

3.2.2 specifying whether referred to fields are to be read as numbers or strings

If the field references should be read as strings, one needs to add an additional dollar sign, e.q. $$1, $$colname, a single dollar sign $1 reads the field value as a number. Here is an example reading in date strings, and using calc functions for doing some time arithmetic.

     (let ((calc-date-format
	     '(YYYY "-" MM "-" DD)))
	(math-format-date (calcFunc-bsub (calcFunc-incmonth (math-parse-date argdate) (string-to-number argmonths)) 1))
	)
WPWP durationWP startWP end
numbersubjectmonthsdatedate
!wpidwpnamewpmonthssdateedate
#WP0Project Management242015-01-012016-12-31
#WP1IT Infrastructure242015-01-012016-12-31
#WP2IdM Mngm + Rem Acc122015-01-012015-12-31
#WP3Data Catalog92015-01-012015-09-30
#WP4provide existing SW242015-01-012016-12-31
#WP5SW development242015-01-012016-12-31
#TOTAL

A function which sums up the values in a column of table tbl if col1 matches match1 and col2 matches match2

;; add vcol column values if col1 matches match1 and col2 matchtes match2
(let ((c1list (org-table-get-remote-range tbl (format "@I$%s..@>$%s" col1 col1)))
      (c2list (org-table-get-remote-range tbl (format "@I$%s..@>$%s" col2 col2)))
      (vallist (org-table-get-remote-range tbl (format "@I$%s..@>$%s" vcol vcol))))
	 (cl-loop for c1tst in c1list
           for c2tst in c2list
           for val in vallist
           when (and (equal c1tst match1) (equal c2tst match2))
           sum (string-to-number val))
	 )
namegroupusevalue
!namegroupusevalue
johnB11
bethB03
mikeC15
leslieA07
barbaraA14
kenC02
thomasA18

To demonstrate the above code, we use it to fill the sum column in the table below. We sum up all values in the above table where the group matches the given target group column, and where the use column matches “1”.

target groupsum
!grp
#A12
#B1
#C5

3.2.3 an analytic look at the involved lisp functions

3.2.3.1 org-sbe

#+TBLFM: @I$6..@II$6='(org-sbe addmonths (argdate $$sdate) (argmonths $$wpmonths))

The double dollar ends up in passing this kind of code line where the resulting string arguments are headed by a dollar sign:

(org-sbe addmonths (argdate $"2015-01-01") (argmonths $"24"))

3.2.3.2 org-table-get-remote-range

There seems to be a bug in the org-table-get-remote-range function. When I reference the remote range by a field name (defined in a special row marked by “^” in the first column), the result is a string that contains the field value wrapped in parentheses:

(pp (org-no-properties (org-table-get-remote-range "remtable1" "$ref_number"))) (princ "\n")
(pp (org-no-properties (org-table-get-remote-range "remtable1" "@2$3"))) (princ "\n")
(pp (org-no-properties (org-table-get-remote-range "remtable1" "$ref_date"))) (princ "\n")
(pp (org-no-properties (org-table-get-remote-range "remtable1" "@4$3"))) (princ "\n")

Exploring the usage of remote inside of a table.

  • The date is read as an equation (“-” is minus) and I get the result of a substraction

Table for remote table test

EntryValue
a number24
^ref_number
a date2014-01-02
^ref_date

Here we try different ways of referencing the fields of the table above using the remote keyword:

Entryfield name refnum reflisp + field name
remote number2424(24)
remote date20112011(2014-01-02)

3.3 some other calc functions used in table formulas

Calc offers a big number of functions. Have a look in info:calc#Function Index. Here I present just a few examples in these subsections

3.3.1 conditions using if

numberclasseven
1Aodd
2Aeven
3Bodd
4Beven

3.3.2 locate position of element in a column: find

In the following table we use find to determine the location of the “1” in each column. Note that we use the qualifier ;E in order to have the vector retain the empty fields.

PosAABBCCDDEEFFGGHHIIJJKKLLMM
1111
21
3
41
51
681
7
8
91
10121
111
121
1311
141
155
161
171
181
19
Result111410214519610112

3.3.3 mapping over elements of a vector

From a mail by Eric S Fraga to the [email protected] list

abresult
[2,3][4,6][0.5, 0.5]

3.4 time calculations

3.4.1 basic usage

Time calculations can be done using the T modifier, which will expect input in HH:MM[:SS] format and deliver output in HH:MM[:SS] format.

For the last column I use the t modifier, which delivers the result as a float according to the setting of the variable org-table-duration-custom-format (‘hours by default).

Itemdurationstartingtotal
(min)time AMhours
Presentation by the candidate00:208:308.50
Presentation Questions00:1008:50:008.83
Break00:1509:00:009.00
Main interview00:9009:15:009.25
Break00:1510:45:0010.75
HR Interview00:6011:00:0011.00
optional Lunch / Coffee00:6012:00:0012.00
optional interview00:3013:00:0013.00

3.4.2 a nicer function for adding up time values

Here another function to add up a time interval and a clock value.

(let ((date (org-parse-time-string
             (concat "2015-06-01 "
                     (substring-no-properties inputtime)))))
	 (setf (nth 1 date) (+ (nth 1 date) (string-to-number delta)))
	 (format-time-string "%H:%M" (apply 'encode-time date)))
09:30

And we use it for calculating the clock value for an interview schedule in the following table.

Itemdurationstarting
(min)time AM
Presentation by the candidate208:30
Presentation Questions1008:50
Break1509:00
Main interview9009:15
Break1510:45
HR Interview6011:00
optional Lunch / Coffee6012:00
optional interview3013:00

3.5 table lookup functions

Interesting advanced possibilities are opened up when using the org table lookup functions

http://orgmode.org/worg/org-tutorials/org-lookups.html

We define a mapping table. Note that we have two mappings for the string “two”.

one1
two2
three3
four4
two100

We fill the second column of the table below according to the associative array defined by the table above. Values which cannot be mapped yield nil (it was #ERROR in older versions). org-lookup-first will find the first matching row and give back the associated mapped value. An #ERROR will be returned for missing key values.

three3
fivenil
two2
sixnil
one1
four4

org-lookup-last accordingly takes the values from the last row that matched.

three3
fivenil
two100
sixnil
one1
four4

3.6 A note on the choice of column names and remote references

  • One must be careful and not use a remote column name that also is used in the current table. Seems that the substitution of the value in the current scope takes precedence over the one in the remote scope.
  • Underscores in column names should be avoided. As seen in the Value4 column of the second table below, the “$value_a4” reference seem to be interpreted just as “$value”
EntryValueValue2Value3Value4
!entryvaluevalue2value3avalue_a4
#example1101102103104
EntryValueValue2Value3Value4
!
#101102103101

3.7 Indirection in remote table references

Org supports indirection for the tablename argument of the remote function in table formulas. So you can refer to tables based on the contents of a current table’s fields:

Tablename
remtableIdColName44
table1USA50
remtable12011ref_date

4 Some examples using tables from babel blocks

4.1 referencing table ranges from lisp code blocks

The lisp function to use for retrieving table values is org-table-get-remote-range. Notes

  • the retrieval from tables using advanced naming syntax (e.g ^ in the initial column to name the element above as “first”) returns the value in parentheses (a bug?).
  • the advanced naming also creates problems in the ranges, since the row containing “first” and the empty value are not filtered out

Here an example for multiple cases.

keyvalue
!keyvalue
A1
^first
B2
C3
D4
SUM10
(mapc (lambda (x) (princ (format "%s => %s\n" (car x) (cdr x))))
      (cl-loop for ref in '("@3$3" "@3$key" "$first" "@I$3..@II$3" "@I$2..@II$3")
    		for tblget = (org-table-get-remote-range "tblRefsFromLisp" ref)
    		collect (cons ref (pp-to-string
    				   (cl-case (type-of tblget)
    				     ('cons (mapcar #'substring-no-properties tblget))
    				     ('string (substring-no-properties tblget))
    				     (t "failed to match")))) into result
    		finally return result))

4.2 filtering a table

I posted this in reply to this stackexchange question.

We produce an example table to work upon

   (let ((countries
          (mapcar #'symbol-name '(CH D USA CN JP PL USA D PL CN CH))))
     (cl-loop for country1 in countries
		for country2 in (reverse countries)
		with counter = 0
		collect (list (format "row%d" counter)
                            (* 2 counter)
                            country1
                            country2
                            (* 5 counter)) into mylst
                            count t into counter
                            finally return (append
                                            '((col1 col2 col3 col4 col5)
						hline)
                                            mylst)))
col1col2col3col4col5
row00CHCH0
row12DCN5
row24USAPL10
row36CND15
row48JPUSA20
row510PLPL25
row612USAJP30
row714DCN35
row816PLUSA40
row918CND45
row1020CHCH50

Now we define a filter function which produces a new table with the required values. Notice that I am using the colnames argument in the BEGIN line in order to preserve the column headings.

(loop for row in tbl
      if (equal (nth 3 row) val)
      collect row into newtbl
      finally return newtbl)
col1col2col3col4col5
row48JPUSA20
row816PLUSA40

Note that in the previous source block the input is actually coming from the re-evaluation of the table1 source block and not from the resulting table.

I can also use this function with the org-mode CALL syntax

col1col2col3col4col5
row12DCN5
row714DCN35

I also demonstrate here the SQLite approach where I use your original requirement of filtering all the rows which contain the string either in columns 3 or 4. A minor drawback of the sqlite approach is that we have some boilerplate code to read in the table and create a SQLite DB.

drop table if exists table1;
create table table1 (col1 VARCHAR, col2 INTEGER, col3 VARCHAR,
col4 VARCHAR, col5 INTEGER);
.import "$tbl" table1
select * from table1 where col3='$val' or col4='$val';
col1col2col3col4col5
row24USAPL10
row48JPUSA20
row612USAJP30
row816PLUSA40
col1col2col3col4col5
row12DCN5
row36CND15
row714DCN35
row918CND45

5 Exporting tables with some columns hidden

It is desirable to be able and hide columns in exported output. This is often the case in tables where a lot of computations are done, and where intermediate results end up in columns that one does not want to end up in the exported document.

This functionality is currently not available by standard org, but since this is Emacs, a simple function implementing this functionality was published by Michael Brand within this emacs-orgmode thread.

The exported table will have col2 removed.

col1col2col3
/<r><#>
a1a2a3
b1b2b3

6 Information on internals

Nicolas Goaziou wrote in a reply about the mechanism how formulas are evaluated:

Field formulas bind stronger than column formulas.

First, all cells with an associated field formula are marked as read-only. Then column formulas are evaluated. Eventually, fields formulas are evaluated.

This was introduced in Org 5.01, AFAICT. Before, the “read-only” part would not happens, i.e, fields formulas would overwrite column formulas.

I think the idea behind this is that formulas are applied to the current state of the table, not some intermediate one, with some formulas applied and others not.

7 Bugs I found [1/2]

7.1 FIXED table names like p2_somename

do not use table names like p2_somename or somename_p2_someother. The p2 is interpretet as column P, field 2 when you go back from the table editor (C-‘), and it will be substituted by the numeric location @2$16. This happens when you use a remote(p2_somename,somefield) reference in a formula. It clearly is a bug. This seems to be fixed in org-version 8.2.7c
onetwo
12
col1col2
2

7.2 OPEN table referenced by remote calls must not contain same column names

onetwo
!onetwo
#12
#34

in the following remote call, the $one variable is replaced by the local table’s column number for column “one” (which is 2) instead of the column numeber of the referred table (where “one” in the first column)

onetwo
!twoone
#2