1 #+OPTIONS: H:3 num:nil toc:t \n:nil ::t |:t ^:t -:t f:t *:t tex:t d:(HIDE) tags:not-in-toc
2 #+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
3 #+SEQ_TODO: TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
4 #+TAGS: Write(w) Update(u) Fix(f) Check(c)
5 #+TITLE: Org tutorial on table lookup functions
7 #+EMAIL: jarmo.hurri AT syk DOT fi
10 #+CATEGORY: org-tutorial
13 Org provides three different functions for performing searches and
14 data dependent calculations in tables. These functions can, among
15 other things, be used to implement associative arrays, count
16 matching cells, rank results, or group data. The following examples
17 will hopefully help you in getting started with these functions.
19 * Associative array with unique keys
21 The most straightforward use of lookups is to treat part of an org
22 table as an associative array: a key can be used to look up a
25 Say you are taking a trip to Scandinavia, and you want to keep track
26 of how much money you have spent on the trip. You decide to convert
27 all sums to euros. Before your trip you write down the following
28 table of approximate currency rates.
30 : | currency | abbreviation | euros |
31 : |-----------------+--------------+-------|
33 : | Norwegian krone | nok | 0.14 |
34 : | Swedish krona | sek | 0.12 |
35 : | US dollar | usd | 0.77 |
37 In what follows we will use the function =org-lookup-first= and the
38 previous table =rates= to automatically convert the sums in
39 different currencies to euros. The signature of function
40 =org-lookup-first= looks as follows:
42 (org-lookup-first VAL S-LIST R-LIST &optional PREDICATE)
44 Assuming that =PREDICATE= is =nil=, in which case the default
45 predicate =equal= is used, this function does a search for the first
46 instance of =VAL= in =S-LIST= and returns the a value from the
47 corresponding position in =R-LIST=. In the table below, each sum is
48 assigned a currency abbreviation; a lookup is done in table =rates=
49 above in the second column for the corresponding abbreviation, and
50 then the corresponding rate is returned from the third column. For
51 each row only the first four columns need to filled; columns 5 and 6
52 are calculated automatically. Notice that an error results if the
53 key is not found: in the last row, an empty key is being searched
56 : | date | expense | sum | currency | rate | euros |
57 : |-------+------------------+------+----------+--------+--------|
58 : | 1.3. | flights | 324 | eur | 1 | 324 |
59 : | 4.6. | books and maps | 243 | usd | 0.77 | 187.11 |
60 : | 30.7. | rental car | 8300 | sek | 0.12 | 996. |
61 : | 2.7. | hotel | 1150 | sek | 0.12 | 138. |
62 : | 2.7. | lunch | 190 | sek | 0.12 | 22.8 |
63 : | 3.7. | fishing licenses | 1400 | nok | 0.14 | 196. |
64 : | 3.7. | gasoline | 340 | | #ERROR | #ERROR |
65 : #+TBLFM: $5='(org-lookup-first $4 '(remote(rates,@2$2..@>$2)) '(remote(rates,@2$3..@>$3)))::$6=$5*$3
67 * Multiple matches with preferred ordering
69 A common task for teachers is the assignment of exam grades from
70 total marks. The starting point for such grading is a table with
71 grade boundaries. Below is one such table, with the rows in
72 increasing order of the lower bound required for a particular grade.
74 : #+TBLNAME: grade-boundaries
75 : | lower bound | grade |
76 : |-------------+-------|
83 We will use the function =org-lookup-last= and the previous table
84 =grade-boundaries= to assign grades to students based on their
85 marks. The signature of function =org-lookup-last= is exactly like
86 the signature of =org-lookup-first=:
88 (org-lookup-last VAL S-LIST R-LIST &optional PREDICATE)
90 However, this function does a search for the /last/ match in
91 =S-LIST= and returns the a value from the corresponding position in
92 =R-LIST=. Here the idea of the lookup used in assigning the grade is
93 as follows. Say a student's exam result is 33 marks. We look for
94 the /last/ row in the table for which the student's marks are greater
95 than or equal to the lower bound; in this case it is the row with
96 lower bound 30. The student's grade is the corresponding element from
97 the second column, in this case a B.
99 Thus, given the number of marks =VAL= of a student, we find the last
100 row of the first column of table =grade-boundaries= for which the
101 lower bound =S= fulfils ~(>= VAL S)~. Thus we will use ~>=~ as
102 =PREDICATE= to perform the matching. Note that =VAL= and =S= are
103 assigned as arguments to the predicate in the order they are in the
104 signature of =org-lookup-last=, where =VAL= precedes =S-LIST=. The
105 following table does the conversion from total marks to the final
106 grade. Notice the literal interpolation =L= of table values into
107 the Elisp formula, which is needed because some values are numbers
108 and some are symbols.
110 : | student | marks | grade |
111 : |---------+-------+-------|
116 : #+TBLFM: $3='(org-lookup-last $2 '(remote(grade-boundaries,@2$1..@>$1)) '(remote(grade-boundaries,@2$2..@>$2)) '>=);L
118 * Counting matching cells
120 The function =org-lookup-all= can not be used by itself in a table
121 equation, because it returns a list of values. However, powerful
122 lookup tasks can be performed by combining the function with other
125 As a simple example consider counting the number of missing values
126 in a table. The signature of function =org-lookup-all= is exactly
127 like the signatures of the other two lookup functions:
129 (org-lookup-all VAL S-LIST R-LIST &optional PREDICATE)
131 However, this function does a search for the /all/ matches in
132 =S-LIST= and returns the all corresponding values from the
133 corresponding positions in =R-LIST=. As is the case with
134 =org-lookup-first= and =org-lookup-last=, if =R-LIST= is =nil=, then
135 the corresponding matching values of =S-LIST= are returned
136 directly. Notice the use of the =E= flag to retain empty fields in
137 the range. Also notice that in this case we are doing the lookup in
138 a true two-dimensional range, which is thus also possible
140 : | group | round 1 | round 2 |
141 : |-------+---------+---------|
150 : |-------+---------+---------|
151 : | total | missing | 7 |
152 : #+TBLFM: @>$3='(length(org-lookup-all "" '(@2$2..@-1$3) nil));E
156 Another example application of =org-lookup-all= is an automatic
157 ranking of results. In the table below, a larger total number of
158 marks is better. Notice that the Elisp expression also
159 automatically takes care of ties.
161 : | group | marks | rank |
162 : |-------+-------+------|
168 : #+TBLFM: $3='(+ 1 (length (org-lookup-all $2 '(@2$2..@>$2) nil '<)));N
170 * Frequency counts from raw data
171 A common situation in the analysis of data is the classification
172 (grouping) of raw data values for, e.g., visualisation. Often this
173 is done by counting the frequencies of observations within certain
174 bounds. The function =org-lookup-all=, combined with other Elisp
175 functions, can be used to perform this task. This example also shows
176 how to construct more complicated lookup rules using multiple values
179 Consider the following table with different results from different
181 : #+TBLNAME: raw-data
194 We will classify the results into different, mutually exclusive
195 classes. For example, the observations that will belong to the first
196 class are in the interval =[1, 1.9]= (endpoints included). In order
197 to perform this classification, we define the following two-place
198 predicate function =in-interval=. Notice that the first parameter of
199 this function is a pair whose first element is the lower bound and
200 second member the upper bound of the interval.
202 : #+BEGIN_SRC emacs-lisp
203 : (defun in-interval (bounds el)
204 : (and (>= el (car bounds)) (<= el (cadr bounds))))
210 Using this predicate function, we can construct a table with class
211 boundaries and corresponding frequencies. Note that the first
212 argument to the function =org-lookup-all=, which is then passed over
213 as the first argument to the predicate =in-interval=, is the pair of
216 : | lower bound | upper bound | frequency |
217 : |-------------+-------------+-----------|
222 : #+TBLFM: $3='(length (org-lookup-all '($1 $2) '(remote(raw-data,@2$2..@>$2)) nil 'in-interval));N
225 The org lookup functions can be used for a large number of different
226 data-dependent calculations. For example, the following spreadsheet
227 operations familiar to libreoffice or Excel users can be implemented
228 using them: =HLOOKUP=, =VLOOKUP=, =COUNTIF=, =SUMIF= and
229 =FREQUENCY=. If you have other interesting examples of the use of
230 these functions, feel free to send them to the [[https://lists.gnu.org/mailman/listinfo/emacs-orgmode][org mailing list]] and
231 we will be happy to add them on this page.