fat_table 0.4.0 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/README.org +1426 -447
- data/README.rdoc +1 -2
- data/TODO.org +17 -10
- data/examples/create_trans.sql +14 -0
- data/examples/quick.pdf +0 -0
- data/examples/quick.png +0 -0
- data/examples/quick.ppm +0 -0
- data/examples/quick.tex +8 -0
- data/examples/quick_small.png +0 -0
- data/examples/quicktable.tex +123 -0
- data/examples/trades.db +0 -0
- data/examples/trans.csv +13 -0
- data/fat_table.gemspec +1 -0
- data/lib/ext/array.rb +15 -0
- data/lib/fat_table/column.rb +89 -208
- data/lib/fat_table/convert.rb +174 -0
- data/lib/fat_table/errors.rb +4 -0
- data/lib/fat_table/evaluator.rb +7 -0
- data/lib/fat_table/footer.rb +228 -0
- data/lib/fat_table/formatters/formatter.rb +200 -166
- data/lib/fat_table/formatters/latex_formatter.rb +9 -7
- data/lib/fat_table/table.rb +366 -117
- data/lib/fat_table/version.rb +1 -1
- data/lib/fat_table.rb +19 -16
- data/md/README.md +1 -2
- metadata +31 -5
data/README.org
CHANGED
@@ -1,5 +1,19 @@
|
|
1
|
-
#+
|
1
|
+
#+TITLE: FatTable User Guide
|
2
|
+
#+OPTIONS: toc:4
|
2
3
|
#+LATEX_HEADER: \usepackage[margin=0.75in]{geometry}
|
4
|
+
#+PROPERTY: header-args:ruby :colnames no :session readme :hlines yes :exports both
|
5
|
+
#+PROPERTY: header-args:sh :exports code
|
6
|
+
#+STARTUP: inlineimages
|
7
|
+
|
8
|
+
#+begin_comment
|
9
|
+
Notes on producing this README.
|
10
|
+
|
11
|
+
1. Result blocks won't be rendered on Github if the #+RESULTS: tag is left
|
12
|
+
over the results block, so manually delete them.
|
13
|
+
|
14
|
+
2. Make sure that the current version of the gem is geting loaded when running
|
15
|
+
the code blocks. rbenv-use-corresponding helps here.
|
16
|
+
#+end_comment
|
3
17
|
|
4
18
|
#+BEGIN_COMMENT
|
5
19
|
This is for markdown output:
|
@@ -11,6 +25,16 @@ The following is for org.
|
|
11
25
|
|
12
26
|
[[https://travis-ci.org/ddoherty03/fat_table.svg?branch=master]]
|
13
27
|
|
28
|
+
* Version
|
29
|
+
#+begin_src ruby :wrap EXAMPLE
|
30
|
+
require 'fat_table'
|
31
|
+
"Current version is: #{FatTable::VERSION}"
|
32
|
+
#+end_src
|
33
|
+
|
34
|
+
#+begin_EXAMPLE
|
35
|
+
Current version is: 0.5.3
|
36
|
+
#+end_EXAMPLE
|
37
|
+
|
14
38
|
* Introduction
|
15
39
|
|
16
40
|
~FatTable~ is a gem that treats tables as a data type. It provides methods for
|
@@ -33,37 +57,141 @@ they makes sense for the output medium and treats other formatting directives as
|
|
33
57
|
no-ops.
|
34
58
|
|
35
59
|
~FatTable~ can be used to perform operations on data that are naturally best
|
36
|
-
conceived of as tables, which in my experience is quite often. It can also
|
37
|
-
as a foundation for providing reporting functions where flexibility
|
38
|
-
output medium can be useful. Finally ~FatTable~ can be used within
|
39
|
-
~org-mode~ files in code blocks targeting the Ruby language. Org mode
|
40
|
-
presented to a ruby code block as an array of arrays, so ~FatTable~
|
41
|
-
them in with its ~.from_aoa~ constructor. A ~FatTable~ table output as an
|
42
|
-
of arrays with its ~.to_aoa~ output function will be rendered in an
|
43
|
-
buffer as an org-table, ready for processing by other code blocks.
|
60
|
+
conceived of as tables, which in my experience is quite often. It can also
|
61
|
+
serve as a foundation for providing reporting functions where flexibility
|
62
|
+
about the output medium can be useful. Finally ~FatTable~ can be used within
|
63
|
+
Emacs ~org-mode~ files in code blocks targeting the Ruby language. Org mode
|
64
|
+
tables are presented to a ruby code block as an array of arrays, so ~FatTable~
|
65
|
+
can read them in with its ~.from_aoa~ constructor. A ~FatTable~ table output as an
|
66
|
+
array of arrays with its ~.to_aoa~ output function will be rendered in an
|
67
|
+
org-mode buffer as an org-table, ready for processing by other code blocks.
|
68
|
+
|
69
|
+
* Table of Contents :toc:noexport:
|
70
|
+
- [[#version][Version]]
|
71
|
+
- [[#introduction][Introduction]]
|
72
|
+
- [[#installation][Installation]]
|
73
|
+
- [[#using-in-a-gem][Using in a gem]]
|
74
|
+
- [[#manually-install][Manually install]]
|
75
|
+
- [[#require][Require]]
|
76
|
+
- [[#usage][Usage]]
|
77
|
+
- [[#quick-start][Quick Start]]
|
78
|
+
- [[#a-word-about-the-examples][A Word About the Examples]]
|
79
|
+
- [[#anatomy-of-a-table][Anatomy of a Table]]
|
80
|
+
- [[#columns][Columns]]
|
81
|
+
- [[#headers][Headers]]
|
82
|
+
- [[#groups][Groups]]
|
83
|
+
- [[#constructing-tables][Constructing Tables]]
|
84
|
+
- [[#empty-tables][Empty Tables]]
|
85
|
+
- [[#without-headers][Without Headers]]
|
86
|
+
- [[#with-headers][With Headers]]
|
87
|
+
- [[#forcing-string-type][Forcing String Type]]
|
88
|
+
- [[#designating-tolerant-columns][Designating "Tolerant" Columns]]
|
89
|
+
- [[#from-csv-or-org-mode-files-or-strings][From CSV or Org Mode files or strings]]
|
90
|
+
- [[#from-arrays-of-arrays][From Arrays of Arrays]]
|
91
|
+
- [[#in-ruby-code][In Ruby Code]]
|
92
|
+
- [[#in-emacs-org-files][In Emacs Org Files]]
|
93
|
+
- [[#from-arrays-of-hashes][From Arrays of Hashes]]
|
94
|
+
- [[#from-sql-queries][From SQL queries]]
|
95
|
+
- [[#marking-groups-in-input][Marking Groups in Input]]
|
96
|
+
- [[#manually][Manually]]
|
97
|
+
- [[#when-reading-in-tables][When Reading in Tables]]
|
98
|
+
- [[#accessing-parts-of-tables][Accessing Parts of Tables]]
|
99
|
+
- [[#rows][Rows]]
|
100
|
+
- [[#columns-1][Columns]]
|
101
|
+
- [[#cells][Cells]]
|
102
|
+
- [[#other-table-attributes][Other table attributes]]
|
103
|
+
- [[#operations-on-tables][Operations on Tables]]
|
104
|
+
- [[#example-input-tables][Example Input Tables]]
|
105
|
+
- [[#select][Select]]
|
106
|
+
- [[#selecting-existing-columns-also-of-omni][Selecting Existing Columns (Also of :omni)]]
|
107
|
+
- [[#copying-and-renaming-existing-columns][Copying and Renaming Existing Columns]]
|
108
|
+
- [[#adding-new-columns][Adding New Columns]]
|
109
|
+
- [[#custom-instance-variables-and-hooks][Custom Instance Variables and Hooks]]
|
110
|
+
- [[#argument-order-and-boundaries][Argument Order and Boundaries]]
|
111
|
+
- [[#where][Where]]
|
112
|
+
- [[#order_by][Order_by]]
|
113
|
+
- [[#order_with][Order_with]]
|
114
|
+
- [[#group_by][Group_by]]
|
115
|
+
- [[#join][Join]]
|
116
|
+
- [[#join-types][Join Types]]
|
117
|
+
- [[#join-expressions][Join Expressions]]
|
118
|
+
- [[#join-examples][Join Examples]]
|
119
|
+
- [[#inner-joins][Inner Joins]]
|
120
|
+
- [[#left-and-right-joins][Left and Right Joins]]
|
121
|
+
- [[#full-join][Full Join]]
|
122
|
+
- [[#cross-join][Cross Join]]
|
123
|
+
- [[#set-operations][Set Operations]]
|
124
|
+
- [[#unions][Unions]]
|
125
|
+
- [[#intersections][Intersections]]
|
126
|
+
- [[#set-differences-with-except][Set Differences with Except]]
|
127
|
+
- [[#uniq-aka-distinct][Uniq (aka Distinct)]]
|
128
|
+
- [[#remove-groups-with-degroup][Remove groups with degroup!]]
|
129
|
+
- [[#formatting-tables][Formatting Tables]]
|
130
|
+
- [[#available-formatter-output-targets][Available Formatter Output Targets]]
|
131
|
+
- [[#output-media][Output Media]]
|
132
|
+
- [[#examples][Examples]]
|
133
|
+
- [[#to-text][To Text]]
|
134
|
+
- [[#to-org][To Org]]
|
135
|
+
- [[#to-term][To Term]]
|
136
|
+
- [[#to-latex][To LaTeX]]
|
137
|
+
- [[#to-aoa-array-of-arrays][To AoA (Array of Arrays)]]
|
138
|
+
- [[#to-aoh-array-of-hashes][To AoH (Array of Hashes)]]
|
139
|
+
- [[#formatting-directives][Formatting Directives]]
|
140
|
+
- [[#string][String]]
|
141
|
+
- [[#numeric][Numeric]]
|
142
|
+
- [[#datetime][DateTime]]
|
143
|
+
- [[#boolean][Boolean]]
|
144
|
+
- [[#nilclass][NilClass]]
|
145
|
+
- [[#the-format-and-format_for-methods][The ~format~ and ~format_for~ methods]]
|
146
|
+
- [[#table-locations][Table Locations]]
|
147
|
+
- [[#location-priority][Location priority]]
|
148
|
+
- [[#type-and-column-priority][Type and Column priority]]
|
149
|
+
- [[#footers][Footers]]
|
150
|
+
- [[#adding-footers][Adding Footers]]
|
151
|
+
- [[#aggregators][Aggregators]]
|
152
|
+
- [[#footer-objects][Footer objects]]
|
153
|
+
- [[#footer-examples][Footer Examples]]
|
154
|
+
- [[#built-in-aggregators][Built-in Aggregators]]
|
155
|
+
- [[#string-aggregators][String Aggregators]]
|
156
|
+
- [[#ruby-objects][Ruby Objects]]
|
157
|
+
- [[#lambdas][Lambdas]]
|
158
|
+
- [[#invoking-formatters][Invoking Formatters]]
|
159
|
+
- [[#by-instantiating-a-formatter][By Instantiating a Formatter]]
|
160
|
+
- [[#by-using-fattable-module-level-method-calls][By Using ~FatTable~ module-level method calls]]
|
161
|
+
- [[#by-calling-methods-on-table-objects][By Calling Methods on Table Objects]]
|
162
|
+
- [[#development][Development]]
|
163
|
+
- [[#contributing][Contributing]]
|
44
164
|
|
45
165
|
* Installation
|
46
|
-
|
47
|
-
** Installing the gem
|
48
|
-
|
166
|
+
** Using in a gem
|
49
167
|
Add this line to your application's Gemfile:
|
50
|
-
|
51
|
-
#+BEGIN_SRC ruby
|
168
|
+
#+BEGIN_SRC ruby :exports code
|
52
169
|
gem 'fat_table'
|
53
170
|
#+END_SRC
|
54
171
|
|
55
|
-
|
172
|
+
Or, something like this in your gemspec file:
|
173
|
+
#+begin_SRC ruby :exports code
|
174
|
+
gem.add_runtime_dependency 'fat_table'
|
175
|
+
#+end_SRC
|
56
176
|
|
177
|
+
And then execute:
|
57
178
|
#+BEGIN_SRC sh
|
58
179
|
$ bundle
|
59
180
|
#+END_SRC
|
60
181
|
|
182
|
+
** Manually install
|
61
183
|
Or install it yourself as:
|
62
184
|
|
63
185
|
#+BEGIN_SRC sh
|
64
186
|
$ gem install fat_table
|
65
187
|
#+END_SRC
|
66
188
|
|
189
|
+
** Require
|
190
|
+
Somewhere in your code, make sure that =FatTable= is required:
|
191
|
+
#+begin_src ruby :exports code :results silent
|
192
|
+
require 'fat_table'
|
193
|
+
#+end_src
|
194
|
+
|
67
195
|
* Usage
|
68
196
|
** Quick Start
|
69
197
|
|
@@ -71,10 +199,10 @@ Or install it yourself as:
|
|
71
199
|
operated on in a number of ways. Here's a quick example to illustrate the use of
|
72
200
|
~FatTable~. See the detailed explanations further on down.
|
73
201
|
|
74
|
-
|
75
|
-
|
76
|
-
require 'fat_table'
|
202
|
+
Here is a set of data that records some kind of stock activity. It's an array
|
203
|
+
of arrays with the first inner array being the headings.
|
77
204
|
|
205
|
+
#+BEGIN_SRC ruby :exports code :results silent
|
78
206
|
data =
|
79
207
|
[['Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Ok'],
|
80
208
|
['2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ENTITY3', 'F'],
|
@@ -89,9 +217,17 @@ operated on in a number of ways. Here's a quick example to illustrate the use of
|
|
89
217
|
['2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ENTITY3', 'T'],
|
90
218
|
['2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ENTITY3', 'T'],
|
91
219
|
['2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ENTITY3', 'F']]
|
220
|
+
#+END_SRC
|
221
|
+
|
222
|
+
Use FatTable to read the data and convert in into a table object. Note that
|
223
|
+
the headings within the table are all converted to symbols, lower-cased and
|
224
|
+
any spaces replaced with underscores.
|
92
225
|
|
93
|
-
|
226
|
+
Below, we select only those rows having more than 2000 shares, sort by a
|
227
|
+
compund key, select all columns but add a column, :ref, for the row number,
|
228
|
+
and finally re-order the columns with a final select.
|
94
229
|
|
230
|
+
#+BEGIN_SRC ruby :results silent :exports code
|
95
231
|
table = FatTable.from_aoa(data) \
|
96
232
|
.where('shares > 2000') \
|
97
233
|
.order_by(:date, :code) \
|
@@ -99,27 +235,51 @@ operated on in a number of ways. Here's a quick example to illustrate the use of
|
|
99
235
|
:price, :ok, ref: '@row') \
|
100
236
|
.select(:ref, :date, :code,
|
101
237
|
:shares, :price, :ok)
|
238
|
+
#+END_SRC
|
102
239
|
|
103
|
-
|
104
|
-
|
240
|
+
You can use the resulting table in other operations, such as performing joins
|
241
|
+
or set operations with other tables, etc. The world's your oyster. But
|
242
|
+
eventually you will want to present the table in some format, and that is
|
243
|
+
where the formatting methods come in. They let you add footers, including
|
244
|
+
groups footers, as well as styling the various elements with very simple
|
245
|
+
formatting directives that can apply to various "locations" in the table. Any
|
246
|
+
formatting directives that are beyond the capabilities of the output medium
|
247
|
+
are simply ignored.
|
248
|
+
|
249
|
+
We can format the table constructed above.
|
250
|
+
#+BEGIN_SRC ruby :exports both
|
105
251
|
table.to_text do |fmt|
|
106
|
-
# Add
|
252
|
+
# Add a group footer at the bottom of each group that results from sorting
|
253
|
+
# with the order_by method.
|
254
|
+
fmt.gfooter('Avg', shares: :avg, price: :avg)
|
255
|
+
# Add some table footers. Averages for the price and shares columns. The
|
256
|
+
# avg_footer method applies the avg aggregate to all the named columns with
|
257
|
+
# an "Average" label.
|
107
258
|
fmt.avg_footer(:price, :shares)
|
259
|
+
# And a second footer that shows the sum for the shares column.
|
108
260
|
fmt.sum_footer(:shares)
|
109
|
-
#
|
110
|
-
|
111
|
-
#
|
261
|
+
# Formats for all locations, :ref column is centered and bold, all numerics
|
262
|
+
# are right-aligned, and all booleans are centered and printed with 'Y' or
|
263
|
+
# 'N'
|
112
264
|
fmt.format(ref: 'CB', numeric: 'R', boolean: 'CY')
|
113
|
-
# Formats for different "locations" in the table
|
265
|
+
# Formats for different "locations" in the table:
|
266
|
+
# The headers are all centered and bold.
|
114
267
|
fmt.format_for(:header, string: 'CB')
|
268
|
+
# In the body rows (i.e., not the headers or footers), the code column is
|
269
|
+
# centered, shares have grouping commas applied and are rounded to one
|
270
|
+
# decimal place, but the price column is rounded to 4 places with no
|
271
|
+
# grouping commas.
|
115
272
|
fmt.format_for(:body, code: 'C', shares: ',0.1', price: '0.4', )
|
273
|
+
# But the price column in the first row of the body (:bfirst location) will
|
274
|
+
# also be formatted with a currency symbol.
|
116
275
|
fmt.format_for(:bfirst, price: '$0.4', )
|
117
|
-
|
276
|
+
# In the footers, apply the same rounding rules, but make the results bold.
|
118
277
|
fmt.format_for(:gfooter, shares: 'B,0.1', price: 'B0.4', )
|
278
|
+
fmt.format_for(:footer, shares: 'B,0.1', price: '$B0.4', )
|
119
279
|
end
|
120
280
|
#+END_SRC
|
121
281
|
|
122
|
-
#+
|
282
|
+
#+begin_example
|
123
283
|
+=========+============+======+=============+==========+====+
|
124
284
|
| Ref | Date | Code | Shares | Price | Ok |
|
125
285
|
+---------+------------+------+-------------+----------+----+
|
@@ -154,23 +314,64 @@ operated on in a number of ways. Here's a quick example to illustrate the use of
|
|
154
314
|
+---------+------------+------+-------------+----------+----+
|
155
315
|
| Total | | | 1,020,119.1 | | |
|
156
316
|
+=========+============+======+=============+==========+====+
|
157
|
-
#+
|
317
|
+
#+end_example
|
158
318
|
|
159
|
-
|
319
|
+
For the text format above, we were wasting our breath specifying bold styling
|
320
|
+
since there is no way to make that happen in plain ASCII text. But with
|
321
|
+
LaTeX, bold is doable. The output of the following code block is being
|
322
|
+
written to a file =examples/quicktable.tex= which is then =\included=-ed in a
|
323
|
+
simple wrapper file, =examples/quick.tex= so it can be compiled by LaTeX.
|
324
|
+
|
325
|
+
#+BEGIN_SRC ruby :results file :file "examples/quicktable.tex"
|
326
|
+
table.to_latex do |fmt|
|
327
|
+
fmt.gfooter('Avg', shares: :avg, price: :avg)
|
328
|
+
fmt.avg_footer(:price, :shares)
|
329
|
+
fmt.sum_footer(:shares)
|
330
|
+
fmt.format(ref: 'CB', numeric: 'R', boolean: 'CY')
|
331
|
+
fmt.format_for(:header, string: 'CB')
|
332
|
+
fmt.format_for(:body, code: 'C', shares: ',0.1c[blue.lightgray]', price: '0.4', )
|
333
|
+
fmt.format_for(:bfirst, price: '$0.4', )
|
334
|
+
fmt.format_for(:gfooter, shares: 'B,0.1', price: 'B0.4', )
|
335
|
+
fmt.format_for(:footer, shares: 'B,0.1', price: '$B0.4', )
|
336
|
+
end
|
337
|
+
#+END_SRC
|
338
|
+
|
339
|
+
#+begin_EXAMPLE
|
340
|
+
[[file:examples/quicktable.tex]]
|
341
|
+
#+end_EXAMPLE
|
342
|
+
|
343
|
+
These commands run pdflatex on the result twice to get the table aligned
|
344
|
+
properly.
|
345
|
+
#+begin_src sh :results silent
|
346
|
+
cd examples
|
347
|
+
pdflatex quick.tex
|
348
|
+
pdflatex quick.tex
|
349
|
+
#+end_src
|
350
|
+
|
351
|
+
And we convert the =PDF= into a smaller image for display:
|
352
|
+
#+begin_src sh :results verbatim
|
353
|
+
cd examples
|
354
|
+
pdftoppm -png quick.pdf >quick.png
|
355
|
+
convert quick.png -resize 600x800 quick_small.png
|
356
|
+
#+end_src
|
357
|
+
|
358
|
+
[[file:examples/quick_small.png]]
|
160
359
|
|
360
|
+
** A Word About the Examples
|
161
361
|
When you install the ~fat_table~ gem, you have access to a program ~ft_console~,
|
162
362
|
which opens a ~pry~ session with ~fat_table~ loaded and the tables used in the
|
163
363
|
examples in this ~README~ defined as instance variables so you can experiment
|
164
364
|
with them. Because they are defined as instance variables, you have to write
|
165
|
-
~tab1~ as ~@tab1~ in ~ft_console~, but otherwise the examples should work as
|
166
|
-
|
365
|
+
~tab1~ as ~@tab1~ in ~ft_console~, but otherwise the examples should work as shown
|
366
|
+
in this ~README~.
|
167
367
|
|
168
|
-
The examples in this ~README~ file are executed
|
169
|
-
~README.org~ file, so they typically end with a call to
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
~
|
368
|
+
The examples in this ~README~ file are executed in Emacs org-mode as code
|
369
|
+
blocks within the ~README.org~ file, so they typically end with a call to
|
370
|
+
~.to_aoa~. That causes Emacs to insert the "Array of Array" ruby data
|
371
|
+
structure into the file and format it as a table, which is the convention for
|
372
|
+
Emacs org-mode. With ~ft_console~, you should instead display your tables with
|
373
|
+
~.to_text~ or ~.to_term~. These will return a string that you can print to the
|
374
|
+
terminal with ~puts~.
|
174
375
|
|
175
376
|
To read in the table used in the Quick Start section above, you might do the
|
176
377
|
following:
|
@@ -230,7 +431,6 @@ directives.
|
|
230
431
|
|
231
432
|
** Anatomy of a Table
|
232
433
|
*** Columns
|
233
|
-
|
234
434
|
~FatTable::Table~ objects consist of an array of ~FatTable::Column~ objects.
|
235
435
|
Each ~Column~ has a header, a type, and an array of items, all of the given type
|
236
436
|
or nil. There are only five permissible types for a ~Column~:
|
@@ -252,39 +452,38 @@ Items of input must be either one of the permissible ruby objects or strings. If
|
|
252
452
|
they are strings, ~FatTable~ attempts to parse them as one of the permissible
|
253
453
|
types as follows:
|
254
454
|
|
255
|
-
- Boolean ::
|
256
|
-
case, are interpreted as ~TrueClass~ and the strings, ~
|
257
|
-
~
|
455
|
+
- Boolean :: The strings, ~t~, ~true~, ~yes~, or ~y~, regardless of
|
456
|
+
case, are interpreted as ~TrueClass~ and the strings, ~f~, ~false~,
|
457
|
+
~no~, or ~n~, regardless of case, are interpreted as ~FalseClass~, in
|
258
458
|
either case resulting in a Boolean column. Empty strings in a column
|
259
459
|
already having a Boolean type are converted to ~nil~.
|
260
|
-
- DateTime ::
|
261
|
-
or ~
|
262
|
-
~
|
460
|
+
- DateTime :: Strings that contain patterns of ~yyyy-mm-dd~ or ~yyyy/mm/dd~
|
461
|
+
or ~mm-dd-yyy~ or ~mm/dd/yyyy~ or any of the foregoing with an added
|
462
|
+
~Thh:mm:ss~ or ~Thh:mm~ will be interpreted as a ~DateTime~ or a ~Date~
|
263
463
|
(if there are no sub-day time components present). The number of digits in
|
264
464
|
the month and day can be one or two, but the year component must be four
|
265
465
|
digits. Any time components are valid if they can be properly interpreted
|
266
466
|
by ~DateTime.parse~. Org mode timestamps (any of the foregoing surrounded
|
267
|
-
by square
|
467
|
+
by square ~[]~ or pointy ~<>~ brackets), active or inactive, are valid
|
268
468
|
input strings for ~DateTime~ columns. Empty strings in a column already
|
269
469
|
having the ~DateTime~ type are converted to ~nil~.
|
270
|
-
- Numeric ::
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
- String ::
|
470
|
+
- Numeric :: All commas (~,~) underscores (~_~) and (~$~) dollar signs (or
|
471
|
+
other currency symbol as set by ~FatTable.currency_symbol~ are removed from
|
472
|
+
the string and if the remaining string can be interpreted as a ~Numeric~, it
|
473
|
+
will be. It is interpreted as an ~Integer~ if there are no decimal places in
|
474
|
+
the remaining string, as a ~Rational~ if the string has the form
|
475
|
+
~<number>:<number>~ or ~<number>/<number>~, or as a ~BigDecimal~ if there is
|
476
|
+
a decimal point in the remaining string. Empty strings in a column already
|
477
|
+
having the Numeric type are converted to nil.
|
478
|
+
- String :: If all else fails, ~FatTable~ applies ~#to_s~ to the input value
|
279
479
|
and, treats it as an item of type ~String~. Empty strings in a column
|
280
480
|
already having the ~String~ type are kept as empty strings.
|
281
|
-
- NilClass ::
|
481
|
+
- NilClass :: Until the input contains a non-blank string that can be parsed as
|
282
482
|
one of the other types, it has this type, meaning that the type is still
|
283
483
|
open. A column comprised completely of blank strings or ~nils~ will retain
|
284
484
|
the ~NilClass~ type.
|
285
485
|
|
286
486
|
*** Headers
|
287
|
-
|
288
487
|
Headers for the columns are formed from the input. No two columns in a table can
|
289
488
|
have the same header. Headers in the input are converted to symbols by
|
290
489
|
|
@@ -293,33 +492,36 @@ have the same header. Headers in the input are converted to symbols by
|
|
293
492
|
- removing any characters that are not letters, numbers, or underscores, and
|
294
493
|
- lowercasing all remaining letters
|
295
494
|
|
296
|
-
Thus, a header of ~
|
495
|
+
Thus, a header of ~Date~ becomes ~:date~, a header of ~Id Number~ becomes,
|
297
496
|
~:id_number~, etc. When referring to a column in code, you must use the symbol
|
298
497
|
form of the header.
|
299
498
|
|
300
499
|
If no sensible headers can be discerned from the input, headers of the form
|
301
500
|
~:col_1~, ~:col_2~, etc., are synthesized.
|
302
501
|
|
303
|
-
|
502
|
+
You should avoid the use of the column names ~:omni~ and ~:sort_key~ because
|
503
|
+
they have special meanings in the ~select~ and ~order_with~ commands,
|
504
|
+
respectively.
|
304
505
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
506
|
+
*** Groups
|
507
|
+
The rows of a ~FatTable~ table can be divided into groups, either from markers
|
508
|
+
in the input or as a result of certain operations. There is only one level of
|
509
|
+
grouping, so ~FatTable~ has no concept of sub-groups. Groups can be shown on
|
510
|
+
output with rules or "hlines" that underline the last row in each group, and
|
511
|
+
you can decorate the output with group footers that summarize the rows in
|
512
|
+
each group.
|
311
513
|
|
312
514
|
** Constructing Tables
|
313
515
|
*** Empty Tables
|
516
|
+
**** Without Headers
|
517
|
+
You can create an empty table with ~FatTable::Table.new~ or, the shorter form,
|
518
|
+
~FatTable.new~, and then add rows with the ~<<~ operator and a Hash. The keys
|
519
|
+
in the added rows determine the names of the headers:
|
314
520
|
|
315
|
-
|
316
|
-
~<<~ operator and a Hash:
|
317
|
-
|
318
|
-
#+BEGIN_SRC ruby
|
521
|
+
#+BEGIN_SRC ruby :results silent
|
319
522
|
tab = FatTable.new
|
320
|
-
tab << { a: 1, b: 2, c: "<2017-01-21>
|
523
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '' }
|
321
524
|
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
322
|
-
tab.to_aoa
|
323
525
|
#+END_SRC
|
324
526
|
|
325
527
|
After this, the table will have column headers ~:a~, ~:b~, ~:c~, ~:d~, and ~:e~.
|
@@ -328,14 +530,187 @@ Column, ~:a~ and ~:b~ will have type Numeric, column ~:c~ will have type
|
|
328
530
|
have an open type. Notice that dates in the input can be wrapped in brackets as
|
329
531
|
in org-mode time stamps.
|
330
532
|
|
533
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
534
|
+
tab.to_text
|
535
|
+
#+END_SRC
|
536
|
+
|
537
|
+
#+begin_EXAMPLE
|
538
|
+
+======+======+============+===+===+
|
539
|
+
| A | B | C | D | E |
|
540
|
+
+------+------+------------+---+---+
|
541
|
+
| 1 | 2 | 2017-01-21 | F | |
|
542
|
+
| 3.14 | 2.17 | 2016-01-21 | T | |
|
543
|
+
+======+======+============+===+===+
|
544
|
+
#+end_EXAMPLE
|
545
|
+
|
546
|
+
You can continue to add rows to the table:
|
547
|
+
#+BEGIN_SRC ruby :results silent
|
548
|
+
tab << { 'F' => '335:113', a: Rational(3, 5) }
|
549
|
+
#+END_SRC
|
550
|
+
|
551
|
+
This last ~<<~ operation adds a new column headed ~:f~ to the table and makes
|
552
|
+
the value of =:f= in all prior rows ~nil~. Also, the values for the new row
|
553
|
+
for which no key was give are assigned ~nil~ as well:
|
554
|
+
|
555
|
+
#+BEGIN_SRC ruby
|
556
|
+
tab.to_text
|
557
|
+
#+END_SRC
|
558
|
+
|
559
|
+
#+begin_EXAMPLE
|
560
|
+
+======+======+============+===+===+=========+
|
561
|
+
| A | B | C | D | E | F |
|
562
|
+
+------+------+------------+---+---+---------+
|
563
|
+
| 1 | 2 | 2017-01-21 | F | | |
|
564
|
+
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
565
|
+
+------+------+------------+---+---+---------+
|
566
|
+
| 3/5 | | | | | 335/113 |
|
567
|
+
+======+======+============+===+===+=========+
|
568
|
+
#+end_EXAMPLE
|
569
|
+
|
570
|
+
**** With Headers
|
571
|
+
Alternatively, you can specify the headers at the outset, in which case,
|
572
|
+
headers in added rows that do not match any of the initial headers cause new
|
573
|
+
columns to be created:
|
574
|
+
|
575
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE :results raw
|
576
|
+
require 'fat_table'
|
577
|
+
tab = FatTable.new(:a, 'b', 'C', :d)
|
578
|
+
tab.headers
|
579
|
+
#+END_SRC
|
580
|
+
|
581
|
+
#+begin_EXAMPLE
|
582
|
+
[:a, :b, :c, :d]
|
583
|
+
#+end_EXAMPLE
|
584
|
+
|
585
|
+
#+begin_src ruby :wrap EXAMPLE
|
586
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '' }
|
587
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
588
|
+
tab.to_text
|
589
|
+
#+end_src
|
590
|
+
|
591
|
+
#+begin_EXAMPLE
|
592
|
+
+======+======+============+===+===+
|
593
|
+
| A | B | C | D | E |
|
594
|
+
+------+------+------------+---+---+
|
595
|
+
| 1 | 2 | 2017-01-21 | F | |
|
596
|
+
| 3.14 | 2.17 | 2016-01-21 | T | |
|
597
|
+
+------+------+------------+---+---+
|
598
|
+
| 1 | 2 | 2017-01-21 | F | |
|
599
|
+
| 3.14 | 2.17 | 2016-01-21 | T | |
|
600
|
+
+======+======+============+===+===+
|
601
|
+
#+end_EXAMPLE
|
602
|
+
|
603
|
+
**** Forcing String Type
|
604
|
+
Occasionally, ~FatTable~'s automatic type detection can get in the way and you
|
605
|
+
just want it to treat one or more columns as Strings regardless of their
|
606
|
+
appearance. Think, for example, of zip codes. At any time after creating a
|
607
|
+
table, you can have it force the String type on any number of columns with the
|
608
|
+
~force_string!~ method. When you do so, all exisiting items in the column are
|
609
|
+
converted to strings with the #to_s method.
|
610
|
+
|
611
|
+
#+begin_src ruby :wrap EXAMPLE
|
612
|
+
tab = FatTable.new(:a, 'b', 'C', :d, :zip)
|
613
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
614
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
615
|
+
tab.force_string!(:zip, :c)
|
616
|
+
tab << { zip: '01879' }
|
617
|
+
tab << { zip: '66210' }
|
618
|
+
tab << { zip: '90210' }
|
619
|
+
tab.to_text
|
620
|
+
#+end_src
|
621
|
+
|
622
|
+
#+begin_EXAMPLE
|
623
|
+
+======+======+============+===+=======+===+
|
624
|
+
| A | B | C | D | Zip | E |
|
625
|
+
+------+------+------------+---+-------+---+
|
626
|
+
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
627
|
+
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
628
|
+
| | | | | 01879 | |
|
629
|
+
| | | | | 66210 | |
|
630
|
+
| | | | | 90210 | |
|
631
|
+
+======+======+============+===+=======+===+
|
632
|
+
#+end_EXAMPLE
|
633
|
+
|
634
|
+
**** Designating "Tolerant" Columns
|
635
|
+
Related to the problem just discussed is the problem of reading files in from
|
636
|
+
the wild where a column may get typed as, say Numeric, but then contain
|
637
|
+
something that can't be parsed as a Numeric. ~FatTable~ raises an exception
|
638
|
+
is such cases, and that may be what you want if you can control the input.
|
639
|
+
But, especially when you cannot do so, it can be helpful to designate one or
|
640
|
+
more columns as "tolerant." This means that when a conversion problem occurs,
|
641
|
+
the column is forced to String type instead of throwing an exception, and the
|
642
|
+
table can continue to be read.
|
643
|
+
|
644
|
+
All of the table construction methods, allow a keyword parameter,
|
645
|
+
~tolerant_columns~, where you can designate what columns should be convert to
|
646
|
+
String type when conversion to the auto-typed column type is not possible.
|
647
|
+
The parameter should be an array of headers, in either string or symbol form,
|
648
|
+
for which this behavior is desired. In addition, it can be set to the special
|
649
|
+
string '*' or symbol ~:*~ to indicate that all the columns should be made
|
650
|
+
tolerant.
|
651
|
+
|
652
|
+
#+begin_src ruby :wrap EXAMPLE
|
653
|
+
require 'fat_table'
|
654
|
+
tab = FatTable.new(:a, 'b', 'C', :d, :zip, tolerant_columns: [:zip])
|
655
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
656
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
657
|
+
tab << { zip: '01879--7884' }
|
658
|
+
tab << { zip: '66210' }
|
659
|
+
tab << { zip: '90210' }
|
660
|
+
tab.to_text
|
661
|
+
#+end_src
|
662
|
+
|
663
|
+
#+RESULTS:
|
664
|
+
#+begin_EXAMPLE
|
665
|
+
+======+======+============+===+=============+===+
|
666
|
+
| A | B | C | D | Zip | E |
|
667
|
+
+------+------+------------+---+-------------+---+
|
668
|
+
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
669
|
+
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
670
|
+
| | | | | 01879--7884 | |
|
671
|
+
| | | | | 66210 | |
|
672
|
+
| | | | | 90210 | |
|
673
|
+
+======+======+============+===+=============+===+
|
674
|
+
#+end_EXAMPLE
|
675
|
+
|
676
|
+
Another way to designate a column as tolerant is to end a column you want to
|
677
|
+
designate as tolerant with a ~!~. The ~!~ will be stripped from the header,
|
678
|
+
but it will be marked as tolerant.
|
679
|
+
#+begin_src ruby :wrap EXAMPLE
|
680
|
+
require 'fat_table'
|
681
|
+
tab = FatTable.new(:a, 'b!', 'C', :d, :zip!)
|
682
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
683
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
684
|
+
tab << { zip: '01879--7884' }
|
685
|
+
tab << { zip: '66210', b: 'Not a Number' }
|
686
|
+
tab << { zip: '90210' }
|
687
|
+
tab.to_text
|
688
|
+
#+end_src
|
689
|
+
|
690
|
+
#+RESULTS:
|
691
|
+
#+begin_EXAMPLE
|
692
|
+
+======+==============+============+===+=============+===+
|
693
|
+
| A | B | C | D | Zip | E |
|
694
|
+
+------+--------------+------------+---+-------------+---+
|
695
|
+
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
696
|
+
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
697
|
+
| | | | | 01879--7884 | |
|
698
|
+
| | Not a Number | | | 66210 | |
|
699
|
+
| | | | | 90210 | |
|
700
|
+
+======+==============+============+===+=============+===+
|
701
|
+
#+end_EXAMPLE
|
702
|
+
|
331
703
|
*** From CSV or Org Mode files or strings
|
704
|
+
Tables can also be read from ~.csv~ files or files containing ~org-mode~
|
705
|
+
tables. Remember that you can make any column tolerant with a
|
706
|
+
~tolerant_columns:~ keyword argument or make them all tolerant by designating
|
707
|
+
the pseudo-column ~:*~ as tolerant.
|
332
708
|
|
333
|
-
Tables can also be read from ~.csv~ files or files containing ~org-mode~ tables.
|
334
709
|
In the case of org-mode files, ~FatTable~ skips through the file until it finds
|
335
710
|
a line that look like a table, that is, it begins with any number of spaces
|
336
711
|
followed by ~|-~. Only the first table in an ~.org~ file is read.
|
337
712
|
|
338
|
-
For both ~.csv~ and ~.org~ files, the first row in the
|
713
|
+
For both ~.csv~ and ~.org~ files, the first row in the table is taken as the
|
339
714
|
header row, and the headers are converted to symbols as described above.
|
340
715
|
|
341
716
|
#+BEGIN_SRC ruby
|
@@ -385,9 +760,11 @@ header row, and the headers are converted to symbols as described above.
|
|
385
760
|
#+END_SRC
|
386
761
|
|
387
762
|
*** From Arrays of Arrays
|
388
|
-
|
389
|
-
You can also initialize a table directly from ruby data structures. You can,
|
390
|
-
example, build a table from an array of arrays
|
763
|
+
**** In Ruby Code
|
764
|
+
You can also initialize a table directly from ruby data structures. You can,
|
765
|
+
for example, build a table from an array of arrays. Remember that you can
|
766
|
+
make any column tolerant with a ~tolerant_columns:~ keyword argument or make
|
767
|
+
them all tolerant by designating the pseudo-column ~:*~ as tolerant.
|
391
768
|
|
392
769
|
#+BEGIN_SRC ruby
|
393
770
|
aoa = [
|
@@ -411,6 +788,7 @@ example, build a table from an array of arrays:
|
|
411
788
|
Notice that the values can either be ruby objects, such as the Integer ~85_000~,
|
412
789
|
or strings that can be parsed into one of the permissible column types.
|
413
790
|
|
791
|
+
**** In Emacs Org Files
|
414
792
|
This method of building a table, ~.from_aoa~, is particularly useful in dealing
|
415
793
|
with Emacs org-mode code blocks. Tables in org-mode are passed to code blocks as
|
416
794
|
arrays of arrays. Likewise, a result of a code block in the form of an array of
|
@@ -480,15 +858,17 @@ This example illustrates several things:
|
|
480
858
|
|
481
859
|
A second ruby data structure that can be used to initialize a ~FatTable~ table
|
482
860
|
is an array of ruby Hashes. Each hash represents a row of the table, and the
|
483
|
-
headers of the table are taken from the keys of the hashes. Accordingly, all
|
484
|
-
hashes must have the same keys.
|
861
|
+
headers of the table are taken from the keys of the hashes. Accordingly, all
|
862
|
+
the hashes must have the same keys. Remember that you can make any column
|
863
|
+
tolerant with a ~tolerant_columns:~ keyword argument or make them all tolerant
|
864
|
+
by designating the pseudo-column ~:*~ as tolerant.
|
485
865
|
|
486
866
|
This same method can in fact take an array of any objects that can be converted
|
487
867
|
to a Hash with the ~#to_h~ method, so you can use an array of your own objects
|
488
868
|
to initialize a table, provided that you define a suitable ~#to_h~ method for
|
489
869
|
the objects' class.
|
490
870
|
|
491
|
-
#+BEGIN_SRC ruby
|
871
|
+
#+BEGIN_SRC ruby :results silent
|
492
872
|
aoh = [
|
493
873
|
{ ref: 'T001', date: '2016-11-01', code: 'P', price: '7.7000', shares: 100 },
|
494
874
|
{ ref: 'T002', date: '2016-11-01', code: 'P', price: 7.7500, shares: 200 },
|
@@ -514,7 +894,6 @@ Notice, again, that the values can either be ruby objects, such as ~Date.today~,
|
|
514
894
|
or strings that can be parsed into one of the permissible column types.
|
515
895
|
|
516
896
|
*** From SQL queries
|
517
|
-
|
518
897
|
Another way to initialize a ~FatTable~ table is with the results of a SQL
|
519
898
|
query. Before you can connect to a database, you need to make sure that the required
|
520
899
|
adapter for your database is installed. ~FatTable~ uses the ~sequel~ gem
|
@@ -529,15 +908,30 @@ You must first set the database parameters to be used for the queries.
|
|
529
908
|
|
530
909
|
#+BEGIN_SRC ruby
|
531
910
|
# This automatically requires sequel.
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
user: 'ken',
|
536
|
-
password: 'imsecret',
|
537
|
-
host: 'db.lan')
|
538
|
-
tab = FatTable.from_sql('select * from trades;')
|
911
|
+
FatTable.connect(adapter: 'sqlite',
|
912
|
+
database: 'examples/trades.db')
|
913
|
+
tab = FatTable.from_sql('select * from trans;').to_text
|
539
914
|
#+END_SRC
|
540
915
|
|
916
|
+
#+begin_example
|
917
|
+
+============+======+==========+==========+=========+=========+====+
|
918
|
+
| Date | Code | Raw | Shares | Price | Info | Ok |
|
919
|
+
+------------+------+----------+----------+---------+---------+----+
|
920
|
+
| 2013-05-29 | S | 15700.0 | 6601.85 | 24.779 | ENTITY3 | F |
|
921
|
+
| 2013-05-02 | P | 118186.4 | 118186.4 | 11.85 | ENTITY1 | T |
|
922
|
+
| 2013-05-20 | S | 12000.0 | 5046.0 | 28.2804 | ENTITY3 | F |
|
923
|
+
| 2013-05-23 | S | 8000.0 | 3364.0 | 27.1083 | ENTITY3 | T |
|
924
|
+
| 2013-05-23 | S | 39906.0 | 16780.47 | 25.1749 | ENTITY3 | T |
|
925
|
+
| 2013-05-20 | S | 85000.0 | 35742.5 | 28.3224 | ENTITY3 | T |
|
926
|
+
| 2013-05-02 | P | 795546.2 | 795546.2 | 1.185 | ENTITY1 | T |
|
927
|
+
| 2013-05-29 | S | 13459.0 | 5659.51 | 24.7464 | ENTITY3 | T |
|
928
|
+
| 2013-05-20 | S | 33302.0 | 14003.49 | 28.6383 | ENTITY3 | T |
|
929
|
+
| 2013-05-29 | S | 15900.0 | 6685.95 | 24.5802 | ENTITY3 | T |
|
930
|
+
| 2013-05-30 | S | 6679.0 | 2808.52 | 25.0471 | ENTITY3 | T |
|
931
|
+
| 2013-05-23 | S | 23054.0 | 9694.21 | 26.8015 | ENTITY3 | F |
|
932
|
+
+============+======+==========+==========+=========+=========+====+
|
933
|
+
#+end_example
|
934
|
+
|
541
935
|
The arguments to ~connect~ are simply passed on to ~sequel~'s connect method, so
|
542
936
|
any set of arguments that work for it should work for ~connect~. Alternatively,
|
543
937
|
you can build the ~Sequel~ connection directly with ~Sequel.connect~ or with
|
@@ -545,7 +939,6 @@ adapter-specific ~Sequel~ connection methods and let ~FatTable~ know to use that
|
|
545
939
|
connection:
|
546
940
|
|
547
941
|
#+BEGIN_SRC ruby
|
548
|
-
require 'fat_table'
|
549
942
|
FatTable.db = Sequel.connect('postgres://user:password@localhost/dbname')
|
550
943
|
FatTable.db = Sequel.ado(conn_string: 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=drive:\path\filename.accdb')
|
551
944
|
#+END_SRC
|
@@ -557,7 +950,18 @@ The ~.connect~ function need only be called once, and the database handle it
|
|
557
950
|
creates will be used for all subsequent ~.from_sql~ calls until ~.connect~ is
|
558
951
|
called again.
|
559
952
|
|
953
|
+
Remember that you can make any column tolerant with a ~tolerant_columns:~
|
954
|
+
keyword argument or make them all tolerant by designating the pseudo-column
|
955
|
+
~:*~ as tolerant.
|
956
|
+
|
560
957
|
*** Marking Groups in Input
|
958
|
+
**** Manually
|
959
|
+
At any point, you can add a boundary to a table by invokong the
|
960
|
+
~mark_boundary~ method. Without an argument, it adds the boundary to the end
|
961
|
+
of the table; with a numeric argument, ~n~, it adds the boundary after row
|
962
|
+
~n~.
|
963
|
+
|
964
|
+
**** When Reading in Tables
|
561
965
|
|
562
966
|
~FatTable~ tables has a concept of "groups" of rows that play a role in many of
|
563
967
|
the methods for operating on them as explained [[Groups][below]].
|
@@ -577,13 +981,12 @@ the form ~:col_1~, ~:col_2~, ... ~:col_n~.
|
|
577
981
|
In org mode table text passed to ~.from_org_file~ and ~.from_org_string~, you
|
578
982
|
/must/ mark the header row by following it with an hrule and you /may/ mark
|
579
983
|
group boundaries with an hrule. In org mode tables, hlines are table rows
|
580
|
-
beginning with something like
|
984
|
+
beginning with something like ~|---~. The ~.from_org_...~ functions always
|
581
985
|
recognizes hlines in the input, so it takes no ~hlines:~ keyword parameter.
|
582
986
|
|
583
987
|
** Accessing Parts of Tables
|
584
988
|
|
585
989
|
*** Rows
|
586
|
-
|
587
990
|
A ~FatTable~ table is an Enumerable, yielding each row of the table as a Hash
|
588
991
|
keyed on the header symbols. The method ~Table#rows~ returns an Array of the
|
589
992
|
rows as Hashes as well.
|
@@ -594,15 +997,13 @@ data row of the table, while ~tab[0]~ returns the first row and tab[-1] returns
|
|
594
997
|
the last row.
|
595
998
|
|
596
999
|
*** Columns
|
597
|
-
|
598
1000
|
If the index provided to ~[]~ is a string or a symbol, it returns an Array of
|
599
1001
|
the items of the column with that header. Thus, ~tab[:ref]~ returns an Array of
|
600
1002
|
all the items of the table's ~:ref~ column.
|
601
1003
|
|
602
1004
|
*** Cells
|
603
|
-
|
604
|
-
|
605
|
-
table:
|
1005
|
+
The two forms of indexing can be combined, in either order, to access
|
1006
|
+
individual cells of the table:
|
606
1007
|
|
607
1008
|
#+BEGIN_SRC ruby
|
608
1009
|
tab[13] # => Hash of the 14th row
|
@@ -612,53 +1013,76 @@ table:
|
|
612
1013
|
#+END_SRC
|
613
1014
|
|
614
1015
|
*** Other table attributes
|
1016
|
+
Here is a quick rundown of other table attributes that you can access:
|
615
1017
|
|
616
1018
|
#+BEGIN_SRC ruby
|
617
1019
|
tab.headers # => an Array of the headers in symbol form
|
618
1020
|
tab.types # => a Hash mapping headers to column types
|
1021
|
+
tab.type(head) # => return the type of the column for the given head
|
619
1022
|
tab.size # => the number of rows in the table
|
620
1023
|
tab.width # => the number of columns in the table
|
621
1024
|
tab.empty? # => is the table empty?
|
622
|
-
tab.column
|
1025
|
+
tab.column(head) # => return the FatTable::Column object for the given head
|
1026
|
+
tab.column?(head) # => does the table have a column with the given head?
|
623
1027
|
tab.groups # => return an Array of the table's groups as Arrays of row Hashes.
|
624
1028
|
#+END_SRC
|
625
1029
|
|
626
|
-
|
1030
|
+
You should note that what the ~.types~ and ~.type(head)~ methods return is a
|
1031
|
+
string naming the "type" assigned by ~FatTable~. All of them are also the
|
1032
|
+
names of Ruby classes except to 'Boolean' a class that doesn't exist in Ruby.
|
1033
|
+
The value ~true~ is a member of the ~TrueClass~ and ~false~ a member of the
|
1034
|
+
~FalseClass~. So for ~FatTable~ to provide a column of type 'Boolean'
|
1035
|
+
requires it to synthesize the type from these Ruby classes.
|
1036
|
+
|
1037
|
+
#+begin_src ruby :wrap EXAMPLE :results raw
|
1038
|
+
tab.types
|
1039
|
+
#+end_src
|
1040
|
+
|
1041
|
+
#+begin_EXAMPLE
|
1042
|
+
{:a=>"Numeric", :b=>"Numeric", :c=>"DateTime", :d=>"Boolean", :e=>"NilClass", :f=>"Numeric"}
|
1043
|
+
#+end_EXAMPLE
|
1044
|
+
|
1045
|
+
#+begin_src ruby :wrap EXAMPLE :results output
|
1046
|
+
puts "Column :d says its type is '#{tab.type(:d)}' and that is a #{tab.type(:d).class}"
|
1047
|
+
#+end_src
|
1048
|
+
|
1049
|
+
#+begin_EXAMPLE
|
1050
|
+
Column :d says its type is 'Boolean' and that is a String
|
1051
|
+
#+end_EXAMPLE
|
627
1052
|
|
1053
|
+
** Operations on Tables
|
628
1054
|
Once you have one or more tables, you will likely want to perform operations on
|
629
1055
|
them. The operations provided by ~FatTable~ are the subject of this section.
|
630
1056
|
Before getting into the operations, though, there are a couple of issues that
|
631
1057
|
cut across all or many of the operations.
|
632
1058
|
|
633
1059
|
First, tables are by and large immutable objects. Each operation creates a new
|
634
|
-
table without affecting the input tables. The only
|
635
|
-
operation, which mutates the receiver table by removing its group
|
1060
|
+
table without affecting the input tables. The only exceptions are the
|
1061
|
+
~degroup!~ operation, which mutates the receiver table by removing its group
|
1062
|
+
boundaries, and ~force_string!~ (explained above at [[*Forcing String Type][Forcing String Type]]),
|
1063
|
+
which forces columns to have the String type despite what the automatic typing
|
1064
|
+
rules determine.
|
636
1065
|
|
637
1066
|
Second, because each operation returns a ~FatTable::Table~ object, the
|
638
1067
|
operations are chainable.
|
639
1068
|
|
640
|
-
<<Groups>>
|
641
1069
|
Third, ~FatTable::Table~ objects can have "groups" of rows within the table.
|
642
|
-
These can be decorated with hlines and group footers on output. Some
|
643
|
-
operations result in marking group boundaries in the result table, others
|
644
|
-
group boundaries that may have existed in the input table. Operations
|
645
|
-
either create or remove groups will be noted below.
|
1070
|
+
These can be decorated with hlines and group footers on output. Some
|
1071
|
+
operations result in marking group boundaries in the result table, others
|
1072
|
+
remove group boundaries that may have existed in the input table. Operations
|
1073
|
+
that either create or remove groups will be noted below.
|
646
1074
|
|
647
1075
|
Finally, the operations are for the most part patterned on SQL table operations,
|
648
1076
|
but when expressions play a role, you write them using ruby syntax rather than
|
649
1077
|
SQL.
|
650
1078
|
|
651
|
-
*** Example Input
|
652
|
-
|
1079
|
+
*** Example Input Tables
|
653
1080
|
For illustration purposes assume that the following tables are read into ruby
|
654
|
-
variables called
|
1081
|
+
variables called ~tab1~ and ~tab2~. We have given the table groups, marked by
|
655
1082
|
the hlines below, and included some duplicate rows to illustrate the effect of
|
656
1083
|
certain operations on groups and duplicates.
|
657
1084
|
|
658
|
-
#+
|
659
|
-
#+BEGIN_SRC ruby
|
660
|
-
require 'fat_table'
|
661
|
-
|
1085
|
+
#+BEGIN_SRC ruby :results silent
|
662
1086
|
tab1_str = <<-EOS
|
663
1087
|
| Ref | Date | Code | Price | G10 | QP10 | Shares | LP | QP | IPLP | IPQP |
|
664
1088
|
|------+------------------+------+--------+-----+------+--------+------+-------+--------+--------|
|
@@ -708,26 +1132,31 @@ tab2_str = <<-EOS
|
|
708
1132
|
| T021 | [2017-01-23 Mon] | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
|
709
1133
|
| T021 | [2017-01-23 Mon] | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
|
710
1134
|
EOS
|
1135
|
+
#+END_SRC
|
711
1136
|
|
712
|
-
tab1
|
713
|
-
|
1137
|
+
Rendering ~tab1~ into Emacs org-mode:
|
1138
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE :results silent
|
1139
|
+
tab1 = FatTable.from_org_string(tab1_str)
|
714
1140
|
#+END_SRC
|
715
1141
|
|
716
|
-
|
1142
|
+
Rendering ~tab2~ into Emacs org-mode:
|
717
1143
|
|
718
|
-
|
719
|
-
|
720
|
-
|
1144
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE :results silent
|
1145
|
+
tab2 = FatTable.from_org_string(tab2_str)
|
1146
|
+
#+END_SRC
|
721
1147
|
|
722
|
-
|
1148
|
+
*** Select
|
1149
|
+
With the ~select~ method, you can select columns to appear in the output
|
1150
|
+
table, rearrange their order, and create new columns that are a function of
|
1151
|
+
other columns.
|
723
1152
|
|
1153
|
+
**** Selecting Existing Columns (Also of :omni)
|
724
1154
|
Here we select three existing columns by simply passing header symbols in the
|
725
1155
|
order we want them to appear in the output. Thus, one use of =select= is to
|
726
1156
|
filter and permute the order of existing columns. The =select= method preserves
|
727
1157
|
any group boundaries present in the input table.
|
728
1158
|
|
729
|
-
#+
|
730
|
-
#+BEGIN_SRC ruby
|
1159
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
731
1160
|
tab1.select(:price, :ref, :shares).to_aoa
|
732
1161
|
#+END_SRC
|
733
1162
|
|
@@ -758,15 +1187,91 @@ any group boundaries present in the input table.
|
|
758
1187
|
| 8.25 | T016 | 100 |
|
759
1188
|
#+END_EXAMPLE
|
760
1189
|
|
761
|
-
|
1190
|
+
It can be tedious to type the names of all the columns in a ~select~
|
1191
|
+
statement, so ~FatTable~ recognizes the special column name ~:omni~. If the
|
1192
|
+
~select~'s first and only column argument is ~:omni~, it will expand to the
|
1193
|
+
names of all the existing columns in the table. Use of ~:omni~ otherwise is
|
1194
|
+
not interpreted specially, so you will get an error complaining about a
|
1195
|
+
non-existent column unless you happen to have a column named ~:omni~ in your
|
1196
|
+
table, which is not advisable. You can add hash arguments after ~:omni~ but
|
1197
|
+
you cannot add additional column names:
|
1198
|
+
|
1199
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1200
|
+
tab1.select(:omni, cost: 'shares * price').to_aoa
|
1201
|
+
#+END_SRC
|
1202
|
+
|
1203
|
+
#+begin_EXAMPLE
|
1204
|
+
| Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp | Cost |
|
1205
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1206
|
+
| T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 | 770.0 |
|
1207
|
+
| T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 | 1550.0 |
|
1208
|
+
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 | 6000.0 |
|
1209
|
+
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 | 6000.0 |
|
1210
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1211
|
+
| T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 | 51423.05 |
|
1212
|
+
| T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 | 30000.0 |
|
1213
|
+
| T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 | 7600.0 |
|
1214
|
+
| T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 | 7600.0 |
|
1215
|
+
| T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 | 1530.0 |
|
1216
|
+
| T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 | 21198.15 |
|
1217
|
+
| T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 | 72580.0 |
|
1218
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1219
|
+
| T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 | 23971.25 |
|
1220
|
+
| T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 | 742.5 |
|
1221
|
+
| T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 | 35485.0 |
|
1222
|
+
| T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 | 35485.0 |
|
1223
|
+
| T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 | 390285.0 |
|
1224
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1225
|
+
| T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 | 43560.15 |
|
1226
|
+
| T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 | 3875.0 |
|
1227
|
+
| T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 | 825.0 |
|
1228
|
+
#+end_EXAMPLE
|
1229
|
+
|
1230
|
+
**** Copying and Renaming Existing Columns
|
1231
|
+
After the list of selected column names in the call to ~select~, you can add
|
1232
|
+
any number of hash-like arguments. You can use these to add a copy of an
|
1233
|
+
existing column. By calling select again, you can include only the copied
|
1234
|
+
column, in effect renaming it. For example, if you want ~tab1~ but with ~:ref~
|
1235
|
+
changed to ~:id~, just add an argument to define the new ~:id~ column:
|
762
1236
|
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
1237
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1238
|
+
tab1.select(:omni, id: :ref).
|
1239
|
+
select(:id, :date, :code, :price, :shares).to_aoa
|
1240
|
+
#+END_SRC
|
1241
|
+
|
1242
|
+
#+begin_EXAMPLE
|
1243
|
+
| Id | Date | Code | Price | Shares |
|
1244
|
+
|------+------------+------+-------+--------|
|
1245
|
+
| T001 | 2016-11-01 | P | 7.7 | 100 |
|
1246
|
+
| T002 | 2016-11-01 | P | 7.75 | 200 |
|
1247
|
+
| T003 | 2016-11-01 | P | 7.5 | 800 |
|
1248
|
+
| T003 | 2016-11-01 | P | 7.5 | 800 |
|
1249
|
+
|------+------------+------+-------+--------|
|
1250
|
+
| T004 | 2016-11-01 | S | 7.55 | 6811 |
|
1251
|
+
| T005 | 2016-11-01 | S | 7.5 | 4000 |
|
1252
|
+
| T006 | 2016-11-01 | S | 7.6 | 1000 |
|
1253
|
+
| T006 | 2016-11-01 | S | 7.6 | 1000 |
|
1254
|
+
| T007 | 2016-11-01 | S | 7.65 | 200 |
|
1255
|
+
| T008 | 2016-11-01 | P | 7.65 | 2771 |
|
1256
|
+
| T009 | 2016-11-01 | P | 7.6 | 9550 |
|
1257
|
+
|------+------------+------+-------+--------|
|
1258
|
+
| T010 | 2016-11-01 | P | 7.55 | 3175 |
|
1259
|
+
| T011 | 2016-11-02 | P | 7.425 | 100 |
|
1260
|
+
| T012 | 2016-11-02 | P | 7.55 | 4700 |
|
1261
|
+
| T012 | 2016-11-02 | P | 7.55 | 4700 |
|
1262
|
+
| T013 | 2016-11-02 | P | 7.35 | 53100 |
|
1263
|
+
|------+------------+------+-------+--------|
|
1264
|
+
| T014 | 2016-11-02 | P | 7.45 | 5847 |
|
1265
|
+
| T015 | 2016-11-02 | P | 7.75 | 500 |
|
1266
|
+
| T016 | 2016-11-02 | P | 8.25 | 100 |
|
1267
|
+
#+end_EXAMPLE
|
1268
|
+
|
1269
|
+
**** Adding New Columns
|
1270
|
+
More interesting is that ~select~ can take hash-like keyword arguments after
|
1271
|
+
the symbol arguments to create new columns in the output as functions of other
|
1272
|
+
columns. For each hash-like parameter, the keyword given must be a symbol,
|
1273
|
+
which becomes the header for the new column, and the value can be a string
|
1274
|
+
representing a ruby expression for the value of a new column.
|
770
1275
|
|
771
1276
|
Within the string expression, the names of existing or already-specified
|
772
1277
|
columns are available as local variables. In addition the instance variables
|
@@ -781,8 +1286,7 @@ variables are set the number of the current row and group number respectively.
|
|
781
1286
|
For example, if we want to rename the ~traded_on~ column to ~:date~ and add a
|
782
1287
|
new column to compute the cost of shares, we could do the following:
|
783
1288
|
|
784
|
-
#+
|
785
|
-
#+BEGIN_SRC ruby
|
1289
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
786
1290
|
tab1.select(:ref, :price, :shares, traded_on: :date, cost: 'price * shares').to_aoa
|
787
1291
|
#+END_SRC
|
788
1292
|
|
@@ -813,8 +1317,8 @@ new column to compute the cost of shares, we could do the following:
|
|
813
1317
|
| T016 | 8.25 | 100 | 2016-11-02 | 825.0 |
|
814
1318
|
#+END_EXAMPLE
|
815
1319
|
|
816
|
-
The parameter
|
817
|
-
to be renamed
|
1320
|
+
The parameter ~traded_on: :date~ caused the ~:date~ column of the input table
|
1321
|
+
to be renamed ~:traded_on~, and the parameter ~cost: 'price * shares'~ created
|
818
1322
|
a new column, ~:cost~, as the product of values in the ~:price~ and ~:shares~
|
819
1323
|
columns.
|
820
1324
|
|
@@ -822,14 +1326,12 @@ The order of the columns in the result tables is the same as the order of the
|
|
822
1326
|
parameters to the ~select~ method. So, you can re-order the columns with a
|
823
1327
|
second, chained call to ~select~:
|
824
1328
|
|
825
|
-
#+
|
826
|
-
|
827
|
-
|
828
|
-
.select(:ref, :traded_on, :price, :shares, :cost) \
|
829
|
-
.to_aoa
|
1329
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1330
|
+
tab1.select(:ref, :price, :shares, traded_on: :date, cost: 'price * shares').
|
1331
|
+
select(:ref, :traded_on, :price, :shares, :cost).to_aoa
|
830
1332
|
#+END_SRC
|
831
1333
|
|
832
|
-
#+
|
1334
|
+
#+begin_EXAMPLE
|
833
1335
|
| Ref | Traded On | Price | Shares | Cost |
|
834
1336
|
|------+------------+-------+--------+----------|
|
835
1337
|
| T001 | 2016-11-01 | 7.7 | 100 | 770.0 |
|
@@ -854,10 +1356,9 @@ second, chained call to ~select~:
|
|
854
1356
|
| T014 | 2016-11-02 | 7.45 | 5847 | 43560.15 |
|
855
1357
|
| T015 | 2016-11-02 | 7.75 | 500 | 3875.0 |
|
856
1358
|
| T016 | 2016-11-02 | 8.25 | 100 | 825.0 |
|
857
|
-
#+
|
1359
|
+
#+end_EXAMPLE
|
858
1360
|
|
859
1361
|
**** Custom Instance Variables and Hooks
|
860
|
-
|
861
1362
|
As the above examples demonstrate, the instance variables ~@row~ and ~@group~
|
862
1363
|
are available when evaluating expressions that add new columns. You can also set
|
863
1364
|
up your own instance variables as well for keeping track of things that cross
|
@@ -878,8 +1379,7 @@ shows the cumulative cost after each transaction in our example table. The
|
|
878
1379
|
following example uses the ~ivars:~ and ~before_hook:~ parameters to keep track
|
879
1380
|
of the running cost of shares, then formats the table.
|
880
1381
|
|
881
|
-
#+
|
882
|
-
#+BEGIN_SRC ruby
|
1382
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
883
1383
|
tab = tab1.select(:ref, :price, :shares, traded_on: :date, \
|
884
1384
|
cost: 'price * shares', cumulative: '@total_cost', \
|
885
1385
|
ivars: { total_cost: 0 }, \
|
@@ -917,7 +1417,6 @@ of the running cost of shares, then formats the table.
|
|
917
1417
|
#+END_EXAMPLE
|
918
1418
|
|
919
1419
|
**** Argument Order and Boundaries
|
920
|
-
|
921
1420
|
Notice that ~select~ can take any number of arguments but all the symbol
|
922
1421
|
arguments must come first followed by all the hash-like keyword arguments,
|
923
1422
|
including the special arguments for instance variables and hooks.
|
@@ -926,21 +1425,21 @@ As the example illustrates, ~.select~ transmits any group boundaries in its
|
|
926
1425
|
input table to the result table.
|
927
1426
|
|
928
1427
|
*** Where
|
929
|
-
|
930
1428
|
You can filter the rows of the result table with the ~.where~ method. It takes a
|
931
1429
|
single string expression as an argument which is evaluated in a manner similar
|
932
1430
|
to ~.select~ in which the value of the cells in each column are available as
|
933
1431
|
local variables and the instance variables ~@row~ and ~@group~ are available for
|
934
1432
|
testing. The expression is evaluated for each row, and if the expression
|
935
1433
|
evaluates to a truthy value, the row is included in the output, otherwise it is
|
936
|
-
not.
|
937
|
-
|
1434
|
+
not.
|
1435
|
+
|
1436
|
+
The ~.where~ method removes any group boundaries in the input, so the output
|
1437
|
+
table has only a single group.
|
938
1438
|
|
939
1439
|
Here we select only those even-numbered rows where either of the two boolean
|
940
1440
|
fields is true:
|
941
1441
|
|
942
|
-
#+
|
943
|
-
#+BEGIN_SRC ruby
|
1442
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
944
1443
|
tab1.where('@row.even? && (g10 || qp10)') \
|
945
1444
|
.to_aoa
|
946
1445
|
#+END_SRC
|
@@ -956,7 +1455,6 @@ fields is true:
|
|
956
1455
|
#+END_EXAMPLE
|
957
1456
|
|
958
1457
|
*** Order_by
|
959
|
-
|
960
1458
|
You can sort a table on any number of columns with ~order_by~. The ~order_by~
|
961
1459
|
method takes any number of symbol arguments for the columns to sort on. If you
|
962
1460
|
specify more than one column, the sort is performed on the first column, then
|
@@ -967,8 +1465,7 @@ All columns of the input table are included in the output.
|
|
967
1465
|
|
968
1466
|
Let's sort our table first by ~:code~, then in reverse order of ~:date~.
|
969
1467
|
|
970
|
-
#+
|
971
|
-
#+BEGIN_SRC ruby
|
1468
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
972
1469
|
tab1.order_by(:code, :date!) \
|
973
1470
|
.to_aoa
|
974
1471
|
#+END_SRC
|
@@ -1004,8 +1501,59 @@ input, it adds group boundaries in the output table at those rows where the sort
|
|
1004
1501
|
keys change. Thus, in each group, ~:code~ and ~:date~ are the same, and when
|
1005
1502
|
either changes, ~order_by~ inserts a group boundary.
|
1006
1503
|
|
1007
|
-
***
|
1504
|
+
*** Order_with
|
1505
|
+
The ~order_with~ method is a convenient combination of ~select~ and
|
1506
|
+
~order_by~. It takes a single string expression as an argument to serve as a
|
1507
|
+
sort key---one that would be valid as a select expression---but with an
|
1508
|
+
optional trailing ~!~ to indicate reverse sort. The resulting table has an
|
1509
|
+
additional column called ~:sort_key~ with the expression evaluated for each
|
1510
|
+
row, and the table is sorted as with ~order_by~ on that column.
|
1511
|
+
|
1512
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1513
|
+
tab1.order_with('price * shares').to_aoa
|
1514
|
+
#+END_SRC
|
1008
1515
|
|
1516
|
+
#+begin_EXAMPLE
|
1517
|
+
| Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp | Sort Key |
|
1518
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1519
|
+
| T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 | 742.5 |
|
1520
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1521
|
+
| T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 | 770.0 |
|
1522
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1523
|
+
| T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 | 825.0 |
|
1524
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1525
|
+
| T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 | 1530.0 |
|
1526
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1527
|
+
| T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 | 1550.0 |
|
1528
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1529
|
+
| T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 | 3875.0 |
|
1530
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1531
|
+
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 | 6000.0 |
|
1532
|
+
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 | 6000.0 |
|
1533
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1534
|
+
| T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 | 7600.0 |
|
1535
|
+
| T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 | 7600.0 |
|
1536
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1537
|
+
| T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 | 21198.15 |
|
1538
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1539
|
+
| T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 | 23971.25 |
|
1540
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1541
|
+
| T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 | 30000.0 |
|
1542
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1543
|
+
| T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 | 35485.0 |
|
1544
|
+
| T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 | 35485.0 |
|
1545
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1546
|
+
| T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 | 43560.15 |
|
1547
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1548
|
+
| T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 | 51423.05 |
|
1549
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1550
|
+
| T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 | 72580.0 |
|
1551
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------+----------|
|
1552
|
+
| T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 | 390285.0 |
|
1553
|
+
#+end_EXAMPLE
|
1554
|
+
|
1555
|
+
|
1556
|
+
*** Group_by
|
1009
1557
|
Like ~order_by~, ~group_by~ takes a set of parameters of column header symbols,
|
1010
1558
|
the "grouping parameters", by which to sort the table into a set of groups that
|
1011
1559
|
are equal with respect to values in those columns. In addition, those parameters
|
@@ -1019,8 +1567,7 @@ For example, let's summarize the ~trades~ table by ~:code~ and ~:price~ again,
|
|
1019
1567
|
and determine total shares, average price, and a few other features of each
|
1020
1568
|
group:
|
1021
1569
|
|
1022
|
-
#+
|
1023
|
-
#+BEGIN_SRC ruby
|
1570
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1024
1571
|
tab1.group_by(:code, :date, price: :avg,
|
1025
1572
|
shares: :sum, lp: :sum, qp: :sum,
|
1026
1573
|
qp10: :all?) \
|
@@ -1040,7 +1587,7 @@ hash-like "aggregating" parameters where the key is the column to aggregate and
|
|
1040
1587
|
the value is a symbol for one of several aggregating methods that
|
1041
1588
|
~FatTable::Column~ objects understand. For example, the ~:avg~ method is applied
|
1042
1589
|
to the :price column so that the output shows the average price in each group.
|
1043
|
-
The ~:shares~, ~:lp~, and ~:qp~ columns are summed, and the ~:
|
1590
|
+
The ~:shares~, ~:lp~, and ~:qp~ columns are summed, and the ~:all?~ aggregate is
|
1044
1591
|
applied to one of the boolean fields, that is, it is ~true~ if any of the values
|
1045
1592
|
in that column are ~true~.
|
1046
1593
|
|
@@ -1053,15 +1600,15 @@ will raise an exception.
|
|
1053
1600
|
|
1054
1601
|
- ~first~ :: the first non-nil item in the column,
|
1055
1602
|
- ~last~ :: the last non-nil item in the column,
|
1056
|
-
- ~
|
1057
|
-
|
1058
|
-
- ~sum~ :: for ~Numeric~
|
1059
|
-
values,
|
1603
|
+
- ~range~ :: form a Range ~~{min}..{max}~ to show the range of values in the
|
1604
|
+
column,
|
1605
|
+
- ~sum~ :: for ~Numeric~ columns, apply '+' to all the non-nil
|
1606
|
+
values; for ~String~ columns, join the elements with a single space,
|
1060
1607
|
- ~count~ :: the number of non-nil values in the column,
|
1061
1608
|
- ~min~ :: for ~Numeric~, ~String~, and ~DateTime~ columns, return the smallest
|
1062
|
-
non-nil value in the column,
|
1609
|
+
non-nil, non-blank value in the column,
|
1063
1610
|
- ~max~ :: for ~Numeric~, ~String~, and ~DateTime~ columns, return the largest
|
1064
|
-
non-nil value in the column,
|
1611
|
+
non-nil, non-blank value in the column,
|
1065
1612
|
- ~avg~ :: for ~Numeric~ and ~DateTime~ columns, return the arithmetic mean of
|
1066
1613
|
the non-nil values in the column; with respect to ~Date~ or ~DateTime~
|
1067
1614
|
objects, each is converted to a numeric Julian date, the average is
|
@@ -1093,7 +1640,6 @@ implicit ~order_by~ on the grouping columns is collapsed into a single row.
|
|
1093
1640
|
|
1094
1641
|
*** Join
|
1095
1642
|
**** Join Types
|
1096
|
-
|
1097
1643
|
So far, all the operations have operated on a single table. ~FatTable~ provides
|
1098
1644
|
several ~join~ methods for combining two tables, each of which takes as
|
1099
1645
|
parameters (1) a second table and (2) except in the case of ~cross_join~, zero
|
@@ -1130,7 +1676,6 @@ for inclusion in the joined output table.
|
|
1130
1676
|
M~ rows.
|
1131
1677
|
|
1132
1678
|
**** Join Expressions
|
1133
|
-
|
1134
1679
|
For each of the join types, if no join expressions are given, the tables will be
|
1135
1680
|
joined on columns having the same column header in both tables, and the join
|
1136
1681
|
condition is satisfied when all the values in those columns are equal. If the
|
@@ -1158,48 +1703,74 @@ local variables within the expression, but the instance variables ~@row~ and
|
|
1158
1703
|
~@group~ are not.
|
1159
1704
|
|
1160
1705
|
**** Join Examples
|
1161
|
-
|
1162
1706
|
The following examples are taken from the [[https://www.tutorialspoint.com/postgresql/postgresql_using_joins.htm][Postgresql tutorial]], with some slight
|
1163
1707
|
modifications. The examples will use the following two tables, which are also
|
1164
1708
|
available in ~ft_console~ as ~@tab_a~ and ~@tab_b~:
|
1165
1709
|
|
1166
|
-
#+
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
| 9 | James | 44 | Norway | 5000 | 2005-07-13 |
|
1180
|
-
| 10 | James | 45 | Texas | 5000 | |
|
1181
|
-
EOS
|
1710
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE :results silent
|
1711
|
+
tab_a_str = <<-EOS
|
1712
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
1713
|
+
|----+-------+-----+------------+--------+------------|
|
1714
|
+
| 1 | Paul | 32 | California | 20000 | 2001-07-13 |
|
1715
|
+
| 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
|
1716
|
+
| 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
|
1717
|
+
| 5 | David | 27 | Texas | 85000 | 2007-12-13 |
|
1718
|
+
| 2 | Allen | 25 | Texas | | 2005-07-13 |
|
1719
|
+
| 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
|
1720
|
+
| 9 | James | 44 | Norway | 5000 | 2005-07-13 |
|
1721
|
+
| 10 | James | 45 | Texas | 5000 | |
|
1722
|
+
EOS
|
1182
1723
|
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1724
|
+
tab_b_str = <<-EOS
|
1725
|
+
| Id | Dept | Emp Id |
|
1726
|
+
|----+-------------+--------|
|
1727
|
+
| 1 | IT Billing | 1 |
|
1728
|
+
| 2 | Engineering | 2 |
|
1729
|
+
| 3 | Finance | 7 |
|
1730
|
+
EOS
|
1190
1731
|
|
1191
|
-
|
1192
|
-
tab_b = FatTable.from_org_string(tab_b_str)
|
1732
|
+
tab_b = FatTable.from_org_string(tab_b_str)
|
1193
1733
|
#+END_SRC
|
1194
1734
|
|
1195
|
-
|
1735
|
+
Here is ~tab_a~:
|
1736
|
+
#+begin_src ruby :wrap EXAMPLE
|
1737
|
+
tab_a = FatTable.from_org_string(tab_a_str)
|
1738
|
+
tab_a.to_aoa
|
1739
|
+
#+end_src
|
1740
|
+
|
1741
|
+
#+begin_EXAMPLE
|
1742
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
1743
|
+
|----+-------+-----+------------+--------+------------|
|
1744
|
+
| 1 | Paul | 32 | California | 20000 | 2001-07-13 |
|
1745
|
+
| 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
|
1746
|
+
| 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
|
1747
|
+
| 5 | David | 27 | Texas | 85000 | 2007-12-13 |
|
1748
|
+
| 2 | Allen | 25 | Texas | | 2005-07-13 |
|
1749
|
+
| 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
|
1750
|
+
| 9 | James | 44 | Norway | 5000 | 2005-07-13 |
|
1751
|
+
| 10 | James | 45 | Texas | 5000 | |
|
1752
|
+
#+end_EXAMPLE
|
1753
|
+
|
1754
|
+
And ~tab_b~:
|
1755
|
+
#+begin_src ruby :wrap EXAMPLE
|
1756
|
+
tab_b = FatTable.from_org_string(tab_b_str)
|
1757
|
+
tab_b.to_aoa
|
1758
|
+
#+end_src
|
1196
1759
|
|
1760
|
+
#+begin_EXAMPLE
|
1761
|
+
| Id | Dept | Emp Id |
|
1762
|
+
|----+-------------+--------|
|
1763
|
+
| 1 | IT Billing | 1 |
|
1764
|
+
| 2 | Engineering | 2 |
|
1765
|
+
| 3 | Finance | 7 |
|
1766
|
+
#+end_EXAMPLE
|
1767
|
+
|
1768
|
+
***** Inner Joins
|
1197
1769
|
With no join expression arguments, the tables are joined when their sole common
|
1198
1770
|
field, ~:id~, is equal in both tables. The result is the natural join of the
|
1199
1771
|
two tables.
|
1200
1772
|
|
1201
|
-
#+
|
1202
|
-
#+BEGIN_SRC ruby
|
1773
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1203
1774
|
tab_a.join(tab_b).to_aoa
|
1204
1775
|
#+END_SRC
|
1205
1776
|
|
@@ -1216,8 +1787,7 @@ in the second table. To correct this, we need to explicitly state the columns we
|
|
1216
1787
|
want to join on in each table by disambiguating them with ~_a~ and ~_b~
|
1217
1788
|
suffixes:
|
1218
1789
|
|
1219
|
-
#+
|
1220
|
-
#+BEGIN_SRC ruby
|
1790
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1221
1791
|
tab_a.join(tab_b, :id_a, :emp_id_b).to_aoa
|
1222
1792
|
#+END_SRC
|
1223
1793
|
|
@@ -1232,8 +1802,7 @@ Instead of using the disambiguated column names as symbols, we could also use a
|
|
1232
1802
|
string containing a ruby expression. Within the expression, the column names
|
1233
1803
|
should be treated as local variables:
|
1234
1804
|
|
1235
|
-
#+
|
1236
|
-
#+BEGIN_SRC ruby
|
1805
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1237
1806
|
tab_a.join(tab_b, 'id_a == emp_id_b').to_aoa
|
1238
1807
|
#+END_SRC
|
1239
1808
|
|
@@ -1245,12 +1814,10 @@ should be treated as local variables:
|
|
1245
1814
|
#+END_EXAMPLE
|
1246
1815
|
|
1247
1816
|
***** Left and Right Joins
|
1248
|
-
|
1249
1817
|
In left join, all the rows of ~tab_a~ are included in the output, augmented by
|
1250
1818
|
the matching columns of ~tab_b~ and augmented with nils where there is no match:
|
1251
1819
|
|
1252
|
-
#+
|
1253
|
-
#+BEGIN_SRC ruby
|
1820
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1254
1821
|
tab_a.left_join(tab_b, 'id_a == emp_id_b').to_aoa
|
1255
1822
|
#+END_SRC
|
1256
1823
|
|
@@ -1271,8 +1838,7 @@ In a right join, all the rows of ~tab_b~ are included in the output, augmented
|
|
1271
1838
|
by the matching columns of ~tab_a~ and augmented with nils where there is no
|
1272
1839
|
match:
|
1273
1840
|
|
1274
|
-
#+
|
1275
|
-
#+BEGIN_SRC ruby
|
1841
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1276
1842
|
tab_a.right_join(tab_b, 'id_a == emp_id_b').to_aoa
|
1277
1843
|
#+END_SRC
|
1278
1844
|
|
@@ -1285,13 +1851,11 @@ match:
|
|
1285
1851
|
#+END_EXAMPLE
|
1286
1852
|
|
1287
1853
|
***** Full Join
|
1288
|
-
|
1289
1854
|
A full join combines the effects of a left join and a right join. All the rows
|
1290
1855
|
from both tables are included in the output augmented by columns of the other
|
1291
1856
|
table where the join expression is satisfied and augmented with nils otherwise.
|
1292
1857
|
|
1293
|
-
#+
|
1294
|
-
#+BEGIN_SRC ruby
|
1858
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1295
1859
|
tab_a.full_join(tab_b, 'id_a == emp_id_b').to_aoa
|
1296
1860
|
#+END_SRC
|
1297
1861
|
|
@@ -1310,14 +1874,12 @@ table where the join expression is satisfied and augmented with nils otherwise.
|
|
1310
1874
|
#+END_EXAMPLE
|
1311
1875
|
|
1312
1876
|
***** Cross Join
|
1313
|
-
|
1314
1877
|
Finally, a cross join outputs every row of ~tab_a~ augmented with every row of
|
1315
1878
|
~tab_b~, in other words, the Cartesian product of the two tables. If ~tab_a~ has
|
1316
1879
|
~N~ rows and ~tab_b~ has ~M~ rows, the output table will have ~N * M~ rows.
|
1317
1880
|
So be careful lest you consume all your computer's memory.
|
1318
1881
|
|
1319
|
-
#+
|
1320
|
-
#+BEGIN_SRC ruby
|
1882
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1321
1883
|
tab_a.cross_join(tab_b).to_aoa
|
1322
1884
|
#+END_SRC
|
1323
1885
|
|
@@ -1351,7 +1913,6 @@ So be careful lest you consume all your computer's memory.
|
|
1351
1913
|
#+END_EXAMPLE
|
1352
1914
|
|
1353
1915
|
*** Set Operations
|
1354
|
-
|
1355
1916
|
~FatTable~ can perform several set operations on pairs of tables. In order for
|
1356
1917
|
two tables to be used this way, they must have the same number of columns with
|
1357
1918
|
the same types or an exception will be raised. We'll call two tables that
|
@@ -1361,8 +1922,7 @@ We'll use the following two set-compatible tables in the examples. They each
|
|
1361
1922
|
have some duplicates and some group boundaries so you can see the effect of the
|
1362
1923
|
set operations on duplicates and groups.
|
1363
1924
|
|
1364
|
-
#+
|
1365
|
-
#+BEGIN_SRC ruby
|
1925
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1366
1926
|
tab1.to_aoa
|
1367
1927
|
#+END_SRC
|
1368
1928
|
|
@@ -1393,8 +1953,7 @@ set operations on duplicates and groups.
|
|
1393
1953
|
| T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
|
1394
1954
|
#+END_EXAMPLE
|
1395
1955
|
|
1396
|
-
#+
|
1397
|
-
#+BEGIN_SRC ruby
|
1956
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1398
1957
|
tab2.to_aoa
|
1399
1958
|
#+END_SRC
|
1400
1959
|
|
@@ -1422,7 +1981,6 @@ set operations on duplicates and groups.
|
|
1422
1981
|
#+END_EXAMPLE
|
1423
1982
|
|
1424
1983
|
**** Unions
|
1425
|
-
|
1426
1984
|
Two tables that are set-compatible can be combined with the ~union~ or
|
1427
1985
|
~union_all~ methods so that the rows of both tables appear in the output. In the
|
1428
1986
|
output table, the headers of the receiver table are used. You can use ~select~
|
@@ -1433,8 +1991,7 @@ Any group boundaries in the input tables are destroyed by ~union~ but are
|
|
1433
1991
|
preserved by ~union_all~. In addition, ~union_all~ (but not ~union~) adds a
|
1434
1992
|
group boundary between the rows of the two input tables.
|
1435
1993
|
|
1436
|
-
#+
|
1437
|
-
#+BEGIN_SRC ruby
|
1994
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1438
1995
|
tab1.union(tab2).to_aoa
|
1439
1996
|
#+END_SRC
|
1440
1997
|
|
@@ -1464,8 +2021,7 @@ group boundary between the rows of the two input tables.
|
|
1464
2021
|
| T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
|
1465
2022
|
#+END_EXAMPLE
|
1466
2023
|
|
1467
|
-
#+
|
1468
|
-
#+BEGIN_SRC ruby
|
2024
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1469
2025
|
tab1.union_all(tab2).to_aoa
|
1470
2026
|
#+END_SRC
|
1471
2027
|
|
@@ -1516,12 +2072,10 @@ group boundary between the rows of the two input tables.
|
|
1516
2072
|
#+END_EXAMPLE
|
1517
2073
|
|
1518
2074
|
**** Intersections
|
1519
|
-
|
1520
2075
|
The ~intersect~ method returns a table having only rows common to both tables,
|
1521
2076
|
eliminating any duplicate rows in the result.
|
1522
2077
|
|
1523
|
-
#+
|
1524
|
-
#+BEGIN_SRC ruby
|
2078
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1525
2079
|
tab1.intersect(tab2).to_aoa
|
1526
2080
|
#+END_SRC
|
1527
2081
|
|
@@ -1540,8 +2094,7 @@ With ~intersect_all~, all the rows of the first table, including duplicates, are
|
|
1540
2094
|
included in the result if they also occur in the second table. However,
|
1541
2095
|
duplicates in the second table do not appear.
|
1542
2096
|
|
1543
|
-
#+
|
1544
|
-
#+BEGIN_SRC ruby
|
2097
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1545
2098
|
tab1.intersect_all(tab2).to_aoa
|
1546
2099
|
#+END_SRC
|
1547
2100
|
|
@@ -1562,8 +2115,7 @@ As a result, it makes a difference which table is the receiver of the
|
|
1562
2115
|
~intersect_all~ method call and which is the argument. In other words, order of
|
1563
2116
|
operation matters.
|
1564
2117
|
|
1565
|
-
#+
|
1566
|
-
#+BEGIN_SRC ruby
|
2118
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1567
2119
|
tab2.intersect_all(tab1).to_aoa
|
1568
2120
|
#+END_SRC
|
1569
2121
|
|
@@ -1580,13 +2132,11 @@ operation matters.
|
|
1580
2132
|
| T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
|
1581
2133
|
#+END_EXAMPLE
|
1582
2134
|
|
1583
|
-
**** Differences with Except
|
1584
|
-
|
2135
|
+
**** Set Differences with Except
|
1585
2136
|
You can use the ~except~ method to delete from a table any rows that occur in
|
1586
2137
|
another table, that is, compute the set difference between the tables.
|
1587
2138
|
|
1588
|
-
#+
|
1589
|
-
#+BEGIN_SRC ruby
|
2139
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1590
2140
|
tab1.except(tab2).to_aoa
|
1591
2141
|
#+END_SRC
|
1592
2142
|
|
@@ -1608,8 +2158,7 @@ another table, that is, compute the set difference between the tables.
|
|
1608
2158
|
Like subtraction, though, the order of operands matters with set difference
|
1609
2159
|
computed by ~except~.
|
1610
2160
|
|
1611
|
-
#+
|
1612
|
-
#+BEGIN_SRC ruby
|
2161
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1613
2162
|
tab2.except(tab1).to_aoa
|
1614
2163
|
#+END_SRC
|
1615
2164
|
|
@@ -1626,8 +2175,7 @@ computed by ~except~.
|
|
1626
2175
|
As with ~intersect_all~, ~except_all~ includes any duplicates in the first,
|
1627
2176
|
receiver table, but not those in the second, argument table.
|
1628
2177
|
|
1629
|
-
#+
|
1630
|
-
#+BEGIN_SRC ruby
|
2178
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1631
2179
|
tab1.except_all(tab2).to_aoa
|
1632
2180
|
#+END_SRC
|
1633
2181
|
|
@@ -1649,8 +2197,7 @@ receiver table, but not those in the second, argument table.
|
|
1649
2197
|
|
1650
2198
|
And, of course, the order of operands matters here as well.
|
1651
2199
|
|
1652
|
-
#+
|
1653
|
-
#+BEGIN_SRC ruby
|
2200
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1654
2201
|
tab2.except_all(tab1).to_aoa
|
1655
2202
|
#+END_SRC
|
1656
2203
|
|
@@ -1667,13 +2214,11 @@ And, of course, the order of operands matters here as well.
|
|
1667
2214
|
#+END_EXAMPLE
|
1668
2215
|
|
1669
2216
|
*** Uniq (aka Distinct)
|
1670
|
-
|
1671
2217
|
The ~uniq~ method takes no arguments and simply removes any duplicate rows from
|
1672
2218
|
the input table. The ~distinct~ method is an alias for ~uniq~. Any groups in
|
1673
2219
|
the input table are lost.
|
1674
2220
|
|
1675
|
-
#+
|
1676
|
-
#+BEGIN_SRC ruby
|
2221
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1677
2222
|
tab1.uniq.to_aoa
|
1678
2223
|
#+END_SRC
|
1679
2224
|
|
@@ -1699,13 +2244,11 @@ the input table are lost.
|
|
1699
2244
|
#+END_EXAMPLE
|
1700
2245
|
|
1701
2246
|
*** Remove groups with degroup!
|
1702
|
-
|
1703
2247
|
Finally, it is sometimes helpful to remove any group boundaries from a table.
|
1704
|
-
You can do this with ~.degroup!~, which
|
1705
|
-
|
2248
|
+
You can do this with ~.degroup!~, which, together with ~force_string!~, are
|
2249
|
+
the only operations that mutate their receiver tables.
|
1706
2250
|
|
1707
|
-
#+
|
1708
|
-
#+BEGIN_SRC ruby
|
2251
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1709
2252
|
tab1.degroup!.to_aoa
|
1710
2253
|
#+END_SRC
|
1711
2254
|
|
@@ -1734,7 +2277,6 @@ receiver table by removing its groups.
|
|
1734
2277
|
#+END_EXAMPLE
|
1735
2278
|
|
1736
2279
|
** Formatting Tables
|
1737
|
-
|
1738
2280
|
Besides creating and operating on tables, you may want to display the resulting
|
1739
2281
|
table. ~FatTable~ seeks to provide a set of formatting directives that are the
|
1740
2282
|
most common across many output media. It provides directives for alignment, for
|
@@ -1751,8 +2293,8 @@ an ~org-mode~ table as a String, and since ~org-mode~ does not support colors,
|
|
1751
2293
|
any color directives are ignored. Some of the output targets are not strings,
|
1752
2294
|
but ruby data structures, and for them, things such as alignment are irrelevant.
|
1753
2295
|
|
1754
|
-
*** Available
|
1755
|
-
|
2296
|
+
*** Available Formatter Output Targets
|
2297
|
+
**** Output Media
|
1756
2298
|
~FatTable~ supports the following output targets for its tables:
|
1757
2299
|
|
1758
2300
|
- Text :: form the table with ACSII characters,
|
@@ -1771,33 +2313,144 @@ class by defining about a dozen methods that get called at various places
|
|
1771
2313
|
during the construction of the output table. The idea is that more output
|
1772
2314
|
formats can be defined by adding additional classes.
|
1773
2315
|
|
1774
|
-
|
2316
|
+
**** Examples
|
2317
|
+
***** To Text
|
2318
|
+
This formatter uses nothing by ASCII characters to draw the table. Notice
|
2319
|
+
that, unlike to ~to_org~ formatter shown below, the intersections of lines are
|
2320
|
+
represented by a ~+~ character. Embelishments such as color, bold, and so
|
2321
|
+
forth are ignored.
|
1775
2322
|
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
2323
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2324
|
+
tab_a.to_text
|
2325
|
+
#+end_SRC
|
1779
2326
|
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
2327
|
+
#+BEGIN_EXAMPLE
|
2328
|
+
+====+=======+=====+============+========+============+
|
2329
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2330
|
+
+----+-------+-----+------------+--------+------------+
|
2331
|
+
| 1 | Paul | 32 | California | 20000 | 2001-07-13 |
|
2332
|
+
| 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
|
2333
|
+
| 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
|
2334
|
+
| 5 | David | 27 | Texas | 85000 | 2007-12-13 |
|
2335
|
+
| 2 | Allen | 25 | Texas | | 2005-07-13 |
|
2336
|
+
| 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
|
2337
|
+
| 9 | James | 44 | Norway | 5000 | 2005-07-13 |
|
2338
|
+
| 10 | James | 45 | Texas | 5000 | |
|
2339
|
+
+====+=======+=====+============+========+============+
|
2340
|
+
#+END_EXAMPLE
|
1787
2341
|
|
1788
|
-
|
2342
|
+
***** To Org
|
2343
|
+
This formatter is designed to format tables in a manner consistent with the
|
2344
|
+
way tables are drawn within Emacs Org Mode. It also uses nothing by ASCII
|
2345
|
+
characters to draw the table, but, the intersections of lines are represented
|
2346
|
+
by a ~|~ character. Embelishments such as color, bold, and so forth are
|
2347
|
+
ignored. When working in Org Mode, note that Emacs will convert an Array of
|
2348
|
+
Arrays into an Org Mode table, so when constructing tables programmatically,
|
2349
|
+
it may be better to use that formatter as shown below.
|
2350
|
+
|
2351
|
+
#+begin_SRC ruby :wrap EXAMPLE
|
2352
|
+
tab_a.to_org
|
2353
|
+
#+end_SRC
|
2354
|
+
|
2355
|
+
#+begin_EXAMPLE
|
2356
|
+
|----+-------+-----+------------+--------+--------------|
|
2357
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2358
|
+
|----+-------+-----+------------+--------+--------------|
|
2359
|
+
| 1 | Paul | 32 | California | 20000 | [2001-07-13] |
|
2360
|
+
| 3 | Teddy | 23 | Norway | 20000 | [2007-12-13] |
|
2361
|
+
| 4 | Mark | 25 | Rich-Mond | 65000 | [2007-12-13] |
|
2362
|
+
| 5 | David | 27 | Texas | 85000 | [2007-12-13] |
|
2363
|
+
| 2 | Allen | 25 | Texas | | [2005-07-13] |
|
2364
|
+
| 8 | Paul | 24 | Houston | 20000 | [2005-07-13] |
|
2365
|
+
| 9 | James | 44 | Norway | 5000 | [2005-07-13] |
|
2366
|
+
| 10 | James | 45 | Texas | 5000 | |
|
2367
|
+
|----+-------+-----+------------+--------+--------------|
|
2368
|
+
#+end_EXAMPLE
|
2369
|
+
|
2370
|
+
***** To Term
|
2371
|
+
When outputting to a terminal or other device that can interpret ANSI
|
2372
|
+
characters and escape codes, you can use this formatter to get a prettier
|
2373
|
+
table. It also allows embelishments such as color and text styles to the
|
2374
|
+
extent the device supports it.
|
2375
|
+
|
2376
|
+
#+begin_SRC ruby :wrap EXAMPLE
|
2377
|
+
tab_a.to_term
|
2378
|
+
#+end_SRC
|
2379
|
+
|
2380
|
+
#+begin_EXAMPLE
|
2381
|
+
╒════╤═══════╤═════╤════════════╤════════╤════════════╕
|
2382
|
+
│ Id │ Name │ Age │ Address │ Salary │ Join Date │
|
2383
|
+
├────┼───────┼─────┼────────────┼────────┼────────────┤
|
2384
|
+
│ 1 │ Paul │ 32 │ California │ 20000 │ 2001-07-13 │
|
2385
|
+
│ 3 │ Teddy │ 23 │ Norway │ 20000 │ 2007-12-13 │
|
2386
|
+
│ 4 │ Mark │ 25 │ Rich-Mond │ 65000 │ 2007-12-13 │
|
2387
|
+
│ 5 │ David │ 27 │ Texas │ 85000 │ 2007-12-13 │
|
2388
|
+
│ 2 │ Allen │ 25 │ Texas │ │ 2005-07-13 │
|
2389
|
+
│ 8 │ Paul │ 24 │ Houston │ 20000 │ 2005-07-13 │
|
2390
|
+
│ 9 │ James │ 44 │ Norway │ 5000 │ 2005-07-13 │
|
2391
|
+
│ 10 │ James │ 45 │ Texas │ 5000 │ │
|
2392
|
+
╘════╧═══════╧═════╧════════════╧════════╧════════════╛
|
2393
|
+
#+end_EXAMPLE
|
2394
|
+
|
2395
|
+
***** To LaTeX
|
2396
|
+
This formatter outputs a table in the form suitable for inclusion in a LaTeX
|
2397
|
+
document using the ~logtable~ package. Natualy it allows embelishments such
|
2398
|
+
as color and text styles to the full extent of LaTeX's formatting prowess.
|
2399
|
+
|
2400
|
+
#+begin_SRC ruby :wrap EXAMPLE
|
2401
|
+
tab_b.to_latex
|
2402
|
+
#+end_SRC
|
2403
|
+
|
2404
|
+
#+begin_EXAMPLE
|
2405
|
+
\begin{longtable}{lll}
|
2406
|
+
Id&
|
2407
|
+
Dept&
|
2408
|
+
Emp Id\\
|
2409
|
+
\endhead
|
2410
|
+
1&
|
2411
|
+
IT Billing&
|
2412
|
+
1\\
|
2413
|
+
2&
|
2414
|
+
Engineering&
|
2415
|
+
2\\
|
2416
|
+
3&
|
2417
|
+
Finance&
|
2418
|
+
7\\
|
2419
|
+
\end{longtable}
|
2420
|
+
#+end_EXAMPLE
|
2421
|
+
|
2422
|
+
***** To AoA (Array of Arrays)
|
2423
|
+
#+begin_SRC ruby :wrap EXAMPLE
|
2424
|
+
tab_b.to_aoa
|
2425
|
+
#+end_SRC
|
2426
|
+
|
2427
|
+
#+begin_EXAMPLE
|
2428
|
+
[["Id", "Dept", "Emp Id"], nil, ["1", "IT Billing", "1"], ["2", "Engineering", "2"],
|
2429
|
+
["3", "Finance", "7"]]
|
2430
|
+
#+end_EXAMPLE
|
2431
|
+
|
2432
|
+
***** To AoH (Array of Hashes)
|
2433
|
+
#+begin_SRC ruby :wrap EXAMPLE
|
2434
|
+
tab_b.to_aoh
|
2435
|
+
#+end_SRC
|
2436
|
+
|
2437
|
+
#+begin_EXAMPLE
|
2438
|
+
[{:id=>"1", :dept=>"IT Billing", :emp_id=>"1"}, {:id=>"2", :dept=>"Engineering", :emp_id=>"2"},
|
2439
|
+
{:id=>"3", :dept=>"Finance", :emp_id=>"7"}]
|
2440
|
+
#+end_EXAMPLE
|
1789
2441
|
|
2442
|
+
|
2443
|
+
*** Formatting Directives
|
1790
2444
|
The formatting methods explained in the next section all take formatting
|
1791
2445
|
directives as strings in which letters and other characters signify what
|
1792
|
-
formatting applies. For example, we may apply the formatting directive
|
2446
|
+
formatting applies. For example, we may apply the formatting directive 'R,$'
|
1793
2447
|
to numbers in a certain part of the table. Each of those characters, and in
|
1794
2448
|
some cases a whole substring, is a single directive. They can appear in any
|
1795
|
-
order, so
|
2449
|
+
order, so '$R,' and ',$R' are equivalent.
|
1796
2450
|
|
1797
2451
|
Here is a list of all the formatting directives that apply to each cell type:
|
1798
2452
|
|
1799
2453
|
**** String
|
1800
|
-
|
1801
2454
|
For a string element, the following instructions are valid. Note that these can
|
1802
2455
|
also be applied to all the other cell types as well since they are all converted
|
1803
2456
|
to a string in forming the output.
|
@@ -1811,22 +2464,21 @@ to a string in forming the output.
|
|
1811
2464
|
- R :: align the element on the right of the column
|
1812
2465
|
- L :: align the element on the left of the column
|
1813
2466
|
- C :: align the element in the center of the column
|
1814
|
-
- c[
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
2467
|
+
- c[<color_spec>] :: render the element in the given color; the <color_spec>
|
2468
|
+
can have the form fgcolor, fgcolor.bgcolor, or .bgcolor, to set the
|
2469
|
+
foreground or background colors respectively, and each of those can be an
|
2470
|
+
ANSI or X11 color name in addition to the special color, 'none', which keeps
|
2471
|
+
the output's default color.
|
1819
2472
|
- _ ~_ :: underline the element, or turn off underline
|
1820
2473
|
- * ~* :: cause the element to blink, or turn off blink
|
1821
2474
|
|
1822
|
-
For example, the directive
|
2475
|
+
For example, the directive 'tCc[red.yellow]' would title-case the element,
|
1823
2476
|
center it, and color it red on a yellow background. The directives that are
|
1824
2477
|
boolean have negating forms so that, for example, if bold is turned on for all
|
1825
2478
|
columns of a given type, it can be countermanded in formatting directives for
|
1826
2479
|
particular columns.
|
1827
2480
|
|
1828
2481
|
**** Numeric
|
1829
|
-
|
1830
2482
|
For a numeric element, all the instructions valid for string are available, in
|
1831
2483
|
addition to the following:
|
1832
2484
|
|
@@ -1841,11 +2493,10 @@ addition to the following:
|
|
1841
2493
|
will result in a :numeric expressed as seconds and can be displayed in
|
1842
2494
|
hours, minutes, and seconds with this formatting instruction.
|
1843
2495
|
|
1844
|
-
For example, the directive
|
2496
|
+
For example, the directive 'R5.0c[blue]' would right-align the numeric
|
1845
2497
|
element, pad it on the left with zeros, and color it blue.
|
1846
2498
|
|
1847
2499
|
**** DateTime
|
1848
|
-
|
1849
2500
|
For a ~DateTime~, all the instructions valid for string are available, in
|
1850
2501
|
addition to the following:
|
1851
2502
|
|
@@ -1857,61 +2508,216 @@ addition to the following:
|
|
1857
2508
|
component where fmt is a valid format string for Date#strftime, otherwise,
|
1858
2509
|
the datetime will be formatted as an ISO 8601 string, YYYY-MM-DD.
|
1859
2510
|
|
1860
|
-
For example,
|
2511
|
+
For example, 'c[pink]d[%b %-d, %Y]C', would format a date element like 'Sep
|
1861
2512
|
22, 1957', center it, and color it pink.
|
1862
2513
|
|
1863
2514
|
**** Boolean
|
1864
|
-
|
1865
2515
|
For a boolean cell, all the instructions valid for string are available, in
|
1866
2516
|
addition to the following:
|
1867
2517
|
|
1868
|
-
- Y :: print true as
|
1869
|
-
- T :: print true as
|
1870
|
-
- X :: print true as
|
2518
|
+
- Y :: print true as ~Y~ and false as ~N~,
|
2519
|
+
- T :: print true as ~T~ and false as ~F~,
|
2520
|
+
- X :: print true as ~X~ and false as an empty string '',
|
1871
2521
|
- b[xxx,yyy] :: print true as the string given as ~xxx~ and false as the string
|
1872
2522
|
given as ~yyy~,
|
1873
2523
|
- c[tcolor,fcolor] :: color a true element with ~tcolor~ and a false element
|
1874
2524
|
with ~fcolor~. Each of the colors may be specified in the same manner as
|
1875
2525
|
colors for strings described above.
|
1876
2526
|
|
1877
|
-
For example, the directive '
|
1878
|
-
render a true boolean as
|
1879
|
-
boolean as
|
2527
|
+
For example, the directive 'b[Yeppers,Nope]c[green.pink,red.pink]' would
|
2528
|
+
render a true boolean as ~Yeppers~ colored green on pink and render a false
|
2529
|
+
boolean as ~Nope~ colored red on pink. See [[https://www.youtube.com/watch?v=oLdFFD8II8U][Yeppers]] for additional information.
|
1880
2530
|
|
1881
2531
|
**** NilClass
|
1882
|
-
|
1883
2532
|
By default, ~nil~ elements are rendered as blank cells, but you can make them
|
1884
2533
|
visible with the following, and in that case, all the formatting instructions
|
1885
2534
|
valid for strings are also available:
|
1886
2535
|
|
1887
2536
|
- n[niltext] :: render a ~nil~ item with the given niltext.
|
1888
2537
|
|
1889
|
-
For example, you might want to use
|
2538
|
+
For example, you might want to use 'n[-]Cc[purple]' to make nils visible as a
|
1890
2539
|
centered purple hyphen.
|
1891
2540
|
|
1892
|
-
***
|
2541
|
+
*** The ~format~ and ~format_for~ methods
|
2542
|
+
Formatters take only two kinds of methods, those that attach footers to a
|
2543
|
+
table, which are discussed in the next section, and those that specify
|
2544
|
+
formatting for table cells, which are the subject of this section.
|
2545
|
+
|
2546
|
+
To set formatting directives for all locations in a table at once, use the
|
2547
|
+
~format~ method; to set formatting directives for a particular location in the
|
2548
|
+
table, use the ~format_for~ method, giving the location as the first
|
2549
|
+
parameter. See below at [[*Table Locations][Table Locations]] for an explanation of all the
|
2550
|
+
locations available.
|
1893
2551
|
|
1894
|
-
|
1895
|
-
|
2552
|
+
Other than that first parameter, the two methods take the same types of
|
2553
|
+
parameters. The remaining parameters are hash-like parameters that use either a
|
2554
|
+
column name or a type as the key and a string with the formatting directives to
|
2555
|
+
apply as the value. The following example says to set the formatting for all
|
2556
|
+
locations in the table and to format all numeric fields as strings that are
|
2557
|
+
rounded to whole numbers (the '0.0' part), that are right-aligned (the 'R'
|
2558
|
+
part), and have grouping commas inserted (the ',' part). But the ~:id~ column is
|
2559
|
+
numeric, and the second parameter overrides the formatting for numerics in
|
2560
|
+
general and calls for the ~:id~ column to be padded to three digits with zeros
|
2561
|
+
on the left (the '3.0' part) and to be centered (the 'C' part).
|
2562
|
+
|
2563
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2564
|
+
tab_a.to_text do |f|
|
2565
|
+
f.format(numeric: '0.0,R', id: '3.0C')
|
2566
|
+
f.format_for(:body, string: 'R')
|
2567
|
+
f.format_for(:header, string: 'C')
|
2568
|
+
end
|
2569
|
+
#+END_SRC
|
2570
|
+
|
2571
|
+
#+begin_EXAMPLE
|
2572
|
+
+=====+=======+=====+============+========+============+
|
2573
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2574
|
+
+-----+-------+-----+------------+--------+------------+
|
2575
|
+
| 001 | Paul | 32 | California | 20,000 | 2001-07-13 |
|
2576
|
+
| 003 | Teddy | 23 | Norway | 20,000 | 2007-12-13 |
|
2577
|
+
| 004 | Mark | 25 | Rich-Mond | 65,000 | 2007-12-13 |
|
2578
|
+
| 005 | David | 27 | Texas | 85,000 | 2007-12-13 |
|
2579
|
+
| 002 | Allen | 25 | Texas | | 2005-07-13 |
|
2580
|
+
| 008 | Paul | 24 | Houston | 20,000 | 2005-07-13 |
|
2581
|
+
| 009 | James | 44 | Norway | 5,000 | 2005-07-13 |
|
2582
|
+
| 010 | James | 45 | Texas | 5,000 | |
|
2583
|
+
+=====+=======+=====+============+========+============+
|
2584
|
+
#+end_EXAMPLE
|
2585
|
+
|
2586
|
+
In the example, the ~format~ method affects the whole table. Its ~numeric:~
|
2587
|
+
directive affected the ~:age~ and ~:salary~ columns because their types are
|
2588
|
+
Numeric. The ~id:~ column is also Numeric, but it's more specific directive
|
2589
|
+
takes precedence and it is formatted accordingly.
|
2590
|
+
|
2591
|
+
But the ~format_for~ methods affected two "locations": the "body" and the
|
2592
|
+
"header". Within the body, the ~:string~ directive calls for all strings to
|
2593
|
+
be right-aligned, but the headers are unaffected by it. The ~format_for~ the
|
2594
|
+
~:header~ location caused all the headers to be centered.
|
2595
|
+
|
2596
|
+
All the other cells in the table, namely the cells in the ~:join_date~ column,
|
2597
|
+
had the default formatting applied.
|
2598
|
+
|
2599
|
+
**** Table Locations
|
2600
|
+
In the ~format_for~ formatting method, the first argument names a "location."
|
2601
|
+
The table is divided into several locations for which separate formatting
|
2602
|
+
directives may be given. These locations are identified by the following
|
2603
|
+
symbols:
|
2604
|
+
|
2605
|
+
- :header :: the first row of the output table containing the headers,
|
2606
|
+
- :footer :: all rows of the table's footers,
|
2607
|
+
- :gfooter :: all rows of the table's group footers,
|
2608
|
+
- :body :: all the data rows of the table, that is, those that are neither part
|
2609
|
+
of the header, footers, or gfooters,
|
2610
|
+
- :bfirst :: the first row of the table's body, and
|
2611
|
+
- :gfirst :: the first row in each group in the table's body.
|
2612
|
+
|
2613
|
+
**** Location priority
|
2614
|
+
Formatting for any given cell depends on its location in the table. The
|
2615
|
+
~format_for~ method takes a location to which its formatting directive are
|
2616
|
+
restricted as the first argument. It can be one of the following:
|
2617
|
+
|
2618
|
+
- ~:header~ :: The directives apply only to the header row, that is the first
|
2619
|
+
row, of the output table; before the directives are applied, the header's
|
2620
|
+
symbol form is converted back into a string and capitalized as is a book
|
2621
|
+
title. Thus, only directives applicable to the String type have any effect.
|
2622
|
+
|
2623
|
+
- ~:body~ :: The directives apply to all rows in the body of the table.
|
2624
|
+
|
2625
|
+
- ~:gfirst~ :: directives apply to the first row in each group in the body of
|
2626
|
+
the table, unless the row is also the first row in the table as a whole, in
|
2627
|
+
which case the ~:bfirst~ directives apply,
|
2628
|
+
|
2629
|
+
- ~:bfirst~ :: The directives apply to the first row in the body of the table,
|
2630
|
+
taking precedence over those directives that apply to the body generally or
|
2631
|
+
the ~:gfirst~ directives that apply to the first row in each group.
|
2632
|
+
|
2633
|
+
- ~:footer~ :: The directives apply to all the footer rows of the output
|
2634
|
+
table, regardless of how many there are.
|
2635
|
+
|
2636
|
+
- ~gfooter~ :: The directives apply to all group footer rows of the output
|
2637
|
+
tables, regardless of how many there are.
|
2638
|
+
|
2639
|
+
Directives given to the ~format~ method apply the directives to all locations in
|
2640
|
+
the table, but they can be overridden by more specific directives given in a
|
2641
|
+
~format_for~ directive.
|
2642
|
+
|
2643
|
+
**** Type and Column priority
|
2644
|
+
A directive based on type applies to all columns having that type unless
|
2645
|
+
overridden by a directive specific to a named column; a directive based on a
|
2646
|
+
column name applies only to cells in that column.
|
2647
|
+
|
2648
|
+
However, there is a twist. Since the end result of formatting is to convert
|
2649
|
+
all columns to strings, the formatting directives for the ~String~ type can
|
2650
|
+
be applied to all column types. Likewise, since all columns may contain nils,
|
2651
|
+
the ~NilClass:~ type applies to nils in all columns regardless of the column's
|
2652
|
+
type.
|
2653
|
+
|
2654
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2655
|
+
tab_a.to_text do |f|
|
2656
|
+
f.format(string: 'R', id: '3.0C', nil: 'Cn[-]', salary: 'n[N/A]')
|
2657
|
+
end
|
2658
|
+
#+END_SRC
|
2659
|
+
|
2660
|
+
#+BEGIN_EXAMPLE
|
2661
|
+
+=====+=======+=====+============+========+============+
|
2662
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2663
|
+
+-----+-------+-----+------------+--------+------------+
|
2664
|
+
| 001 | Paul | 32 | California | 20000 | 2001-07-13 |
|
2665
|
+
| 003 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
|
2666
|
+
| 004 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
|
2667
|
+
| 005 | David | 27 | Texas | 85000 | 2007-12-13 |
|
2668
|
+
| 002 | Allen | 25 | Texas | N/A | 2005-07-13 |
|
2669
|
+
| 008 | Paul | 24 | Houston | 20000 | 2005-07-13 |
|
2670
|
+
| 009 | James | 44 | Norway | 5000 | 2005-07-13 |
|
2671
|
+
| 010 | James | 45 | Texas | 5000 | |
|
2672
|
+
+=====+=======+=====+============+========+============+
|
2673
|
+
#+END_EXAMPLE
|
2674
|
+
|
2675
|
+
The ~string: 'R'~ directive causes all the cells to be right-aligned except
|
2676
|
+
~:id~ which specifies centering for the ~:id~ column only. The ~n[N/A]~
|
2677
|
+
directive for specifies how nil are displayed in the numeric column, ~:salary~,
|
2678
|
+
but not for other nils, such as in the last row of the ~:join_date~ column.
|
2679
|
+
|
2680
|
+
*** Footers
|
2681
|
+
**** Adding Footers
|
2682
|
+
You can call the ~footer,~ ~gfooter, foot, and gfoot~ methods on ~Formatter~
|
2683
|
+
objects to add footers and group footers. Note that all of these methods
|
2684
|
+
return a ~Footer~ object that can be accessed to extract the computed values.
|
2685
|
+
All of these methods return the ~FatTable::Footer~ object so constructed. It
|
2686
|
+
can be used to access the values and other attributes of the footer
|
2687
|
+
computed. Their signatures are:
|
1896
2688
|
|
1897
2689
|
- ~footer(label, *sum_cols, **agg_cols)~ :: where ~label~ is a label to be
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
- ~
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
2690
|
+
placed in the first cell of the footer (unless that column is named as one
|
2691
|
+
of the ~sum_cols~ or ~agg_cols~, in which case the label is ignored),
|
2692
|
+
~*sum_cols~ are zero or more symbols for columns to be summed, and
|
2693
|
+
~**agg_cols~ is zero or more hash-like parameters with a column symbol as a
|
2694
|
+
key and a valid aggregate as the value. This causes a table-wide header to
|
2695
|
+
be added at the bottom of the table applying the ~:sum~ aggregate to the
|
2696
|
+
~sum_cols~ and the named aggregate to the ~agg_cols~. A table can have any
|
2697
|
+
number of footers attached, and they will appear at the bottom of the output
|
2698
|
+
table in the order they are given.
|
2699
|
+
|
2700
|
+
- ~foot(label, label_col, **agg_cols)~ :: where ~label~ is a label to be
|
2701
|
+
placed in the column with header ~label_col~, or, if ommitted, in the first
|
2702
|
+
cell of the footer (unless that column is named as one of the ~agg_cols~, in
|
2703
|
+
which case the label is ignored), and ~**agg_cols~ is zero or more hash-like
|
2704
|
+
parameters with a column symbol as a key and a valid aggregate as the
|
2705
|
+
value. This causes a table-wide header to be added at the bottom of the
|
2706
|
+
table applying ~agg~, to the ~agg_cols~. A table can have any number of
|
2707
|
+
footers attached, and they will appear at the bottom of the output table in
|
2708
|
+
the order they are given.
|
2709
|
+
|
2710
|
+
- ~gfooter(label, *sum_cols, **agg_cols)~ :: where the parameters have the
|
2711
|
+
same meaning as for the ~footer~ method, but results in a footer for each
|
2712
|
+
group in the table rather than the table as a whole. These will appear in
|
2713
|
+
the output table just below each group.
|
2714
|
+
|
2715
|
+
- ~gfoot(label, label_col, **agg_cols)~ :: where the parameters have the same
|
2716
|
+
meaning as for the ~foot~ method, but results in a footer for each group in
|
2717
|
+
the table rather than the table as a whole. These will appear in the output
|
2718
|
+
table just below each group.
|
1912
2719
|
|
1913
2720
|
There are also a number of convenience methods for adding common footers:
|
1914
|
-
|
1915
2721
|
- ~sum_footer(*cols)~ :: Add a footer summing the given columns with the label
|
1916
2722
|
'Total'.
|
1917
2723
|
- ~sum_gfooter(*cols)~ :: Add a group footer summing the given columns with the
|
@@ -1929,22 +2735,324 @@ There are also a number of convenience methods for adding common footers:
|
|
1929
2735
|
- ~max_gfooter(*cols)~ :: Add a group footer showing the maximum for the given
|
1930
2736
|
columns with the label 'Group Maximum'.
|
1931
2737
|
|
1932
|
-
|
2738
|
+
**** Aggregators
|
2739
|
+
When adding a footer with the above methods, you can specify an aggregator for
|
2740
|
+
each column named in the ~agg_cols~ parameter. There are several candidates
|
2741
|
+
for what you can use for an aggregator:
|
2742
|
+
|
2743
|
+
- Symbol :: one of the following built-in aggregators: :first, :last, :range,
|
2744
|
+
:sum, :count, :min, :max, :avg, :var, :pvar, :dev, :pdev, :any?, :all?,
|
2745
|
+
:none?, and :one?.
|
2746
|
+
+ The symbols ending in a question mark are valid only for boolean columns;
|
2747
|
+
+ :count, :first, and :last work with any column type,
|
2748
|
+
+ :min, :max, and :range work with all types except boolean;
|
2749
|
+
+ :sum, works only with numeric columns, and
|
2750
|
+
+ :avg, :var, :dev, :pvar, and :pdev work with numeric or datetime columns.
|
2751
|
+
In the case of datetime columns, these aggrgators convert the dates to
|
2752
|
+
julian date numbers, perform the calculation, then convert the result back
|
2753
|
+
to a datetime object.
|
2754
|
+
- String :: using a string as an aggrgegator can result in:
|
2755
|
+
+ the string being converted to an object matching the type of the column
|
2756
|
+
(for example, using '$1,888' in a numeric column puts the constant number
|
2757
|
+
1888 in the footer field, using '1957-09-22' puts the fixed date in the
|
2758
|
+
field, etc.)
|
2759
|
+
+ if the string cannot be parsed as a valid object matching the column's
|
2760
|
+
type, it is placed literally in the footer field (for example, using
|
2761
|
+
'(estimated)' can be used to add additional information to the footer)
|
2762
|
+
- Ruby object :: you can put a number in a numeric footer field, a DateTime
|
2763
|
+
object in a datetime footer field, or a true or false in a boolean footer
|
2764
|
+
field;
|
2765
|
+
- A Lambda :: finally, you can provide a lambda for performing arbitrary
|
2766
|
+
calculations and placing the result in the footer field. The number of
|
2767
|
+
arguments the lambda takes can vary:
|
2768
|
+
* If the lambda is used in a group footer, it must take a single integer
|
2769
|
+
argument that is set to the group number being calculated and /can/ take a
|
2770
|
+
second argument for the column symbol in which it appears, or
|
2771
|
+
* If the lambda is used in an ordinary footer, it either takes no arguments,
|
2772
|
+
or a single argument for the column symbol in which it appears.
|
2773
|
+
|
2774
|
+
**** Footer objects
|
2775
|
+
Each of the methods for adding a footer to a ~Formatter~ returns a ~Footer~ object
|
2776
|
+
that you can query for attributes of the generated footer, including accessing
|
2777
|
+
their computed values. Here are the accessors available on a
|
2778
|
+
~FatTable::Footer~ object:
|
2779
|
+
|
2780
|
+
- ~[h]~ :: Return the value of under the ~h~ header, or if this is a group
|
2781
|
+
footer, return an array of the values for all the groups under the ~h~
|
2782
|
+
header.
|
2783
|
+
- .<header> :: like, ~[h]~ but makes the values available in method-call form.
|
2784
|
+
- ~number_of_groups~ :: Return the total number of groups in the table to
|
2785
|
+
which this footer belongs. Note that if the table has both group footers
|
2786
|
+
and normal footers, this will return the number of groups even for a normal
|
2787
|
+
footer.
|
2788
|
+
- ~column(h)~, ~column(h, k)~ :: Return a FatTable::Column object for the
|
2789
|
+
header h and, if the footer is a group footer, the kth group.
|
2790
|
+
- ~items(h)~, ~items(h, k)~ :: Return an Array of the values for the header
|
2791
|
+
~h~ and, if a group, for the ~k~th group.
|
2792
|
+
- ~to_h~, ~to_h(k)~ :: Return a Hash with a key for each column header mapped
|
2793
|
+
to the footer value for that column, nil for unused columns. Use the index
|
2794
|
+
~k~ to specify which group to access in the case of a group footer.
|
2795
|
+
|
2796
|
+
|
2797
|
+
**** Footer Examples
|
2798
|
+
As a reminder, here is the table, ~tab_a~ defined earlier:
|
2799
|
+
|
2800
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2801
|
+
tab_a.to_aoa
|
2802
|
+
#+END_SRC
|
1933
2803
|
|
1934
|
-
|
1935
|
-
|
1936
|
-
|
2804
|
+
#+begin_EXAMPLE
|
2805
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2806
|
+
|----+-------+-----+------------+--------+------------|
|
2807
|
+
| 1 | Paul | 32 | California | 20000 | 2001-07-13 |
|
2808
|
+
| 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
|
2809
|
+
| 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
|
2810
|
+
| 5 | David | 27 | Texas | 85000 | 2007-12-13 |
|
2811
|
+
| 2 | Allen | 25 | Texas | | 2005-07-13 |
|
2812
|
+
| 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
|
2813
|
+
| 9 | James | 44 | Norway | 5000 | 2005-07-13 |
|
2814
|
+
| 10 | James | 45 | Texas | 5000 | |
|
2815
|
+
#+end_EXAMPLE
|
1937
2816
|
|
1938
|
-
|
2817
|
+
***** Built-in Aggregators
|
2818
|
+
You can add a footer compute the average of the given columns. You may be
|
2819
|
+
surprised that you can average a set of dates, but ~:avg~ simply converts the
|
2820
|
+
dates to Julian numbers, averages that, then converts the result back to a
|
2821
|
+
date.
|
2822
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2823
|
+
tab_a.to_text do |f|
|
2824
|
+
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]')
|
2825
|
+
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
2826
|
+
f.footer('Tally', age: :count)
|
2827
|
+
end
|
2828
|
+
#+END_SRC
|
1939
2829
|
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
2830
|
+
#+begin_EXAMPLE
|
2831
|
+
+=========+=======+=====+============+========+=============+
|
2832
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2833
|
+
+---------+-------+-----+------------+--------+-------------+
|
2834
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 |
|
2835
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 |
|
2836
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 |
|
2837
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 |
|
2838
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 |
|
2839
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 |
|
2840
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 |
|
2841
|
+
| 10 | James | 45 | Texas | 5,000 | |
|
2842
|
+
+---------+-------+-----+------------+--------+-------------+
|
2843
|
+
| Average | | 31 | | 31,429 | 29-DEC-2005 |
|
2844
|
+
+---------+-------+-----+------------+--------+-------------+
|
2845
|
+
| Tally | | 8 | | | |
|
2846
|
+
+=========+=======+=====+============+========+=============+
|
2847
|
+
#+end_EXAMPLE
|
1945
2848
|
|
1946
|
-
|
1947
|
-
|
2849
|
+
***** String Aggregators
|
2850
|
+
If the string is convertible into its columns's type, it will be converted to
|
2851
|
+
that type; otherwise, it will be placed in the footer literally. This example
|
2852
|
+
also shows how the values from one footer might be used in composing another
|
2853
|
+
footer.
|
2854
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2855
|
+
tab_a.to_text do |f|
|
2856
|
+
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]')
|
2857
|
+
avg_ft = f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
2858
|
+
f.footer('Tally', age: :count)
|
2859
|
+
if avg_ft[:salary] < 30000
|
2860
|
+
cmt = "We're saving"
|
2861
|
+
else
|
2862
|
+
cmt = "We're overspending"
|
2863
|
+
end
|
2864
|
+
f.footer('Pay', join_date: "We have #{avg_ft.number_of_groups} grp")
|
2865
|
+
f.footer('Group count', join_date: "We have #{avg_ft.number_of_groups} grp")
|
2866
|
+
f.footer('Comment', join_date: cmt)
|
2867
|
+
end
|
2868
|
+
#+END_SRC
|
2869
|
+
|
2870
|
+
#+begin_EXAMPLE
|
2871
|
+
+=============+=======+=====+============+========+====================+
|
2872
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2873
|
+
+-------------+-------+-----+------------+--------+--------------------+
|
2874
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 |
|
2875
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 |
|
2876
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 |
|
2877
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 |
|
2878
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 |
|
2879
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 |
|
2880
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 |
|
2881
|
+
| 10 | James | 45 | Texas | 5,000 | |
|
2882
|
+
+-------------+-------+-----+------------+--------+--------------------+
|
2883
|
+
| Average | | 31 | | 31,429 | 29-DEC-2005 |
|
2884
|
+
+-------------+-------+-----+------------+--------+--------------------+
|
2885
|
+
| Tally | | 8 | | | |
|
2886
|
+
+-------------+-------+-----+------------+--------+--------------------+
|
2887
|
+
| Pay | | | | | We have 1 grp |
|
2888
|
+
+-------------+-------+-----+------------+--------+--------------------+
|
2889
|
+
| Group count | | | | | We have 1 grp |
|
2890
|
+
+-------------+-------+-----+------------+--------+--------------------+
|
2891
|
+
| Comment | | | | | We're overspending |
|
2892
|
+
+=============+=======+=====+============+========+====================+
|
2893
|
+
#+end_EXAMPLE
|
2894
|
+
|
2895
|
+
***** Ruby Objects
|
2896
|
+
You can make the aggregator an normal ruby object, in which case it is just
|
2897
|
+
inserted into the footer at the requested location. If its type is the same
|
2898
|
+
as the column type, it participates in the formatting for that type and
|
2899
|
+
column.
|
2900
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2901
|
+
tab_a.to_text do |f|
|
2902
|
+
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
2903
|
+
f.footer('Report Date', age: :count, join_date: Date.today)
|
2904
|
+
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]')
|
2905
|
+
end
|
2906
|
+
#+END_SRC
|
2907
|
+
|
2908
|
+
#+begin_EXAMPLE
|
2909
|
+
+=============+=======+=====+============+========+=============+
|
2910
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2911
|
+
+-------------+-------+-----+------------+--------+-------------+
|
2912
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 |
|
2913
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 |
|
2914
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 |
|
2915
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 |
|
2916
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 |
|
2917
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 |
|
2918
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 |
|
2919
|
+
| 10 | James | 45 | Texas | 5,000 | |
|
2920
|
+
+-------------+-------+-----+------------+--------+-------------+
|
2921
|
+
| Average | | 31 | | 31,429 | 29-DEC-2005 |
|
2922
|
+
+-------------+-------+-----+------------+--------+-------------+
|
2923
|
+
| Report Date | | 8 | | | 20-JAN-2022 |
|
2924
|
+
+=============+=======+=====+============+========+=============+
|
2925
|
+
#+end_EXAMPLE
|
2926
|
+
|
2927
|
+
But it can be any type. Here we pick a lottery winner from the employee ids.
|
2928
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2929
|
+
tab_a.to_text do |f|
|
2930
|
+
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
2931
|
+
winner_id = tab_a.column(:id).items.sample
|
2932
|
+
f.footer('Lottery Winner', age: :count, join_date: winner_id)
|
2933
|
+
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]')
|
2934
|
+
end
|
2935
|
+
#+END_SRC
|
2936
|
+
|
2937
|
+
#+begin_EXAMPLE
|
2938
|
+
+================+=======+=====+============+========+=============+
|
2939
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2940
|
+
+----------------+-------+-----+------------+--------+-------------+
|
2941
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 |
|
2942
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 |
|
2943
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 |
|
2944
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 |
|
2945
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 |
|
2946
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 |
|
2947
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 |
|
2948
|
+
| 10 | James | 45 | Texas | 5,000 | |
|
2949
|
+
+----------------+-------+-----+------------+--------+-------------+
|
2950
|
+
| Average | | 31 | | 31,429 | 29-DEC-2005 |
|
2951
|
+
+----------------+-------+-----+------------+--------+-------------+
|
2952
|
+
| Lottery Winner | | 8 | | | 4 |
|
2953
|
+
+================+=======+=====+============+========+=============+
|
2954
|
+
#+end_EXAMPLE
|
2955
|
+
|
2956
|
+
***** Lambdas
|
2957
|
+
Perhaps the most flexible form of aggregator is a lambda form. They require 2
|
2958
|
+
or 3 parameters in non-group and group footers, respectively:
|
2959
|
+
|
2960
|
+
- ~->(f, c) {...}~ :: in a normal, non-group footer, you must provide for two
|
2961
|
+
paramters: the first, ~f~, will be bound to the footer in which the lambda
|
2962
|
+
appears and the second, ~c~, will be bound to the column header to which the
|
2963
|
+
lambda is attached.
|
2964
|
+
- ~->(f, c, k)~ :: in a group footer, you must provide for three paramters:
|
2965
|
+
the first, ~f~, will be bound to the footer in which the lambda appears, the
|
2966
|
+
second, ~c~, will be bound to the column header to which the lambda is
|
2967
|
+
attached, and the third, ~k~ will be bound to the group number of the group
|
2968
|
+
being evaluated.
|
2969
|
+
|
2970
|
+
With the first argument, the footer itself becomes available and with it all
|
2971
|
+
the things accessible with the footers, including the items in the current
|
2972
|
+
column, through the ~f.items(c)~ accessor.
|
2973
|
+
|
2974
|
+
Compute the summ of the squares if the items in the ~:age~ column:
|
2975
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2976
|
+
tab_a.to_text do |f|
|
2977
|
+
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]')
|
2978
|
+
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
2979
|
+
f.footer('SSQ', age: ->(f, c) { sa = f.items(c).map {|x| x * x}.sum; Math.sqrt(sa) })
|
2980
|
+
end
|
2981
|
+
#+END_SRC
|
2982
|
+
|
2983
|
+
#+begin_EXAMPLE
|
2984
|
+
+=========+=======+=====+============+========+=============+
|
2985
|
+
| Id | Name | Age | Address | Salary | Join Date |
|
2986
|
+
+---------+-------+-----+------------+--------+-------------+
|
2987
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 |
|
2988
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 |
|
2989
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 |
|
2990
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 |
|
2991
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 |
|
2992
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 |
|
2993
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 |
|
2994
|
+
| 10 | James | 45 | Texas | 5,000 | |
|
2995
|
+
+---------+-------+-----+------------+--------+-------------+
|
2996
|
+
| Average | | 31 | | 31,429 | 29-DEC-2005 |
|
2997
|
+
+---------+-------+-----+------------+--------+-------------+
|
2998
|
+
| SSQ | | 90 | | | |
|
2999
|
+
+=========+=======+=====+============+========+=============+
|
3000
|
+
#+end_EXAMPLE
|
3001
|
+
|
3002
|
+
Group the table according to the employee's year of joining, then compute the
|
3003
|
+
summ of the squares if the ages in each group:
|
3004
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
3005
|
+
tab_a.order_with('join_date.year').to_text do |f|
|
3006
|
+
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]', sort_key: '0.0~,')
|
3007
|
+
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
3008
|
+
f.gfooter('Group SSQ', age: ->(f, c, k) { sa = f.items(c, k).map {|x| x * x}.sum; Math.sqrt(sa) })
|
3009
|
+
f.footer('Total SSQ', age: ->(f, c) { sa = f.items(c).map {|x| x * x}.sum; Math.sqrt(sa) })
|
3010
|
+
end
|
3011
|
+
#+END_SRC
|
3012
|
+
|
3013
|
+
#+begin_EXAMPLE
|
3014
|
+
+===========+=======+=====+============+========+=============+==========+
|
3015
|
+
| Id | Name | Age | Address | Salary | Join Date | Sort Key |
|
3016
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3017
|
+
| 10 | James | 45 | Texas | 5,000 | | |
|
3018
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3019
|
+
| Group SSQ | | 45 | | | | |
|
3020
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3021
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 | 2001 |
|
3022
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3023
|
+
| Group SSQ | | 32 | | | | |
|
3024
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3025
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 | 2005 |
|
3026
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 | 2005 |
|
3027
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 | 2005 |
|
3028
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3029
|
+
| Group SSQ | | 56 | | | | |
|
3030
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3031
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 | 2007 |
|
3032
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 | 2007 |
|
3033
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 | 2007 |
|
3034
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3035
|
+
| Group SSQ | | 43 | | | | |
|
3036
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3037
|
+
| Average | | 31 | | 31,429 | 29-DEC-2005 | |
|
3038
|
+
+-----------+-------+-----+------------+--------+-------------+----------+
|
3039
|
+
| Total SSQ | | 90 | | | | |
|
3040
|
+
+===========+=======+=====+============+========+=============+==========+
|
3041
|
+
#+end_EXAMPLE
|
3042
|
+
|
3043
|
+
*** Invoking Formatters
|
3044
|
+
As the examples show, one way to invoke the formatting methods is simply to
|
3045
|
+
call one of the ~to_xxx~ methods directly on a table, which will yield a
|
3046
|
+
~FatTable::Formatter~ object to the block, and that is often the most
|
3047
|
+
convenient way to do it. But there are a few other ways.
|
3048
|
+
|
3049
|
+
**** By Instantiating a Formatter
|
3050
|
+
You can instantiate a ~XXXFormatter~ object and feed it a table as a
|
3051
|
+
parameter. There is a Formatter subclass for each target output medium, for
|
3052
|
+
example, ~AoaFormatter~ will produce a ruby array of arrays. You can then call
|
3053
|
+
the ~output~ method on the ~XXXFormatter~.
|
3054
|
+
|
3055
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1948
3056
|
FatTable::AoaFormatter.new(tab_a).output
|
1949
3057
|
#+END_SRC
|
1950
3058
|
|
@@ -1964,8 +3072,7 @@ There is a Formatter subclass for each target output medium, for example,
|
|
1964
3072
|
The ~XXXFormatter.new~ method yields the new instance to any block given, and
|
1965
3073
|
you can call methods on it to affect the formatting of the output:
|
1966
3074
|
|
1967
|
-
#+
|
1968
|
-
#+BEGIN_SRC ruby
|
3075
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1969
3076
|
FatTable::AoaFormatter.new(tab_a) do |f|
|
1970
3077
|
f.format(numeric: '0.0,R', id: '3.0C')
|
1971
3078
|
end.output
|
@@ -1984,15 +3091,13 @@ you can call methods on it to affect the formatting of the output:
|
|
1984
3091
|
| 010 | James | 45 | Texas | 5,000 | |
|
1985
3092
|
#+END_EXAMPLE
|
1986
3093
|
|
1987
|
-
**** ~FatTable~ module-level method calls
|
1988
|
-
|
3094
|
+
**** By Using ~FatTable~ module-level method calls
|
1989
3095
|
The ~FatTable~ module provides a set of methods of the form ~to_aoa~, ~to_text~,
|
1990
3096
|
etc., to access a ~Formatter~ without having to create an instance yourself.
|
1991
3097
|
Without a block, they apply the default formatting to the table and call the
|
1992
3098
|
~.output~ method automatically:
|
1993
3099
|
|
1994
|
-
#+
|
1995
|
-
#+BEGIN_SRC ruby
|
3100
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
1996
3101
|
FatTable.to_aoa(tab_a)
|
1997
3102
|
#+END_SRC
|
1998
3103
|
|
@@ -2013,8 +3118,7 @@ With a block, these methods yield a ~Formatter~ instance on which you can call
|
|
2013
3118
|
formatting and footer methods. The ~.output~ method is called on the ~Formatter~
|
2014
3119
|
automatically after the block:
|
2015
3120
|
|
2016
|
-
#+
|
2017
|
-
#+BEGIN_SRC ruby
|
3121
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2018
3122
|
FatTable.to_aoa(tab_a) do |f|
|
2019
3123
|
f.format(numeric: '0.0,R', id: '3.0C')
|
2020
3124
|
end
|
@@ -2033,13 +3137,11 @@ automatically after the block:
|
|
2033
3137
|
| 010 | James | 45 | Texas | 5,000 | |
|
2034
3138
|
#+END_EXAMPLE
|
2035
3139
|
|
2036
|
-
**** Calling
|
2037
|
-
|
2038
|
-
|
2039
|
-
Table:
|
3140
|
+
**** By Calling Methods on Table Objects
|
3141
|
+
Finally, as in many of the examples, you can call methods such as ~to_aoa~,
|
3142
|
+
~to_text~, etc., directly on a Table:
|
2040
3143
|
|
2041
|
-
#+
|
2042
|
-
#+BEGIN_SRC ruby
|
3144
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2043
3145
|
tab_a.to_aoa
|
2044
3146
|
#+END_SRC
|
2045
3147
|
|
@@ -2058,8 +3160,7 @@ Table:
|
|
2058
3160
|
|
2059
3161
|
And you can supply a block to them as well to specify formatting or footers:
|
2060
3162
|
|
2061
|
-
#+
|
2062
|
-
#+BEGIN_SRC ruby
|
3163
|
+
#+BEGIN_SRC ruby :wrap EXAMPLE
|
2063
3164
|
tab_a.to_aoa do |f|
|
2064
3165
|
f.format(numeric: '0.0,R', id: '3.0C')
|
2065
3166
|
f.sum_footer(:salary, :age)
|
@@ -2081,135 +3182,13 @@ And you can supply a block to them as well to specify formatting or footers:
|
|
2081
3182
|
| Total | | 245 | | 220,000 | |
|
2082
3183
|
#+END_EXAMPLE
|
2083
3184
|
|
2084
|
-
*** The ~format~ and ~format_for~ methods
|
2085
|
-
|
2086
|
-
Formatters take only two kinds of methods, those that attach footers to a
|
2087
|
-
table, which are discussed in the next section, and those that specify
|
2088
|
-
formatting for table cells, which are the subject of this section.
|
2089
|
-
|
2090
|
-
To set formatting directives for all locations in a table at once, use the
|
2091
|
-
~format~ method; to set formatting directives for a particular location in the
|
2092
|
-
table, use the ~format_for~ method, giving the location as the first parameter.
|
2093
|
-
|
2094
|
-
Other than that first parameter, the two methods take the same types of
|
2095
|
-
parameters. The remaining parameters are hash-like parameters that use either a
|
2096
|
-
column name or a type as the key and a string with the formatting directives to
|
2097
|
-
apply as the value. The following example says to set the formatting for all
|
2098
|
-
locations in the table and to format all numeric fields as strings that are
|
2099
|
-
rounded to whole numbers (the '0.0' part), that are right-aligned (the 'R'
|
2100
|
-
part), and have grouping commas inserted (the ',' part). But the ~:id~ column is
|
2101
|
-
numeric, and the second parameter overrides the formatting for numerics in
|
2102
|
-
general and calls for the ~:id~ column to be padded to three digits with zeros
|
2103
|
-
on the left (the '3.0' part) and to be centered (the 'C' part).
|
2104
|
-
|
2105
|
-
#+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
|
2106
|
-
#+BEGIN_SRC ruby
|
2107
|
-
tab_a.to_aoa do |f|
|
2108
|
-
f.format(numeric: '0.0,R', id: '3.0C')
|
2109
|
-
end
|
2110
|
-
#+END_SRC
|
2111
|
-
|
2112
|
-
#+BEGIN_EXAMPLE
|
2113
|
-
| Id | Name | Age | Address | Salary | Join Date |
|
2114
|
-
|-----+-------+-----+------------+--------+------------|
|
2115
|
-
| 001 | Paul | 32 | California | 20,000 | 2001-07-13 |
|
2116
|
-
| 003 | Teddy | 23 | Norway | 20,000 | 2007-12-13 |
|
2117
|
-
| 004 | Mark | 25 | Rich-Mond | 65,000 | 2007-12-13 |
|
2118
|
-
| 005 | David | 27 | Texas | 85,000 | 2007-12-13 |
|
2119
|
-
| 002 | Allen | 25 | Texas | | 2005-07-13 |
|
2120
|
-
| 008 | Paul | 24 | Houston | 20,000 | 2005-07-13 |
|
2121
|
-
| 009 | James | 44 | Norway | 5,000 | 2005-07-13 |
|
2122
|
-
| 010 | James | 45 | Texas | 5,000 | |
|
2123
|
-
#+END_EXAMPLE
|
2124
|
-
|
2125
|
-
The ~numeric:~ directive affected the ~:age~ and ~:salary~ columns and the ~id:~
|
2126
|
-
directive affected only the ~:id~ column. All the other cells in the table had
|
2127
|
-
the default formatting applied.
|
2128
|
-
|
2129
|
-
**** Location priority
|
2130
|
-
|
2131
|
-
Formatting for any given cell depends on its location in the table. The
|
2132
|
-
~format_for~ method takes a location to which its formatting directive are
|
2133
|
-
restricted as the first argument. It can be one of the following:
|
2134
|
-
|
2135
|
-
- ~:header~ :: directive apply only to the header row, that is the first row, of
|
2136
|
-
the output table,
|
2137
|
-
|
2138
|
-
- ~:footer~ :: directives apply to all the footer rows of the output table,
|
2139
|
-
regardless of how many there are,
|
2140
|
-
|
2141
|
-
- ~gfooter~ :: directives apply to all group footer rows of the output tables,
|
2142
|
-
regardless of how many there are,
|
2143
|
-
|
2144
|
-
- ~:body~ :: directives apply to all rows in the body of the table unless the
|
2145
|
-
row is the first row in the table or in a group and separate directives for
|
2146
|
-
those have been given, in which case those directives apply,
|
2147
|
-
|
2148
|
-
- ~:gfirst~ :: directives apply to the first row in each group in the body of
|
2149
|
-
the table, unless the row is also the first row in the table as a whole, in
|
2150
|
-
which case the ~:bfirst~ directives apply,
|
2151
|
-
|
2152
|
-
- ~:bfirst~ :: directives apply to the first row in the body of the table.
|
2153
|
-
|
2154
|
-
If you give directives for ~:body~, they are copied to ~:bfirst~ and ~:gfirst~
|
2155
|
-
as well and can be overridden by directives for those locations.
|
2156
|
-
|
2157
|
-
Directives given to the ~format~ method apply the directives to all locations in
|
2158
|
-
the table, but they can be overridden by more specific directives given in a
|
2159
|
-
~format_for~ directive.
|
2160
|
-
|
2161
|
-
**** Type and Column priority
|
2162
|
-
|
2163
|
-
A directive based on type applies to all columns having that type unless
|
2164
|
-
overridden by a directive specific to a named column; a directive based on a
|
2165
|
-
column name applies only to cells in that column.
|
2166
|
-
|
2167
|
-
However, there is a twist. Since the end result of formatting is to convert all
|
2168
|
-
columns to strings, the formatting directives for the ~:string~ type applies to
|
2169
|
-
all columns. Likewise, since all columns may contain nils, the ~nil:~ type
|
2170
|
-
applies to nils in all columns regardless of the column's type.
|
2171
|
-
|
2172
|
-
#+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
|
2173
|
-
#+BEGIN_SRC ruby
|
2174
|
-
require 'fat_table'
|
2175
|
-
tab_a.to_text do |f|
|
2176
|
-
f.format(string: 'R', id: '3.0C', salary: 'n[N/A]')
|
2177
|
-
end
|
2178
|
-
#+END_SRC
|
2179
|
-
|
2180
|
-
#+BEGIN_EXAMPLE
|
2181
|
-
+=====+=======+=====+============+========+============+
|
2182
|
-
| Id | Name | Age | Address | Salary | Join Date |
|
2183
|
-
+-----+-------+-----+------------+--------+------------+
|
2184
|
-
| 001 | Paul | 32 | California | 20000 | 2001-07-13 |
|
2185
|
-
| 003 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
|
2186
|
-
| 004 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
|
2187
|
-
| 005 | David | 27 | Texas | 85000 | 2007-12-13 |
|
2188
|
-
| 002 | Allen | 25 | Texas | N/A | 2005-07-13 |
|
2189
|
-
| 008 | Paul | 24 | Houston | 20000 | 2005-07-13 |
|
2190
|
-
| 009 | James | 44 | Norway | 5000 | 2005-07-13 |
|
2191
|
-
| 010 | James | 45 | Texas | 5000 | |
|
2192
|
-
+=====+=======+=====+============+========+============+
|
2193
|
-
#+END_EXAMPLE
|
2194
|
-
|
2195
|
-
The ~string: 'R'~ directive causes all the cells to be right-aligned except
|
2196
|
-
~:id~ which specifies centering for the ~:id~ column only. The ~n[N/A]~
|
2197
|
-
directive for specifies how nil are displayed in the numeric column, ~:salary~,
|
2198
|
-
but not for other nils, such as in the last row of the ~:join_date~ column.
|
2199
|
-
|
2200
3185
|
* Development
|
2201
|
-
|
2202
3186
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
2203
3187
|
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
2204
3188
|
prompt that will allow you to experiment.
|
2205
3189
|
|
2206
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
2207
|
-
release a new version, update the version number in `version.rb`, and then run
|
2208
|
-
`bundle exec rake release`, which will create a git tag for the version, push
|
2209
|
-
git commits and tags, and push the `.gem` file to
|
2210
|
-
[rubygems.org](https://rubygems.org).
|
3190
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
2211
3191
|
|
2212
3192
|
* Contributing
|
2213
|
-
|
2214
3193
|
Bug reports and pull requests are welcome on GitHub at
|
2215
3194
|
https://github.com/ddoherty03/fat_table.
|