fat_period 1.0.0 → 1.0.2

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.
data/README.org ADDED
@@ -0,0 +1,2196 @@
1
+ #+OPTIONS: :toc
2
+ #+LATEX_HEADER: \usepackage[margin=0.75in]{geometry}
3
+
4
+ #+BEGIN_COMMENT
5
+ This is for markdown output:
6
+
7
+ The following is for org.
8
+ #+END_COMMENT
9
+
10
+ [[https://travis-ci.org/ddoherty03/fat_table.svg?branch=master]]
11
+
12
+ * Introduction
13
+
14
+ ~FatPeriod~ provides a Ruby ~Period~ class for dealing with time periods, that
15
+ is ranges whose endpoints are ~Date~s. Set operations, for example, are
16
+ provided for Period, as well as methods for parsing strings into Periods and
17
+ methods for breaking a larger periods into an array of smaller periods of
18
+ various 'chunk' sizes that correspond to calendar-related periods such as
19
+ days, weeks, months, and so forth.'
20
+
21
+ * Installation
22
+
23
+ ** Installing the gem
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ #+BEGIN_SRC ruby
28
+ gem 'fat_period'
29
+ #+END_SRC
30
+
31
+ And then execute:
32
+
33
+ #+BEGIN_SRC sh
34
+ $ bundle
35
+ #+END_SRC
36
+
37
+ Or install it yourself as:
38
+
39
+ #+BEGIN_SRC sh
40
+ $ gem install fat_period
41
+ #+END_SRC
42
+
43
+ * Usage
44
+
45
+ ** Construction of Periods
46
+
47
+ A Period can be constructed with two arguments for the begin and end date.
48
+ Each can either be a Date, a string parseable as a Date by the Date.parse
49
+ method, or an object that responds to ~#to_s~ and can be parsed as a Date by
50
+ Date.parse:
51
+
52
+ #+begin_SRC ruby
53
+ p1 = Period.new(Date.today, Date.today + 30)
54
+ p2 = Period.new('Nov 22, 1963', Date.today)
55
+ p3 = Period.new('1961-01-21', '1963-11-22')
56
+ puts "Camelot lasted #{p3.length} days"
57
+ #+end_SRC
58
+
59
+
60
+
61
+ ** Quick Start
62
+
63
+ ~FatTable~ provides table objects as a data type that can be constructed and
64
+ operated on in a number of ways. Here's a quick example to illustrate the use of
65
+ ~FatTable~. See the detailed explanations further on down.
66
+
67
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
68
+ #+BEGIN_SRC ruby
69
+ require 'fat_table'
70
+
71
+ data =
72
+ [['Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Ok'],
73
+ ['2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ENTITY3', 'F'],
74
+ ['2013-05-02', 'P', 118_186.40, 118_186.4, 11.8500, 'ENTITY1', 'T'],
75
+ ['2013-05-20', 'S', 12_000.00, 5046.00, 28.2804, 'ENTITY3', 'F'],
76
+ ['2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ENTITY3', 'T'],
77
+ ['2013-05-23', 'S', 39_906.00, 16_780.47, 25.1749, 'ENTITY3', 'T'],
78
+ ['2013-05-20', 'S', 85_000.00, 35_742.50, 28.3224, 'ENTITY3', 'T'],
79
+ ['2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ENTITY1', 'T'],
80
+ ['2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ENTITY3', 'T'],
81
+ ['2013-05-20', 'S', 33_302.00, 14_003.49, 28.6383, 'ENTITY3', 'T'],
82
+ ['2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ENTITY3', 'T'],
83
+ ['2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ENTITY3', 'T'],
84
+ ['2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ENTITY3', 'F']]
85
+
86
+ # Build the Table and then perform chained operations on it
87
+
88
+ table = FatTable.from_aoa(data) \
89
+ .where('shares > 2000') \
90
+ .order_by(:date, :code) \
91
+ .select(:date, :code, :shares,
92
+ :price, :ok, ref: '@row') \
93
+ .select(:ref, :date, :code,
94
+ :shares, :price, :ok)
95
+
96
+ # Convert the table to an ASCII text string
97
+
98
+ table.to_text do |fmt|
99
+ # Add some table footers
100
+ fmt.avg_footer(:price, :shares)
101
+ fmt.sum_footer(:shares)
102
+ # Add a group footer
103
+ fmt.gfooter('Avg', shares: :avg, price: :avg)
104
+ # Formats for all locations
105
+ fmt.format(ref: 'CB', numeric: 'R', boolean: 'CY')
106
+ # Formats for different "locations" in the table
107
+ fmt.format_for(:header, string: 'CB')
108
+ fmt.format_for(:body, code: 'C', shares: ',0.1', price: '0.4', )
109
+ fmt.format_for(:bfirst, price: '$0.4', )
110
+ fmt.format_for(:footer, shares: 'B,0.1', price: '$B0.4', )
111
+ fmt.format_for(:gfooter, shares: 'B,0.1', price: 'B0.4', )
112
+ end
113
+ #+END_SRC
114
+
115
+ #+BEGIN_EXAMPLE
116
+ +=========+============+======+=============+==========+====+
117
+ | Ref | Date | Code | Shares | Price | Ok |
118
+ +---------+------------+------+-------------+----------+----+
119
+ | 1 | 2013-05-02 | P | 118,186.4 | $11.8500 | Y |
120
+ | 2 | 2013-05-02 | P | 795,546.2 | 1.1850 | Y |
121
+ +---------+------------+------+-------------+----------+----+
122
+ | Avg | | | 456,866.3 | 6.5175 | |
123
+ +---------+------------+------+-------------+----------+----+
124
+ | 3 | 2013-05-20 | S | 5,046.0 | 28.2804 | N |
125
+ | 4 | 2013-05-20 | S | 35,742.5 | 28.3224 | Y |
126
+ | 5 | 2013-05-20 | S | 14,003.5 | 28.6383 | Y |
127
+ +---------+------------+------+-------------+----------+----+
128
+ | Avg | | | 18,264.0 | 28.4137 | |
129
+ +---------+------------+------+-------------+----------+----+
130
+ | 6 | 2013-05-23 | S | 3,364.0 | 27.1083 | Y |
131
+ | 7 | 2013-05-23 | S | 16,780.5 | 25.1749 | Y |
132
+ | 8 | 2013-05-23 | S | 9,694.2 | 26.8015 | N |
133
+ +---------+------------+------+-------------+----------+----+
134
+ | Avg | | | 9,946.2 | 26.3616 | |
135
+ +---------+------------+------+-------------+----------+----+
136
+ | 9 | 2013-05-29 | S | 6,601.9 | 24.7790 | N |
137
+ | 10 | 2013-05-29 | S | 5,659.5 | 24.7464 | Y |
138
+ | 11 | 2013-05-29 | S | 6,686.0 | 24.5802 | Y |
139
+ +---------+------------+------+-------------+----------+----+
140
+ | Avg | | | 6,315.8 | 24.7019 | |
141
+ +---------+------------+------+-------------+----------+----+
142
+ | 12 | 2013-05-30 | S | 2,808.5 | 25.0471 | Y |
143
+ +---------+------------+------+-------------+----------+----+
144
+ | Avg | | | 2,808.5 | 25.0471 | |
145
+ +---------+------------+------+-------------+----------+----+
146
+ | Average | | | 85,009.9 | $23.0428 | |
147
+ +---------+------------+------+-------------+----------+----+
148
+ | Total | | | 1,020,119.1 | | |
149
+ +=========+============+======+=============+==========+====+
150
+ #+END_EXAMPLE
151
+
152
+ ** A Word About the Examples
153
+
154
+ When you install the ~fat_table~ gem, you have access to a program ~ft_console~,
155
+ which opens a ~pry~ session with ~fat_table~ loaded and the tables used in the
156
+ examples in this ~README~ defined as instance variables so you can experiment
157
+ with them. Because they are defined as instance variables, you have to write
158
+ ~tab1~ as ~@tab1~ in ~ft_console~, but otherwise the examples should work as
159
+ shown in this ~README~.
160
+
161
+ The examples in this ~README~ file are executed as code blocks within the
162
+ ~README.org~ file, so they typically end with a call to ~.to_aoa~. That causes
163
+ the table to be inserted into the file and formatted as a table. With
164
+ ~ft_console~, you should instead display your tables with ~.to_text~ or
165
+ ~.to_term~. These will return a string that you can print to the terminal with
166
+ ~puts~.
167
+
168
+ To read in the table used in the Quick Start section above, you might do the
169
+ following:
170
+
171
+ #+BEGIN_EXAMPLE
172
+ $ ft_console[1] pry(main)> ls
173
+ ActiveSupport::ToJsonWithActiveSupportEncoder#methods: to_json
174
+ self.methods: inspect to_s
175
+ instance variables:
176
+ @aoa @tab1 @tab2 @tab_a @tab_b @tt
177
+ @data @tab1_str @tab2_str @tab_a_str @tab_b_str
178
+ locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_ lib str version
179
+ [2] pry(main)> table = FatTable.from_aoa(@data)
180
+ => #<FatTable::Table:0x0055b40e6cd870
181
+ @boundaries=[],
182
+ @columns=
183
+ [#<FatTable::Column:0x0055b40e6cc948
184
+ @header=:date,
185
+ @items=
186
+ [Wed, 29 May 2013,
187
+ Thu, 02 May 2013,
188
+ Mon, 20 May 2013,
189
+ Thu, 23 May 2013,
190
+ Thu, 23 May 2013,
191
+ Mon, 20 May 2013,
192
+ Thu, 02 May 2013,
193
+ Wed, 29 May 2013,
194
+ Mon, 20 May 2013,
195
+ ...
196
+ @items=["ENTITY3", "ENTITY1", "ENTITY3", "ENTITY3", "ENTITY3", "ENTITY3", "ENTITY1", "ENTITY3", "ENTITY3", "ENTITY3", "ENTITY3", "ENTITY3"],
197
+ @raw_header=:info,
198
+ @type="String">,
199
+ #<FatTable::Column:0x0055b40e6d2668 @header=:ok, @items=[false, true, false, true, true, true, true, true, true, true, true, false], @raw_header=:ok, @type="Boolean">]>
200
+ [3] pry(main)> puts table.to_text
201
+ +============+======+==========+==========+=========+=========+====+
202
+ | Date | Code | Raw | Shares | Price | Info | Ok |
203
+ +------------+------+----------+----------+---------+---------+----+
204
+ | 2013-05-29 | S | 15700.0 | 6601.85 | 24.779 | ENTITY3 | F |
205
+ | 2013-05-02 | P | 118186.4 | 118186.4 | 11.85 | ENTITY1 | T |
206
+ | 2013-05-20 | S | 12000.0 | 5046.0 | 28.2804 | ENTITY3 | F |
207
+ | 2013-05-23 | S | 8000.0 | 3364.0 | 27.1083 | ENTITY3 | T |
208
+ | 2013-05-23 | S | 39906.0 | 16780.47 | 25.1749 | ENTITY3 | T |
209
+ | 2013-05-20 | S | 85000.0 | 35742.5 | 28.3224 | ENTITY3 | T |
210
+ | 2013-05-02 | P | 795546.2 | 795546.2 | 1.185 | ENTITY1 | T |
211
+ | 2013-05-29 | S | 13459.0 | 5659.51 | 24.7464 | ENTITY3 | T |
212
+ | 2013-05-20 | S | 33302.0 | 14003.49 | 28.6383 | ENTITY3 | T |
213
+ | 2013-05-29 | S | 15900.0 | 6685.95 | 24.5802 | ENTITY3 | T |
214
+ | 2013-05-30 | S | 6679.0 | 2808.52 | 25.0471 | ENTITY3 | T |
215
+ | 2013-05-23 | S | 23054.0 | 9694.21 | 26.8015 | ENTITY3 | F |
216
+ +============+======+==========+==========+=========+=========+====+
217
+ => nil
218
+ [4] pry(main)>
219
+ #+END_EXAMPLE
220
+
221
+ If you use ~puts table.to_term~, you can see the effect of the color formatting
222
+ directives.
223
+
224
+ ** Anatomy of a Table
225
+ *** Columns
226
+
227
+ ~FatTable::Table~ objects consist of an array of ~FatTable::Column~ objects.
228
+ Each ~Column~ has a header, a type, and an array of items, all of the given type
229
+ or nil. There are only five permissible types for a ~Column~:
230
+
231
+ 1. *Boolean* (for holding ruby ~TrueClass~ and ~FalseClass~ objects),
232
+ 2. *DateTime* (for holding ruby ~DateTime~ or ~Date~ objects),
233
+ 3. *Numeric* (for holding ruby ~Integer~, ~Rational~, or ~BigDecimal~ objects),
234
+ 4. *String* (for ruby ~String~ objects), or
235
+ 5. *NilClass* (for the undetermined column type).
236
+
237
+ When a ~Table~ is constructed from an external source, all ~Columns~ start out
238
+ having a type of ~NilClass~, that is, their type is as yet undetermined. When a
239
+ string or object of one of the four determined types is added to a ~Column~, it
240
+ fixes the type of the column and all further items added to the ~Column~ must
241
+ either be ~nil~ (indicating no value) or be capable of being coerced to the
242
+ column's type. Otherwise, ~FatTable~ raises an exception.
243
+
244
+ Items of input must be either one of the permissible ruby objects or strings. If
245
+ they are strings, ~FatTable~ attempts to parse them as one of the permissible
246
+ types as follows:
247
+
248
+ - Boolean :: the strings, ~'t'~, ~'true'~, ~'yes'~, or ~'y'~, regardless of
249
+ case, are interpreted as ~TrueClass~ and the strings, ~'f'~, ~'false'~,
250
+ ~'no'~, or ~'n'~, regardless of case, are interpreted as ~FalseClass~, in
251
+ either case resulting in a Boolean column. Empty strings in a column
252
+ already having a Boolean type are converted to ~nil~.
253
+ - DateTime :: strings that contain patterns of ~'yyyy-mm-dd'~ or ~'yyyy/mm/dd'~
254
+ or ~'mm-dd-yyy'~ or ~'mm/dd/yyyy'~ or any of the foregoing with an added
255
+ ~'Thh:mm:ss'~ or ~'Thh:mm'~ will be interpreted as a ~DateTime~ or a ~Date~
256
+ (if there are no sub-day time components present). The number of digits in
257
+ the month and day can be one or two, but the year component must be four
258
+ digits. Any time components are valid if they can be properly interpreted
259
+ by ~DateTime.parse~. Org mode timestamps (any of the foregoing surrounded
260
+ by square '~[]~' or pointy '~<>~' brackets), active or inactive, are valid
261
+ input strings for ~DateTime~ columns. Empty strings in a column already
262
+ having the ~DateTime~ type are converted to ~nil~.
263
+ - Numeric :: all commas ~','~, underscores, ~'_'~, and ~'$'~ dollar signs (or
264
+ other currency symbol as set by ~FatTable.currency_symbol~ are removed from
265
+ the string and if the remaining string can be interpreted as a ~Numeric~,
266
+ it will be. It is interpreted as an ~Integer~ if there are no decimal
267
+ places in the remaining string, as a ~Rational~ if the string has the form
268
+ '~<number>:<number>~' or '~<number>/<number>~', or as a ~BigDecimal~ if
269
+ there is a decimal point in the remaining string. Empty strings in a column
270
+ already having the Numeric type are converted to nil.
271
+ - String :: if all else fails, ~FatTable~ applies ~#to_s~ to the input value
272
+ and, treats it as an item of type ~String~. Empty strings in a column
273
+ already having the ~String~ type are kept as empty strings.
274
+ - NilClass :: until the input contains a non-blank string that can be parsed as
275
+ one of the other types, it has this type, meaning that the type is still
276
+ open. A column comprised completely of blank strings or ~nils~ will retain
277
+ the ~NilClass~ type.
278
+
279
+ *** Headers
280
+
281
+ Headers for the columns are formed from the input. No two columns in a table can
282
+ have the same header. Headers in the input are converted to symbols by
283
+
284
+ - converting the header to a string with ~#to_s~,
285
+ - converting any run of blanks to an underscore ~_~,
286
+ - removing any characters that are not letters, numbers, or underscores, and
287
+ - lowercasing all remaining letters
288
+
289
+ Thus, a header of ~'Date'~ becomes ~:date~, a header of ~'Id Number'~ becomes,
290
+ ~:id_number~, etc. When referring to a column in code, you must use the symbol
291
+ form of the header.
292
+
293
+ If no sensible headers can be discerned from the input, headers of the form
294
+ ~:col_1~, ~:col_2~, etc., are synthesized.
295
+
296
+ *** Groups
297
+
298
+ The rows of a ~FatTable~ table can be sub-divided into groups, either from
299
+ markers in the input or as a result of certain operations. There is only one
300
+ level of grouping, so ~FatTable~ has no concept of sub-groups. Groups can be
301
+ shown on output with rules or "hlines" that underline the last row in each
302
+ group, and you can decorate the output with group footers that summarize the
303
+ columns in each group.
304
+
305
+ ** Constructing Tables
306
+ *** Empty Tables
307
+
308
+ You can create an empty table with ~FatTable.new~, and then add rows with the
309
+ ~<<~ operator and a Hash:
310
+
311
+ #+BEGIN_SRC ruby
312
+ tab = FatTable.new
313
+ tab << { a: 1, b: 2, c: "<2017-01-21>', d: 'f', e: '' }
314
+ tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
315
+ tab.to_aoa
316
+ #+END_SRC
317
+
318
+ After this, the table will have column headers ~:a~, ~:b~, ~:c~, ~:d~, and ~:e~.
319
+ Column, ~:a~ and ~:b~ will have type Numeric, column ~:c~ will have type
320
+ ~DateTime~, and column ~:d~ will have type ~Boolean~. Column ~:e~ will still
321
+ have an open type. Notice that dates in the input can be wrapped in brackets as
322
+ in org-mode time stamps.
323
+
324
+ *** From CSV or Org Mode files or strings
325
+
326
+ Tables can also be read from ~.csv~ files or files containing ~org-mode~ tables.
327
+ In the case of org-mode files, ~FatTable~ skips through the file until it finds
328
+ a line that look like a table, that is, it begins with any number of spaces
329
+ followed by ~|-~. Only the first table in an ~.org~ file is read.
330
+
331
+ For both ~.csv~ and ~.org~ files, the first row in the tables is taken as the
332
+ header row, and the headers are converted to symbols as described above.
333
+
334
+ #+BEGIN_SRC ruby
335
+ tab1 = FatTable.from_csv_file('~/data.csv')
336
+ tab2 = FatTable.from_org_file('~/project.org')
337
+
338
+ csv_body = <<-EOS
339
+ Ref,Date,Code,RawShares,Shares,Price,Info
340
+ 1,2006-05-02,P,5000,5000,8.6000,2006-08-09-1-I
341
+ 2,2006-05-03,P,5000,5000,8.4200,2006-08-09-1-I
342
+ 3,2006-05-04,P,5000,5000,8.4000,2006-08-09-1-I
343
+ 4,2006-05-10,P,8600,8600,8.0200,2006-08-09-1-D
344
+ 5,2006-05-12,P,10000,10000,7.2500,2006-08-09-1-D
345
+ 6,2006-05-12,P,2000,2000,6.7400,2006-08-09-1-I
346
+ EOS
347
+
348
+ tab3 = FatTable.from_csv_string(csv_body)
349
+
350
+ org_body = <<-EOS
351
+ .* Smith Transactions
352
+ :PROPERTIES:
353
+ :TABLE_EXPORT_FILE: smith.csv
354
+ :END:
355
+
356
+ #+TBLNAME: smith_tab
357
+ | Ref | Date | Code | Raw | Shares | Price | Info |
358
+ |-----+------------+------+---------+--------+----------+---------|
359
+ | 29 | 2013-05-02 | P | 795,546 | 2,609 | 1.18500 | ENTITY1 |
360
+ | 30 | 2013-05-02 | P | 118,186 | 388 | 11.85000 | ENTITY1 |
361
+ | 31 | 2013-05-02 | P | 340,948 | 1,926 | 1.18500 | ENTITY2 |
362
+ | 32 | 2013-05-02 | P | 50,651 | 286 | 11.85000 | ENTITY2 |
363
+ | 33 | 2013-05-20 | S | 12,000 | 32 | 28.28040 | ENTITY3 |
364
+ | 34 | 2013-05-20 | S | 85,000 | 226 | 28.32240 | ENTITY3 |
365
+ | 35 | 2013-05-20 | S | 33,302 | 88 | 28.63830 | ENTITY3 |
366
+ | 36 | 2013-05-23 | S | 8,000 | 21 | 27.10830 | ENTITY3 |
367
+ | 37 | 2013-05-23 | S | 23,054 | 61 | 26.80150 | ENTITY3 |
368
+ | 38 | 2013-05-23 | S | 39,906 | 106 | 25.17490 | ENTITY3 |
369
+ | 39 | 2013-05-29 | S | 13,459 | 36 | 24.74640 | ENTITY3 |
370
+ | 40 | 2013-05-29 | S | 15,700 | 42 | 24.77900 | ENTITY3 |
371
+ | 41 | 2013-05-29 | S | 15,900 | 42 | 24.58020 | ENTITY3 |
372
+ | 42 | 2013-05-30 | S | 6,679 | 18 | 25.04710 | ENTITY3 |
373
+
374
+ .* Another Heading
375
+ EOS
376
+
377
+ tab4 = FatTable.from_org_string(org_body)
378
+ #+END_SRC
379
+
380
+ *** From Arrays of Arrays
381
+
382
+ You can also initialize a table directly from ruby data structures. You can, for
383
+ example, build a table from an array of arrays:
384
+
385
+ #+BEGIN_SRC ruby
386
+ aoa = [
387
+ ['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
388
+ [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ENTITY1', 'T'],
389
+ [2, '2013-05-02', 'P', 118_186.40, 118_186.4, 11.8500, 'ENTITY1', 'T'],
390
+ [7, '2013-05-20', 'S', 12_000.00, 5046.00, 28.2804, 'ENTITY3', 'F'],
391
+ [8, '2013-05-20', 'S', 85_000.00, 35_742.50, 28.3224, 'ENTITY3', 'T'],
392
+ [9, '2013-05-20', 'S', 33_302.00, 14_003.49, 28.6383, 'ENTITY3', 'T'],
393
+ [10, '2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ENTITY3', 'T'],
394
+ [11, '2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ENTITY3', 'F'],
395
+ [12, '2013-05-23', 'S', 39_906.00, 16_780.47, 25.1749, 'ENTITY3', 'T'],
396
+ [13, '2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ENTITY3', 'T'],
397
+ [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ENTITY3', 'F'],
398
+ [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ENTITY3', 'T'],
399
+ [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ENTITY3', 'T']
400
+ ]
401
+ tab = FatTable.from_aoa(aoa)
402
+ #+END_SRC
403
+
404
+ Notice that the values can either be ruby objects, such as the Integer ~85_000~,
405
+ or strings that can be parsed into one of the permissible column types.
406
+
407
+ This method of building a table, ~.from_aoa~, is particularly useful in dealing
408
+ with Emacs org-mode code blocks. Tables in org-mode are passed to code blocks as
409
+ arrays of arrays. Likewise, a result of a code block in the form of an array of
410
+ arrays is displayed as an org-mode table:
411
+
412
+ #+BEGIN_EXAMPLE
413
+ #+NAME: trades1
414
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | LP | QP | IPLP | IPQP |
415
+ |------+------------+------+--------+-----+------+--------+-------+--------+--------+--------|
416
+ | T001 | 2016-11-01 | P | 7.7000 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
417
+ | T002 | 2016-11-01 | P | 7.7500 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
418
+ | T003 | 2016-11-01 | P | 7.5000 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
419
+ | T004 | 2016-11-01 | S | 7.5500 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
420
+ | T005 | 2016-11-01 | S | 7.5000 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
421
+ | T006 | 2016-11-01 | S | 7.6000 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
422
+ | T007 | 2016-11-01 | S | 7.6500 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
423
+ | T008 | 2016-11-01 | P | 7.6500 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
424
+ | T009 | 2016-11-01 | P | 7.6000 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
425
+ | T010 | 2016-11-01 | P | 7.5500 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
426
+ | T011 | 2016-11-02 | P | 7.4250 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
427
+ | T012 | 2016-11-02 | P | 7.5500 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
428
+ | T013 | 2016-11-02 | P | 7.3500 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
429
+ | T014 | 2016-11-02 | P | 7.4500 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
430
+ | T015 | 2016-11-02 | P | 7.7500 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
431
+ | T016 | 2016-11-02 | P | 8.2500 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
432
+
433
+ #+HEADER: :colnames no
434
+ :#+BEGIN_SRC ruby :var tab=trades1
435
+ require 'fat_table'
436
+ tab = FatTable.from_aoa(tab).where('shares > 500')
437
+ tab.to_aoa
438
+ :#+END_SRC
439
+
440
+ #+RESULTS:
441
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
442
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
443
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
444
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
445
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
446
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
447
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
448
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
449
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
450
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
451
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
452
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
453
+ #+END_EXAMPLE
454
+
455
+ This example illustrates several things:
456
+
457
+ 1. The named org-mode table, ~trades1~, can be passed into a ruby code block
458
+ using the ~:var tab=trades1~ header argument to the code block; that makes
459
+ the variable ~tab~ available to the code block as an array of arrays, which
460
+ ~FatTable~ then uses to initialize the table.
461
+ 2. The code block requires that you set ~:colnames no~ in the header arguments.
462
+ This suppresses org-mode's own processing of the header line so that
463
+ ~FatTable~ can see the headers. Failure to do this will cause an error.
464
+ 3. The table is subjected to some processing, in this case selecting those rows
465
+ where the number of shares is greater than 500. More on that later.
466
+ 4. ~FatTable~ passes back to org-mode an array of arrays using the ~.to_aoa~
467
+ method. In an ~org-mode~ buffer, these are rendered as tables. We'll often
468
+ apply ~.to_aoa~ at the end of example blocks in this ~README~ to render the
469
+ results as a table inside this file. As we'll see below, ~.to_aoa~ can also
470
+ take a block to which formatting and footer directives can be attached.
471
+
472
+ *** From Arrays of Hashes
473
+
474
+ A second ruby data structure that can be used to initialize a ~FatTable~ table
475
+ is an array of ruby Hashes. Each hash represents a row of the table, and the
476
+ headers of the table are taken from the keys of the hashes. Accordingly, all the
477
+ hashes should have the same keys.
478
+
479
+ This same method can in fact take an array of any objects that can be converted
480
+ to a Hash with the ~#to_h~ method, so you can use an array of your own objects
481
+ to initialize a table, provided that you define a suitable ~#to_h~ method for
482
+ the objects' class.
483
+
484
+ #+BEGIN_SRC ruby
485
+ aoh = [
486
+ { ref: 'T001', date: '2016-11-01', code: 'P', price: '7.7000', shares: 100 },
487
+ { ref: 'T002', date: '2016-11-01', code: 'P', price: 7.7500, shares: 200 },
488
+ { ref: 'T003', date: '2016-11-01', code: 'P', price: 7.5000, shares: 800 },
489
+ { ref: 'T004', date: '2016-11-01', code: 'S', price: 7.5500, shares: 6811 },
490
+ { ref: 'T005', date: Date.today, code: 'S', price: 7.5000, shares: 4000 },
491
+ { ref: 'T006', date: '2016-11-01', code: 'S', price: 7.6000, shares: 1000 },
492
+ { ref: 'T007', date: '2016-11-01', code: 'S', price: 7.6500, shares: 200 },
493
+ { ref: 'T008', date: '2016-11-01', code: 'P', price: 7.6500, shares: 2771 },
494
+ { ref: 'T009', date: '2016-11-01', code: 'P', price: 7.6000, shares: 9550 },
495
+ { ref: 'T010', date: '2016-11-01', code: 'P', price: 7.5500, shares: 3175 },
496
+ { ref: 'T011', date: '2016-11-02', code: 'P', price: 7.4250, shares: 100 },
497
+ { ref: 'T012', date: '2016-11-02', code: 'P', price: 7.5500, shares: 4700 },
498
+ { ref: 'T013', date: '2016-11-02', code: 'P', price: 7.3500, shares: 53100 },
499
+ { ref: 'T014', date: '2016-11-02', code: 'P', price: 7.4500, shares: 5847 },
500
+ { ref: 'T015', date: '2016-11-02', code: 'P', price: 7.7500, shares: 500 },
501
+ { ref: 'T016', date: '2016-11-02', code: 'P', price: 8.2500, shares: 100 }
502
+ ]
503
+ tab = FatTable.from_aoh(aoh)
504
+ #+END_SRC
505
+
506
+ Notice, again, that the values can either be ruby objects, such as ~Date.today~,
507
+ or strings that can be parsed into one of the permissible column types.
508
+
509
+ *** From SQL queries
510
+
511
+ Another way to initialize a ~FatTable~ table is with the results of a SQL query.
512
+ ~FatTable~ uses the ~sequel~ gem to query databases. You must first set the
513
+ database parameters to be used for the queries.
514
+
515
+ #+BEGIN_SRC ruby
516
+ # This automatically requires sequel.
517
+ require 'fat_table'
518
+ FatTable.connect(adapter: 'postgres',
519
+ database: 'XXX_development',
520
+ user: 'ken',
521
+ password: 'imsecret',
522
+ host: 'db.lan')
523
+ tab = FatTable.from_sql('select * from trades;')
524
+ #+END_SRC
525
+
526
+ The arguments to ~connect~ are simply passed on to ~sequel~'s connect method, so
527
+ any set of arguments that work for it should work for ~connect~. Alternatively,
528
+ you can build the ~Sequel~ connection directly with ~Sequel.connect~ or with
529
+ adapter-specific ~Sequel~ connection methods and let ~FatTable~ know to use that
530
+ connection:
531
+
532
+ #+BEGIN_SRC ruby
533
+ require 'fat_table'
534
+ FatTable.db = Sequel.connect('postgres://user:password@localhost/dbname')
535
+ FatTable.db = Sequel.ado(conn_string: 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=drive:\path\filename.accdb')
536
+ #+END_SRC
537
+
538
+ Consult ~Sequel's~ documentation for details on its connection methods.
539
+ [[http://sequel.jeremyevans.net/rdoc/files/doc/opening_databases_rdoc.html]]
540
+
541
+ The ~.connect~ function need only be called once, and the database handle it
542
+ creates will be used for all subsequent ~.from_sql~ calls until ~.connect~ is
543
+ called again.
544
+
545
+ *** Marking Groups in Input
546
+
547
+ ~FatTable~ tables has a concept of "groups" of rows that play a role in many of
548
+ the methods for operating on them as explained [[Groups][below]].
549
+
550
+ The ~.from_aoa~ and ~.from_aoh~ functions take an optional keyword parameter
551
+ ~hlines:~ that, if set to ~true~, causes them to mark group boundaries in the
552
+ table wherever a row Array (for ~.from_aoa~) or Hash (for ~.from_aoh~) is
553
+ followed by a ~nil~. Each boundary means that the rows above it and after the
554
+ header or prior group boundary all belong to a group. By default ~hlines~ is
555
+ false for both functions so neither expects hlines in its input.
556
+
557
+ In the case of ~.from_aoa~, if ~hlines:~ is set true, the input must also
558
+ include a ~nil~ in the second element of the outer array to indicate that the
559
+ first row is to be used as headers. Otherwise, it will synthesize headers of
560
+ the form ~:col_1~, ~:col_2~, ... ~:col_n~.
561
+
562
+ In org mode table text passed to ~.from_org_file~ and ~.from_org_string~, you
563
+ /must/ mark the header row by following it with an hrule and you /may/ mark
564
+ group boundaries with an hrule. In org mode tables, hlines are table rows
565
+ beginning with something like '~|---~'. The ~.from_org_...~ functions always
566
+ recognizes hlines in the input, so it takes no ~hlines:~ keyword parameter.
567
+
568
+ ** Accessing Parts of Tables
569
+
570
+ *** Rows
571
+
572
+ A ~FatTable~ table is an Enumerable, yielding each row of the table as a Hash
573
+ keyed on the header symbols. The method ~Table#rows~ returns an Array of the
574
+ rows as Hashes as well.
575
+
576
+ You can also use indexing to access a row of the table by number. Using an
577
+ integer index returns a Hash of the given row. Thus, ~tab[20]~ returns the 21st
578
+ data row of the table, while ~tab[0]~ returns the first row and tab[-1] returns
579
+ the last row.
580
+
581
+ *** Columns
582
+
583
+ If the index provided to ~[]~ is a string or a symbol, it returns an Array of
584
+ the items of the column with that header. Thus, ~tab[:ref]~ returns an Array of
585
+ all the items of the table's ~:ref~ column.
586
+
587
+ *** Cells
588
+
589
+ The two forms of indexing can be combined to access individual cells of the
590
+ table:
591
+
592
+ #+BEGIN_SRC ruby
593
+ tab[13] # => Hash of the 14th row
594
+ tab[:date] # => Array of all Dates in the :date column
595
+ tab[13][:date] # => The Date in the 14th row
596
+ tab[:date][13] # => The Date in the 14th row; indexes can be in either order.
597
+ #+END_SRC
598
+
599
+ *** Other table attributes
600
+
601
+ #+BEGIN_SRC ruby
602
+ tab.headers # => an Array of the headers in symbol form
603
+ tab.types # => a Hash mapping headers to column types
604
+ tab.size # => the number of rows in the table
605
+ tab.width # => the number of columns in the table
606
+ tab.empty? # => is the table empty?
607
+ tab.column?(head) # => does the table have a column with the given header?
608
+ tab.groups # => return an Array of the table's groups as Arrays of row Hashes.
609
+ #+END_SRC
610
+
611
+ ** Operations on Tables
612
+
613
+ Once you have one or more tables, you will likely want to perform operations on
614
+ them. The operations provided by ~FatTable~ are the subject of this section.
615
+ Before getting into the operations, though, there are a couple of issues that
616
+ cut across all or many of the operations.
617
+
618
+ First, tables are by and large immutable objects. Each operation creates a new
619
+ table without affecting the input tables. The only exception is the ~degroup!~
620
+ operation, which mutates the receiver table by removing its group boundaries.
621
+
622
+ Second, because each operation returns a ~FatTable::Table~ object, the
623
+ operations are chainable.
624
+
625
+ <<Groups>>
626
+ Third, ~FatTable::Table~ objects can have "groups" of rows within the table.
627
+ These can be decorated with hlines and group footers on output. Some of these
628
+ operations result in marking group boundaries in the result table, others remove
629
+ group boundaries that may have existed in the input table. Operations that
630
+ either create or remove groups will be noted below.
631
+
632
+ Finally, the operations are for the most part patterned on SQL table operations,
633
+ but when expressions play a role, you write them using ruby syntax rather than
634
+ SQL.
635
+
636
+ *** Example Input Table
637
+
638
+ For illustration purposes assume that the following tables are read into ruby
639
+ variables called '~tab1~' and '~tab2~. We have given the table groups, marked by
640
+ the hlines below, and included some duplicate rows to illustrate the effect of
641
+ certain operations on groups and duplicates.
642
+
643
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
644
+ #+BEGIN_SRC ruby
645
+ require 'fat_table'
646
+
647
+ tab1_str = <<-EOS
648
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | LP | QP | IPLP | IPQP |
649
+ |------+------------------+------+--------+-----+------+--------+------+-------+--------+--------|
650
+ | T001 | [2016-11-01 Tue] | P | 7.7000 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
651
+ | T002 | [2016-11-01 Tue] | P | 7.7500 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
652
+ | T003 | [2016-11-01 Tue] | P | 7.5000 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
653
+ | T003 | [2016-11-01 Tue] | P | 7.5000 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
654
+ |------+------------------+------+--------+-----+------+--------+------+-------+--------+--------|
655
+ | T004 | [2016-11-01 Tue] | S | 7.5500 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
656
+ | T005 | [2016-11-01 Tue] | S | 7.5000 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
657
+ | T006 | [2016-11-01 Tue] | S | 7.6000 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
658
+ | T006 | [2016-11-01 Tue] | S | 7.6000 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
659
+ | T007 | [2016-11-01 Tue] | S | 7.6500 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
660
+ | T008 | [2016-11-01 Tue] | P | 7.6500 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
661
+ | T009 | [2016-11-01 Tue] | P | 7.6000 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
662
+ |------+------------------+------+--------+-----+------+--------+------+-------+--------+--------|
663
+ | T010 | [2016-11-01 Tue] | P | 7.5500 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
664
+ | T011 | [2016-11-02 Wed] | P | 7.4250 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
665
+ | T012 | [2016-11-02 Wed] | P | 7.5500 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
666
+ | T012 | [2016-11-02 Wed] | P | 7.5500 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
667
+ | T013 | [2016-11-02 Wed] | P | 7.3500 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
668
+ |------+------------------+------+--------+-----+------+--------+------+-------+--------+--------|
669
+ | T014 | [2016-11-02 Wed] | P | 7.4500 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
670
+ | T015 | [2016-11-02 Wed] | P | 7.7500 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
671
+ | T016 | [2016-11-02 Wed] | P | 8.2500 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
672
+ EOS
673
+
674
+ tab2_str = <<-EOS
675
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | LP | QP | IPLP | IPQP |
676
+ |------+------------------+------+--------+-----+------+--------+-------+------+--------+--------|
677
+ | T003 | [2016-11-01 Tue] | P | 7.5000 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
678
+ | T003 | [2016-11-01 Tue] | P | 7.5000 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
679
+ | T017 | [2016-11-01 Tue] | P | 8.3 | F | T | 1801 | 1201 | 600 | 0.2453 | 0.1924 |
680
+ |------+------------------+------+--------+-----+------+--------+-------+------+--------+--------|
681
+ | T018 | [2016-11-01 Tue] | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
682
+ | T018 | [2016-11-01 Tue] | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
683
+ | T006 | [2016-11-01 Tue] | S | 7.6000 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
684
+ | T007 | [2016-11-01 Tue] | S | 7.6500 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
685
+ |------+------------------+------+--------+-----+------+--------+-------+------+--------+--------|
686
+ | T014 | [2016-11-02 Wed] | P | 7.4500 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
687
+ | T015 | [2016-11-02 Wed] | P | 7.7500 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
688
+ | T015 | [2016-11-02 Wed] | P | 7.7500 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
689
+ | T016 | [2016-11-02 Wed] | P | 8.2500 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
690
+ |------+------------------+------+--------+-----+------+--------+-------+------+--------+--------|
691
+ | T019 | [2017-01-15 Sun] | S | 8.75 | T | F | 300 | 175 | 125 | 0.2453 | 0.1924 |
692
+ | T020 | [2017-01-19 Thu] | S | 8.25 | F | T | 700 | 615 | 85 | 0.2453 | 0.1924 |
693
+ | T021 | [2017-01-23 Mon] | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
694
+ | T021 | [2017-01-23 Mon] | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
695
+ EOS
696
+
697
+ tab1 = FatTable.from_org_string(tab1_str)
698
+ tab2 = FatTable.from_org_string(tab2_str)
699
+ #+END_SRC
700
+
701
+ *** Select
702
+
703
+ With the ~select~ method, you can select which existing columns should appear in
704
+ the output table and create new columns in the output table that are a function
705
+ of existing and new columns.
706
+
707
+ **** Selecting Existing Columns
708
+
709
+ Here we select three existing columns by simply passing header symbols in the
710
+ order we want them to appear in the output. Thus, one use of =select= is to
711
+ filter and permute the order of existing columns. The =select= method preserves
712
+ any group boundaries present in the input table.
713
+
714
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
715
+ #+BEGIN_SRC ruby
716
+ tab1.select(:price, :ref, :shares).to_aoa
717
+ #+END_SRC
718
+
719
+ #+BEGIN_EXAMPLE
720
+ | Price | Ref | Shares |
721
+ |-------+------+--------|
722
+ | 7.7 | T001 | 100 |
723
+ | 7.75 | T002 | 200 |
724
+ | 7.5 | T003 | 800 |
725
+ | 7.5 | T003 | 800 |
726
+ |-------+------+--------|
727
+ | 7.55 | T004 | 6811 |
728
+ | 7.5 | T005 | 4000 |
729
+ | 7.6 | T006 | 1000 |
730
+ | 7.6 | T006 | 1000 |
731
+ | 7.65 | T007 | 200 |
732
+ | 7.65 | T008 | 2771 |
733
+ | 7.6 | T009 | 9550 |
734
+ |-------+------+--------|
735
+ | 7.55 | T010 | 3175 |
736
+ | 7.425 | T011 | 100 |
737
+ | 7.55 | T012 | 4700 |
738
+ | 7.55 | T012 | 4700 |
739
+ | 7.35 | T013 | 53100 |
740
+ |-------+------+--------|
741
+ | 7.45 | T014 | 5847 |
742
+ | 7.75 | T015 | 500 |
743
+ | 8.25 | T016 | 100 |
744
+ #+END_EXAMPLE
745
+
746
+ **** Adding New Columns
747
+
748
+ More interesting is that ~select~ can take hash-like keyword arguments after the
749
+ symbol arguments to create new columns in the output as functions of other
750
+ columns. For each hash-like parameter, the keyword given must be a symbol, which
751
+ becomes the header for the new column, and the value must be either: (1) a
752
+ symbol representing an existing column, which has the effect of renaming an
753
+ existing column, or (2) a string representing a ruby expression for the value of
754
+ a new column.
755
+
756
+ Within the string expression, the names of existing or already-specified columns
757
+ are available as local variables, as well as the instance variables '@row' and
758
+ '@group'. So for our example table, the string expressions for new columns have
759
+ access to local variables ~ref~, ~date~, ~code~, ~price~, ~g10~, ~qp10~,
760
+ ~shares~, ~lp~, ~qp~, ~iplp~, and ~ipqp~ as well as the instance variables
761
+ ~@row~ and ~@group~. The local variables are set to the values of the cell in
762
+ their respective columns for each row in the input table and the instance
763
+ variables are set the number of the current row and group respectively.
764
+
765
+ For example, if we want to rename the ~:date~ column and add a new column to
766
+ compute the cost of shares, we could do the following:
767
+
768
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
769
+ #+BEGIN_SRC ruby
770
+ tab1.select(:ref, :price, :shares, traded_on: :date, cost: 'price * shares').to_aoa
771
+ #+END_SRC
772
+
773
+ #+BEGIN_EXAMPLE
774
+ | Ref | Price | Shares | Traded On | Cost |
775
+ |------+-------+--------+------------+----------|
776
+ | T001 | 7.7 | 100 | 2016-11-01 | 770.0 |
777
+ | T002 | 7.75 | 200 | 2016-11-01 | 1550.0 |
778
+ | T003 | 7.5 | 800 | 2016-11-01 | 6000.0 |
779
+ | T003 | 7.5 | 800 | 2016-11-01 | 6000.0 |
780
+ |------+-------+--------+------------+----------|
781
+ | T004 | 7.55 | 6811 | 2016-11-01 | 51423.05 |
782
+ | T005 | 7.5 | 4000 | 2016-11-01 | 30000.0 |
783
+ | T006 | 7.6 | 1000 | 2016-11-01 | 7600.0 |
784
+ | T006 | 7.6 | 1000 | 2016-11-01 | 7600.0 |
785
+ | T007 | 7.65 | 200 | 2016-11-01 | 1530.0 |
786
+ | T008 | 7.65 | 2771 | 2016-11-01 | 21198.15 |
787
+ | T009 | 7.6 | 9550 | 2016-11-01 | 72580.0 |
788
+ |------+-------+--------+------------+----------|
789
+ | T010 | 7.55 | 3175 | 2016-11-01 | 23971.25 |
790
+ | T011 | 7.425 | 100 | 2016-11-02 | 742.5 |
791
+ | T012 | 7.55 | 4700 | 2016-11-02 | 35485.0 |
792
+ | T012 | 7.55 | 4700 | 2016-11-02 | 35485.0 |
793
+ | T013 | 7.35 | 53100 | 2016-11-02 | 390285.0 |
794
+ |------+-------+--------+------------+----------|
795
+ | T014 | 7.45 | 5847 | 2016-11-02 | 43560.15 |
796
+ | T015 | 7.75 | 500 | 2016-11-02 | 3875.0 |
797
+ | T016 | 8.25 | 100 | 2016-11-02 | 825.0 |
798
+ #+END_EXAMPLE
799
+
800
+ The parameter '~traded_on: :date~' caused the ~:date~ column of the input table
801
+ to be renamed '~:traded_on~, and the parameter ~cost: 'price * shares'~ created
802
+ a new column, ~:cost~, as the product of values in the ~:price~ and ~:shares~
803
+ columns.
804
+
805
+ The order of the columns in the result tables is the same as the order of the
806
+ parameters to the ~select~ method. So, you can re-order the columns with a
807
+ second, chained call to ~select~:
808
+
809
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
810
+ #+BEGIN_SRC ruby
811
+ tab1.select(:ref, :price, :shares, traded_on: :date, cost: 'price * shares') \
812
+ .select(:ref, :traded_on, :price, :shares, :cost) \
813
+ .to_aoa
814
+ #+END_SRC
815
+
816
+ #+BEGIN_EXAMPLE
817
+ | Ref | Traded On | Price | Shares | Cost |
818
+ |------+------------+-------+--------+----------|
819
+ | T001 | 2016-11-01 | 7.7 | 100 | 770.0 |
820
+ | T002 | 2016-11-01 | 7.75 | 200 | 1550.0 |
821
+ | T003 | 2016-11-01 | 7.5 | 800 | 6000.0 |
822
+ | T003 | 2016-11-01 | 7.5 | 800 | 6000.0 |
823
+ |------+------------+-------+--------+----------|
824
+ | T004 | 2016-11-01 | 7.55 | 6811 | 51423.05 |
825
+ | T005 | 2016-11-01 | 7.5 | 4000 | 30000.0 |
826
+ | T006 | 2016-11-01 | 7.6 | 1000 | 7600.0 |
827
+ | T006 | 2016-11-01 | 7.6 | 1000 | 7600.0 |
828
+ | T007 | 2016-11-01 | 7.65 | 200 | 1530.0 |
829
+ | T008 | 2016-11-01 | 7.65 | 2771 | 21198.15 |
830
+ | T009 | 2016-11-01 | 7.6 | 9550 | 72580.0 |
831
+ |------+------------+-------+--------+----------|
832
+ | T010 | 2016-11-01 | 7.55 | 3175 | 23971.25 |
833
+ | T011 | 2016-11-02 | 7.425 | 100 | 742.5 |
834
+ | T012 | 2016-11-02 | 7.55 | 4700 | 35485.0 |
835
+ | T012 | 2016-11-02 | 7.55 | 4700 | 35485.0 |
836
+ | T013 | 2016-11-02 | 7.35 | 53100 | 390285.0 |
837
+ |------+------------+-------+--------+----------|
838
+ | T014 | 2016-11-02 | 7.45 | 5847 | 43560.15 |
839
+ | T015 | 2016-11-02 | 7.75 | 500 | 3875.0 |
840
+ | T016 | 2016-11-02 | 8.25 | 100 | 825.0 |
841
+ #+END_EXAMPLE
842
+
843
+ **** Custom Instance Variables and Hooks
844
+
845
+ As the above examples demonstrate, the instance variables ~@row~ and ~@group~
846
+ are available when evaluating expressions that add new columns. You can also set
847
+ up your own instance variables as well for keeping track of things that cross
848
+ row boundaries, such as running sums.
849
+
850
+ To declare instance variables, you can use the ~ivars:~ hash parameter to
851
+ ~select~. Each key of the hash becomes an instance variable and each value
852
+ becomes its initial value before any rows are evaluated.
853
+
854
+ In addition, you can provide ~before_hook:~ and ~after_hook:~ parameters to
855
+ ~select~ as strings that are evaluated as ruby expressions before and after each
856
+ row is processed. You can use these to update instance variables. The values set
857
+ in the ~before_hook:~ can be used in expressions for adding new columns by
858
+ referencing them with the '@' prefix.
859
+
860
+ For example, suppose we wanted to not only add a cost column, but a column that
861
+ shows the cumulative cost after each transaction in our example table. The
862
+ following example uses the ~ivars:~ and ~before_hook:~ parameters to keep track
863
+ of the running cost of shares, then formats the table.
864
+
865
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
866
+ #+BEGIN_SRC ruby
867
+ tab = tab1.select(:ref, :price, :shares, traded_on: :date, \
868
+ cost: 'price * shares', cumulative: '@total_cost', \
869
+ ivars: { total_cost: 0 }, \
870
+ before_hook: '@total_cost += price * shares')
871
+ FatTable.to_aoa(tab) do |f|
872
+ f.format(price: '0.4', shares: '0.0,', cost: '0.2,', cumulative: '0.2,')
873
+ end
874
+ #+END_SRC
875
+
876
+ #+BEGIN_EXAMPLE
877
+ | Ref | Price | Shares | Traded On | Cost | Cumulative |
878
+ |------+--------+--------+------------+------------+------------|
879
+ | T001 | 7.7000 | 100 | 2016-11-01 | 770.00 | 770.00 |
880
+ | T002 | 7.7500 | 200 | 2016-11-01 | 1,550.00 | 2,320.00 |
881
+ | T003 | 7.5000 | 800 | 2016-11-01 | 6,000.00 | 8,320.00 |
882
+ | T003 | 7.5000 | 800 | 2016-11-01 | 6,000.00 | 14,320.00 |
883
+ |------+--------+--------+------------+------------+------------|
884
+ | T004 | 7.5500 | 6,811 | 2016-11-01 | 51,423.05 | 65,743.05 |
885
+ | T005 | 7.5000 | 4,000 | 2016-11-01 | 30,000.00 | 95,743.05 |
886
+ | T006 | 7.6000 | 1,000 | 2016-11-01 | 7,600.00 | 103,343.05 |
887
+ | T006 | 7.6000 | 1,000 | 2016-11-01 | 7,600.00 | 110,943.05 |
888
+ | T007 | 7.6500 | 200 | 2016-11-01 | 1,530.00 | 112,473.05 |
889
+ | T008 | 7.6500 | 2,771 | 2016-11-01 | 21,198.15 | 133,671.20 |
890
+ | T009 | 7.6000 | 9,550 | 2016-11-01 | 72,580.00 | 206,251.20 |
891
+ |------+--------+--------+------------+------------+------------|
892
+ | T010 | 7.5500 | 3,175 | 2016-11-01 | 23,971.25 | 230,222.45 |
893
+ | T011 | 7.4250 | 100 | 2016-11-02 | 742.50 | 230,964.95 |
894
+ | T012 | 7.5500 | 4,700 | 2016-11-02 | 35,485.00 | 266,449.95 |
895
+ | T012 | 7.5500 | 4,700 | 2016-11-02 | 35,485.00 | 301,934.95 |
896
+ | T013 | 7.3500 | 53,100 | 2016-11-02 | 390,285.00 | 692,219.95 |
897
+ |------+--------+--------+------------+------------+------------|
898
+ | T014 | 7.4500 | 5,847 | 2016-11-02 | 43,560.15 | 735,780.10 |
899
+ | T015 | 7.7500 | 500 | 2016-11-02 | 3,875.00 | 739,655.10 |
900
+ | T016 | 8.2500 | 100 | 2016-11-02 | 825.00 | 740,480.10 |
901
+ #+END_EXAMPLE
902
+
903
+ **** Argument Order and Boundaries
904
+
905
+ Notice that ~select~ can take any number of arguments but all the symbol
906
+ arguments must come first followed by all the hash-like keyword arguments,
907
+ including the special arguments for instance variables and hooks.
908
+
909
+ As the example illustrates, ~.select~ transmits any group boundaries in its
910
+ input table to the result table.
911
+
912
+ *** Where
913
+
914
+ You can filter the rows of the result table with the ~.where~ method. It takes a
915
+ single string expression as an argument which is evaluated in a manner similar
916
+ to ~.select~ in which the value of the cells in each column are available as
917
+ local variables and the instance variables ~@row~ and ~@group~ are available for
918
+ testing. The expression is evaluated for each row, and if the expression
919
+ evaluates to a truthy value, the row is included in the output, otherwise it is
920
+ not. The ~.where~ method obliterates any group boundaries in the input, so the
921
+ output table has only a single group.
922
+
923
+ Here we select only those even-numbered rows where either of the two boolean
924
+ fields is true:
925
+
926
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
927
+ #+BEGIN_SRC ruby
928
+ tab1.where('@row.even? && (g10 || qp10)') \
929
+ .to_aoa
930
+ #+END_SRC
931
+
932
+ #+BEGIN_EXAMPLE
933
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
934
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
935
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
936
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
937
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
938
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
939
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
940
+ #+END_EXAMPLE
941
+
942
+ *** Order_by
943
+
944
+ You can sort a table on any number of columns with ~order_by~. The ~order_by~
945
+ method takes any number of symbol arguments for the columns to sort on. If you
946
+ specify more than one column, the sort is performed on the first column, then
947
+ all columns that are equal with respect to the first column are sorted by the
948
+ second column, and so on. Ordering is done is ascending order for each of the
949
+ columns, but can be reversed by adding a '!' to the end a symbol argument.
950
+ All columns of the input table are included in the output.
951
+
952
+ Let's sort our table first by ~:code~, then in reverse order of ~:date~.
953
+
954
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
955
+ #+BEGIN_SRC ruby
956
+ tab1.order_by(:code, :date!) \
957
+ .to_aoa
958
+ #+END_SRC
959
+
960
+ #+begin_EXAMPLE
961
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
962
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
963
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
964
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
965
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
966
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
967
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
968
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
969
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
970
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
971
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
972
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
973
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
974
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
975
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
976
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
977
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
978
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
979
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
980
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
981
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
982
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
983
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
984
+ #+end_EXAMPLE
985
+
986
+ The interesting thing about ~order_by~ is that, while it ignores groups in its
987
+ input, it adds group boundaries in the output table at those rows where the sort
988
+ keys change. Thus, in each group, ~:code~ and ~:date~ are the same, and when
989
+ either changes, ~order_by~ inserts a group boundary.
990
+
991
+ *** Group_by
992
+
993
+ Like ~order_by~, ~group_by~ takes a set of parameters of column header symbols,
994
+ the "grouping parameters", by which to sort the table into a set of groups that
995
+ are equal with respect to values in those columns. In addition, those parameters
996
+ can be followed by a series of hash-like parameters, the "aggregating
997
+ parameters", that indicate how any of the remaining, non-group columns are to be
998
+ aggregated into a single value. The output table has one row for each group for
999
+ which the grouping parameters are equal containing those columns and an
1000
+ aggregate column for each of the aggregating parameters.
1001
+
1002
+ For example, let's summarize the ~trades~ table by ~:code~ and ~:price~ again,
1003
+ and determine total shares, average price, and a few other features of each
1004
+ group:
1005
+
1006
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1007
+ #+BEGIN_SRC ruby
1008
+ tab1.group_by(:code, :date, price: :avg,
1009
+ shares: :sum, lp: :sum, qp: :sum,
1010
+ qp10: :all?) \
1011
+ .to_aoa { |f| f.format(avg_price: '0.5R') }
1012
+ #+END_SRC
1013
+
1014
+ #+BEGIN_EXAMPLE
1015
+ | Code | Date | Avg Price | Sum Shares | Sum Lp | Sum Qp | All QP10 |
1016
+ |------+------------+-----------+------------+--------+--------+----------|
1017
+ | P | 2016-11-01 | 7.60714 | 17396 | 2473 | 14923 | F |
1018
+ | P | 2016-11-02 | 7.61786 | 69047 | 9945 | 59102 | F |
1019
+ | S | 2016-11-01 | 7.58000 | 13011 | 1852 | 11159 | F |
1020
+ #+END_EXAMPLE
1021
+
1022
+ After the grouping column parameters, ~:code~ and ~:date~, there are several
1023
+ hash-like "aggregating" parameters where the key is the column to aggregate and
1024
+ the value is a symbol for one of several aggregating methods that
1025
+ ~FatTable::Column~ objects understand. For example, the ~:avg~ method is applied
1026
+ to the :price column so that the output shows the average price in each group.
1027
+ The ~:shares~, ~:lp~, and ~:qp~ columns are summed, and the ~:any?~ aggregate is
1028
+ applied to one of the boolean fields, that is, it is ~true~ if any of the values
1029
+ in that column are ~true~. The column names in the output of the aggregated
1030
+ columns have the name of the aggregating method pre-pended to the column name.
1031
+
1032
+ Here is a list of all the aggregate methods available. If the description
1033
+ restricts the aggregate to particular column types, applying it to other types
1034
+ will raise an exception.
1035
+
1036
+ - ~first~ :: the first non-nil item in the column,
1037
+ - ~last~ :: the last non-nil item in the column,
1038
+ - ~rng~ :: form a string of the form ~"#{first}..#{last}"~ to show the range of
1039
+ values in the column,
1040
+ - ~sum~ :: for ~Numeric~ and ~String~ columns, apply '+' to all the non-nil
1041
+ values,
1042
+ - ~count~ :: the number of non-nil values in the column,
1043
+ - ~min~ :: for ~Numeric~, ~String~, and ~DateTime~ columns, return the smallest
1044
+ non-nil value in the column,
1045
+ - ~max~ :: for ~Numeric~, ~String~, and ~DateTime~ columns, return the largest
1046
+ non-nil value in the column,
1047
+ - ~avg~ :: for ~Numeric~ and ~DateTime~ columns, return the arithmetic mean of
1048
+ the non-nil values in the column; with respect to ~Date~ or ~DateTime~
1049
+ objects, each is converted to a numeric Julian date, the average is
1050
+ calculated, and the result converted back to a ~Date~ or ~DateTime~ object,
1051
+ - ~var~ :: for ~Numeric~ and ~DateTime~ columns, compute the sample variance of
1052
+ the non-nil values in the column, dates are converted to Julian date
1053
+ numbers as for the ~:avg~ aggregate,
1054
+ - ~pvar~ :: for ~Numeric~ and ~DateTime~ columns, compute the population
1055
+ variance of the non-nil values in the column, dates are converted to Julian
1056
+ date numbers as for the ~:avg~ aggregate,
1057
+ - ~dev~ :: for ~Numeric~ and ~DateTime~ columns, compute the sample standard
1058
+ deviation of the non-nil values in the column, dates are converted to
1059
+ Julian date numbers as for the ~:avg~ aggregate,
1060
+ - ~pdev~ :: for ~Numeric~ and ~DateTime~ columns, compute the population
1061
+ standard deviation of the non-nil values in the column, dates are converted
1062
+ to numbers as for the ~:avg~ aggregate,
1063
+ - ~all?~ :: for ~Boolean~ columns only, return true if all of the non-nil values
1064
+ in the column are true,
1065
+ - ~any?~ :: for ~Boolean~ columns only, return true if any non-nil value in the
1066
+ column is true,
1067
+ - ~none?~ :: for ~Boolean~ columns only, return true if no non-nil value in the
1068
+ column is true,
1069
+ - ~one?~ :: for ~Boolean~ columns only, return true if exactly one non-nil value
1070
+ in the column is true,
1071
+
1072
+ Perhaps surprisingly, the ~group_by~ method ignores any groups in its input and
1073
+ results in no group boundaries in the output since each group formed by the
1074
+ implicit ~order_by~ on the grouping columns is collapsed into a single row.
1075
+
1076
+ *** Join
1077
+ **** Join Types
1078
+
1079
+ So far, all the operations have operated on a single table. ~FatTable~ provides
1080
+ several ~join~ methods for combining two tables, each of which takes as
1081
+ parameters (1) a second table and (2) except in the case of ~cross_join~, zero
1082
+ or more "join expressions". In the descriptions below, ~T1~ is the table on
1083
+ which the method is called, ~T2~ is the table supplied as the first parameter
1084
+ ~other~, and ~R1~ and ~R2~ are rows in their respective tables being considered
1085
+ for inclusion in the joined output table.
1086
+
1087
+ - ~join(other, *jexps)~ :: Performs an "inner join" on the tables. For each row
1088
+ ~R1~ of ~T1~, the joined table has a row for each row in ~T2~ that
1089
+ satisfies the join condition with ~R1~.
1090
+
1091
+ - ~left_join(other, *jexps)~ :: First, an inner join is performed. Then, for
1092
+ each row in ~T1~ that does not satisfy the join condition with any row in
1093
+ ~T2~, a joined row is added with null values in columns of ~T2~. Thus, the
1094
+ joined table always has at least one row for each row in ~T1~.
1095
+
1096
+ - ~right_join(other, *jexps)~ :: First, an inner join is performed. Then, for
1097
+ each row in ~T2~ that does not satisfy the join condition with any row in
1098
+ ~T1~, a joined row is added with null values in columns of ~T1~. This is
1099
+ the converse of a left join: the result table will always have a row for
1100
+ each row in ~T2~.
1101
+
1102
+ - ~full_join(other, *jexps)~ :: First, an inner join is performed. Then, for
1103
+ each row in ~T1~ that does not satisfy the join condition with any row in
1104
+ ~T2~, a joined row is added with null values in columns of ~T2~. Also, for
1105
+ each row of ~T2~ that does not satisfy the join condition with any row in
1106
+ ~T1~, a joined row with null values in the columns of ~T1~ is added.
1107
+
1108
+ - ~cross_join(other)~ :: For every possible combination of rows from ~T1~ and
1109
+ ~T2~ (i.e., a Cartesian product), the joined table will contain a row
1110
+ consisting of all columns in ~T1~ followed by all columns in ~T2~. If the
1111
+ tables have ~N~ and ~M~ rows respectively, the joined table will have ~N *
1112
+ M~ rows.
1113
+
1114
+ **** Join Expressions
1115
+
1116
+ For each of the join types, if no join expressions are given, the tables will be
1117
+ joined on columns having the same column header in both tables, and the join
1118
+ condition is satisfied when all the values in those columns are equal. If the
1119
+ join type is an inner join, this is a so-called "natural" join.
1120
+
1121
+ If the join expressions are one or more symbols, the join condition requires
1122
+ that the values of both tables are equal for all columns named by the symbols. A
1123
+ column that appears in both tables can be given without modification and will be
1124
+ assumed to require equality on that column. If an unmodified symbol is not a
1125
+ name that appears in both tables, an exception will be raised. Column names that
1126
+ are unique to the first table must have a ~_a~ appended to the column name and
1127
+ column names that are unique to the other table must have a ~_b~ appended to the
1128
+ column name. These disambiguated column names must come in pairs, one for the
1129
+ first table and one for the second, and they will imply a join condition that
1130
+ the columns must be equal on those columns. Several such symbol expressions will
1131
+ require that all such implied pairs are equal in order for the join condition to
1132
+ be met.
1133
+
1134
+ Finally, a join expression can be a string that contains an arbitrary ruby
1135
+ expression that will be evaluated for truthiness. Within the string, /all/
1136
+ column names must be disambiguated with the ~_a~ or ~_b~ modifiers whether they
1137
+ are common to both tables or not. As with ~select~ and ~where~ methods, the
1138
+ names of the columns in both tables (albeit disambiguated) are available as
1139
+ local variables within the expression, but the instance variables ~@row~ and
1140
+ ~@group~ are not.
1141
+
1142
+ **** Join Examples
1143
+
1144
+ The following examples are taken from the [[https://www.tutorialspoint.com/postgresql/postgresql_using_joins.htm][Postgresql tutorial]], with some slight
1145
+ modifications. The examples will use the following two tables, which are also
1146
+ available in ~ft_console~ as ~@tab_a~ and ~@tab_b~:
1147
+
1148
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1149
+ #+BEGIN_SRC ruby
1150
+ require 'fat_table'
1151
+
1152
+ tab_a_str = <<-EOS
1153
+ | Id | Name | Age | Address | Salary | Join Date |
1154
+ |----+-------+-----+------------+--------+------------|
1155
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 |
1156
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
1157
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
1158
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 |
1159
+ | 2 | Allen | 25 | Texas | | 2005-07-13 |
1160
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
1161
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 |
1162
+ | 10 | James | 45 | Texas | 5000 | |
1163
+ EOS
1164
+
1165
+ tab_b_str = <<-EOS
1166
+ | Id | Dept | Emp Id |
1167
+ |----+-------------+--------|
1168
+ | 1 | IT Billing | 1 |
1169
+ | 2 | Engineering | 2 |
1170
+ | 3 | Finance | 7 |
1171
+ EOS
1172
+
1173
+ tab_a = FatTable.from_org_string(tab_a_str)
1174
+ tab_b = FatTable.from_org_string(tab_b_str)
1175
+ #+END_SRC
1176
+
1177
+ ***** Inner Joins
1178
+
1179
+ With no join expression arguments, the tables are joined when their sole common
1180
+ field, ~:id~, is equal in both tables. The result is the natural join of the
1181
+ two tables.
1182
+
1183
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1184
+ #+BEGIN_SRC ruby
1185
+ tab_a.join(tab_b).to_aoa
1186
+ #+END_SRC
1187
+
1188
+ #+BEGIN_EXAMPLE
1189
+ | Id | Name | Age | Address | Salary | Join Date | Dept | Emp Id |
1190
+ |----+-------+-----+------------+--------+------------+-------------+--------|
1191
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | IT Billing | 1 |
1192
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 | Finance | 7 |
1193
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | Engineering | 2 |
1194
+ #+END_EXAMPLE
1195
+
1196
+ But the natural join joined employee IDs in the first table and department IDs
1197
+ in the second table. To correct this, we need to explicitly state the columns we
1198
+ want to join on in each table by disambiguating them with ~_a~ and ~_b~
1199
+ suffixes:
1200
+
1201
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1202
+ #+BEGIN_SRC ruby
1203
+ tab_a.join(tab_b, :id_a, :emp_id_b).to_aoa
1204
+ #+END_SRC
1205
+
1206
+ #+BEGIN_EXAMPLE
1207
+ | Id | Name | Age | Address | Salary | Join Date | Id B | Dept |
1208
+ |----+-------+-----+------------+--------+------------+------+-------------|
1209
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 1 | IT Billing |
1210
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 2 | Engineering |
1211
+ #+END_EXAMPLE
1212
+
1213
+ Instead of using the disambiguated column names as symbols, we could also use a
1214
+ string containing a ruby expression. Within the expression, the column names
1215
+ should be treated as local variables:
1216
+
1217
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1218
+ #+BEGIN_SRC ruby
1219
+ tab_a.join(tab_b, 'id_a == emp_id_b').to_aoa
1220
+ #+END_SRC
1221
+
1222
+ #+BEGIN_EXAMPLE
1223
+ | Id | Name | Age | Address | Salary | Join Date | Id B | Dept | Emp Id |
1224
+ |----+-------+-----+------------+--------+------------+------+-------------+--------|
1225
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 1 | IT Billing | 1 |
1226
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 2 | Engineering | 2 |
1227
+ #+END_EXAMPLE
1228
+
1229
+ ***** Left and Right Joins
1230
+
1231
+ In left join, all the rows of ~tab_a~ are included in the output, augmented by
1232
+ the matching columns of ~tab_b~ and augmented with nils where there is no match:
1233
+
1234
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1235
+ #+BEGIN_SRC ruby
1236
+ tab_a.left_join(tab_b, 'id_a == emp_id_b').to_aoa
1237
+ #+END_SRC
1238
+
1239
+ #+BEGIN_EXAMPLE
1240
+ | Id | Name | Age | Address | Salary | Join Date | Id B | Dept | Emp Id |
1241
+ |----+-------+-----+------------+--------+------------+------+-------------+--------|
1242
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 1 | IT Billing | 1 |
1243
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 | | | |
1244
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 | | | |
1245
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 | | | |
1246
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 2 | Engineering | 2 |
1247
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 | | | |
1248
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 | | | |
1249
+ | 10 | James | 45 | Texas | 5000 | | | | |
1250
+ #+END_EXAMPLE
1251
+
1252
+ In a right join, all the rows of ~tab_b~ are included in the output, augmented
1253
+ by the matching columns of ~tab_a~ and augmented with nils where there is no
1254
+ match:
1255
+
1256
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1257
+ #+BEGIN_SRC ruby
1258
+ tab_a.right_join(tab_b, 'id_a == emp_id_b').to_aoa
1259
+ #+END_SRC
1260
+
1261
+ #+BEGIN_EXAMPLE
1262
+ | Id | Name | Age | Address | Salary | Join Date | Id B | Dept | Emp Id |
1263
+ |----+-------+-----+------------+--------+------------+------+-------------+--------|
1264
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 1 | IT Billing | 1 |
1265
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 2 | Engineering | 2 |
1266
+ | | | | | | | 3 | Finance | 7 |
1267
+ #+END_EXAMPLE
1268
+
1269
+ ***** Full Join
1270
+
1271
+ A full join combines the effects of a left join and a right join. All the rows
1272
+ from both tables are included in the output augmented by columns of the other
1273
+ table where the join expression is satisfied and augmented with nils otherwise.
1274
+
1275
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1276
+ #+BEGIN_SRC ruby
1277
+ tab_a.full_join(tab_b, 'id_a == emp_id_b').to_aoa
1278
+ #+END_SRC
1279
+
1280
+ #+BEGIN_EXAMPLE
1281
+ | Id | Name | Age | Address | Salary | Join Date | Id B | Dept | Emp Id |
1282
+ |----+-------+-----+------------+--------+------------+------+-------------+--------|
1283
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 1 | IT Billing | 1 |
1284
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 | | | |
1285
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 | | | |
1286
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 | | | |
1287
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 2 | Engineering | 2 |
1288
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 | | | |
1289
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 | | | |
1290
+ | 10 | James | 45 | Texas | 5000 | | | | |
1291
+ | | | | | | | 3 | Finance | 7 |
1292
+ #+END_EXAMPLE
1293
+
1294
+ ***** Cross Join
1295
+
1296
+ Finally, a cross join outputs every row of ~tab_a~ augmented with every row of
1297
+ ~tab_b~, in other words, the Cartesian product of the two tables. If ~tab_a~ has
1298
+ ~N~ rows and ~tab_b~ has ~M~ rows, the output table will have ~N * M~ rows.
1299
+
1300
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1301
+ #+BEGIN_SRC ruby
1302
+ tab_a.cross_join(tab_b).to_aoa
1303
+ #+END_SRC
1304
+
1305
+ #+BEGIN_EXAMPLE
1306
+ | Id | Name | Age | Address | Salary | Join Date | Id B | Dept | Emp Id |
1307
+ |----+-------+-----+------------+--------+------------+------+-------------+--------|
1308
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 1 | IT Billing | 1 |
1309
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 2 | Engineering | 2 |
1310
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 | 3 | Finance | 7 |
1311
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 | 1 | IT Billing | 1 |
1312
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 | 2 | Engineering | 2 |
1313
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 | 3 | Finance | 7 |
1314
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 | 1 | IT Billing | 1 |
1315
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 | 2 | Engineering | 2 |
1316
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 | 3 | Finance | 7 |
1317
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 | 1 | IT Billing | 1 |
1318
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 | 2 | Engineering | 2 |
1319
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 | 3 | Finance | 7 |
1320
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 1 | IT Billing | 1 |
1321
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 2 | Engineering | 2 |
1322
+ | 2 | Allen | 25 | Texas | | 2005-07-13 | 3 | Finance | 7 |
1323
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 | 1 | IT Billing | 1 |
1324
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 | 2 | Engineering | 2 |
1325
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 | 3 | Finance | 7 |
1326
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 | 1 | IT Billing | 1 |
1327
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 | 2 | Engineering | 2 |
1328
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 | 3 | Finance | 7 |
1329
+ | 10 | James | 45 | Texas | 5000 | | 1 | IT Billing | 1 |
1330
+ | 10 | James | 45 | Texas | 5000 | | 2 | Engineering | 2 |
1331
+ | 10 | James | 45 | Texas | 5000 | | 3 | Finance | 7 |
1332
+ #+END_EXAMPLE
1333
+
1334
+ *** Set Operations
1335
+
1336
+ ~FatTable~ can perform several set operations on tables. In order for two tables
1337
+ to be used this way, they must have the same number of columns with the same
1338
+ types or an exception will be raised. We'll call two tables that qualify for
1339
+ combining with set operations "set-compatible."
1340
+
1341
+ We'll use the following two set-compatible tables in the examples. They each
1342
+ have some duplicates and some group boundaries so you can see the effect of the
1343
+ set operations on duplicates and groups.
1344
+
1345
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1346
+ #+BEGIN_SRC ruby
1347
+ tab1.to_aoa
1348
+ #+END_SRC
1349
+
1350
+ #+BEGIN_EXAMPLE
1351
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1352
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1353
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1354
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1355
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1356
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1357
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1358
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1359
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1360
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1361
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1362
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1363
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1364
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1365
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1366
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1367
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1368
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1369
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1370
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1371
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1372
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1373
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1374
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1375
+ #+END_EXAMPLE
1376
+
1377
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1378
+ #+BEGIN_SRC ruby
1379
+ tab2.to_aoa
1380
+ #+END_SRC
1381
+
1382
+ #+BEGIN_EXAMPLE
1383
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1384
+ |------+------------+------+-------+-----+------+--------+-------+------+--------+--------|
1385
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1386
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1387
+ | T017 | 2016-11-01 | P | 8.3 | F | T | 1801 | 1201 | 600 | 0.2453 | 0.1924 |
1388
+ |------+------------+------+-------+-----+------+--------+-------+------+--------+--------|
1389
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1390
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1391
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1392
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1393
+ |------+------------+------+-------+-----+------+--------+-------+------+--------+--------|
1394
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1395
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1396
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1397
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1398
+ |------+------------+------+-------+-----+------+--------+-------+------+--------+--------|
1399
+ | T019 | 2017-01-15 | S | 8.75 | T | F | 300 | 175 | 125 | 0.2453 | 0.1924 |
1400
+ | T020 | 2017-01-19 | S | 8.25 | F | T | 700 | 615 | 85 | 0.2453 | 0.1924 |
1401
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1402
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1403
+ #+END_EXAMPLE
1404
+
1405
+ **** Unions
1406
+
1407
+ Two tables that are set-compatible can be combined with the ~union~ or
1408
+ ~union_all~ methods so that the rows of both tables appear in the output. In the
1409
+ output table, the headers of the receiver table are used. You can use ~select~
1410
+ to change or re-order the headers if you prefer. The ~union~ method eliminates
1411
+ duplicate rows in the result table, the ~union_all~ method does not.
1412
+
1413
+ Any group boundaries in the input tables are destroyed by ~union~ but are
1414
+ preserved by ~union_all~. In addition, ~union_all~ (but not ~union~) adds a
1415
+ group boundary between the rows of the two input tables.
1416
+
1417
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1418
+ #+BEGIN_SRC ruby
1419
+ tab1.union(tab2).to_aoa
1420
+ #+END_SRC
1421
+
1422
+ #+BEGIN_EXAMPLE
1423
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1424
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1425
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1426
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1427
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1428
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1429
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1430
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1431
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1432
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1433
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1434
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1435
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1436
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1437
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1438
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1439
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1440
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1441
+ | T017 | 2016-11-01 | P | 8.3 | F | T | 1801 | 1201 | 600 | 0.2453 | 0.1924 |
1442
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1443
+ | T019 | 2017-01-15 | S | 8.75 | T | F | 300 | 175 | 125 | 0.2453 | 0.1924 |
1444
+ | T020 | 2017-01-19 | S | 8.25 | F | T | 700 | 615 | 85 | 0.2453 | 0.1924 |
1445
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1446
+ #+END_EXAMPLE
1447
+
1448
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1449
+ #+BEGIN_SRC ruby
1450
+ tab1.union_all(tab2).to_aoa
1451
+ #+END_SRC
1452
+
1453
+ #+BEGIN_EXAMPLE
1454
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1455
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1456
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1457
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1458
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1459
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1460
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1461
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1462
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1463
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1464
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1465
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1466
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1467
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1468
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1469
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1470
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1471
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1472
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1473
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1474
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1475
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1476
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1477
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1478
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1479
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1480
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1481
+ | T017 | 2016-11-01 | P | 8.3 | F | T | 1801 | 1201 | 600 | 0.2453 | 0.1924 |
1482
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1483
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1484
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1485
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1486
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1487
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1488
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1489
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1490
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1491
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1492
+ |------+------------+------+-------+-----+------+--------+-------+-------+--------+--------|
1493
+ | T019 | 2017-01-15 | S | 8.75 | T | F | 300 | 175 | 125 | 0.2453 | 0.1924 |
1494
+ | T020 | 2017-01-19 | S | 8.25 | F | T | 700 | 615 | 85 | 0.2453 | 0.1924 |
1495
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1496
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1497
+ #+END_EXAMPLE
1498
+
1499
+ **** Intersections
1500
+
1501
+ The ~intersect~ method returns a table having only rows common to both tables,
1502
+ eliminating any duplicate rows in the result.
1503
+
1504
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1505
+ #+BEGIN_SRC ruby
1506
+ tab1.intersect(tab2).to_aoa
1507
+ #+END_SRC
1508
+
1509
+ #+BEGIN_EXAMPLE
1510
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1511
+ |------+------------+------+-------+-----+------+--------+-----+------+--------+--------|
1512
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1513
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1514
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1515
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1516
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1517
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1518
+ #+END_EXAMPLE
1519
+
1520
+ With ~intersect_all~, all the rows of the first table, including duplicates, are
1521
+ included in the result if they also occur in the second table. However,
1522
+ duplicates in the second table do not appear.
1523
+
1524
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1525
+ #+BEGIN_SRC ruby
1526
+ tab1.intersect_all(tab2).to_aoa
1527
+ #+END_SRC
1528
+
1529
+ #+BEGIN_EXAMPLE
1530
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1531
+ |------+------------+------+-------+-----+------+--------+-----+------+--------+--------|
1532
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1533
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1534
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1535
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1536
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1537
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1538
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1539
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1540
+ #+END_EXAMPLE
1541
+
1542
+ As a result, it makes a difference which table is the receiver of the
1543
+ ~intersect_all~ method call and which is the argument. In other words, order of
1544
+ operation matters.
1545
+
1546
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1547
+ #+BEGIN_SRC ruby
1548
+ tab2.intersect_all(tab1).to_aoa
1549
+ #+END_SRC
1550
+
1551
+ #+BEGIN_EXAMPLE
1552
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1553
+ |------+------------+------+-------+-----+------+--------+-----+------+--------+--------|
1554
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1555
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1556
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1557
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1558
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1559
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1560
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1561
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1562
+ #+END_EXAMPLE
1563
+
1564
+ **** Differences with Except
1565
+
1566
+ You can use the ~except~ method to delete from a table any rows that occur in
1567
+ another table, that is, compute the set difference between the tables.
1568
+
1569
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1570
+ #+BEGIN_SRC ruby
1571
+ tab1.except(tab2).to_aoa
1572
+ #+END_SRC
1573
+
1574
+ #+BEGIN_EXAMPLE
1575
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1576
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1577
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1578
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1579
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1580
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1581
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1582
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1583
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1584
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1585
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1586
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1587
+ #+END_EXAMPLE
1588
+
1589
+ Like subtraction, though, the order of operands matters with set difference
1590
+ computed by ~except~.
1591
+
1592
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1593
+ #+BEGIN_SRC ruby
1594
+ tab2.except(tab1).to_aoa
1595
+ #+END_SRC
1596
+
1597
+ #+BEGIN_EXAMPLE
1598
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1599
+ |------+------------+------+-------+-----+------+--------+-------+------+--------+--------|
1600
+ | T017 | 2016-11-01 | P | 8.3 | F | T | 1801 | 1201 | 600 | 0.2453 | 0.1924 |
1601
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1602
+ | T019 | 2017-01-15 | S | 8.75 | T | F | 300 | 175 | 125 | 0.2453 | 0.1924 |
1603
+ | T020 | 2017-01-19 | S | 8.25 | F | T | 700 | 615 | 85 | 0.2453 | 0.1924 |
1604
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1605
+ #+END_EXAMPLE
1606
+
1607
+ As with ~intersect_all~, ~except_all~ includes any duplicates in the first,
1608
+ receiver table, but not those in the second, argument table.
1609
+
1610
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1611
+ #+BEGIN_SRC ruby
1612
+ tab1.except_all(tab2).to_aoa
1613
+ #+END_SRC
1614
+
1615
+ #+BEGIN_EXAMPLE
1616
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1617
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1618
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1619
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1620
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1621
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1622
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1623
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1624
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1625
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1626
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1627
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1628
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1629
+ #+END_EXAMPLE
1630
+
1631
+ And, of course, the order of operands matters here as well.
1632
+
1633
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1634
+ #+BEGIN_SRC ruby
1635
+ tab2.except_all(tab1).to_aoa
1636
+ #+END_SRC
1637
+
1638
+ #+BEGIN_EXAMPLE
1639
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1640
+ |------+------------+------+-------+-----+------+--------+-------+------+--------+--------|
1641
+ | T017 | 2016-11-01 | P | 8.3 | F | T | 1801 | 1201 | 600 | 0.2453 | 0.1924 |
1642
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1643
+ | T018 | 2016-11-01 | S | 7.152 | T | F | 2516 | 2400 | 116 | 0.2453 | 0.1924 |
1644
+ | T019 | 2017-01-15 | S | 8.75 | T | F | 300 | 175 | 125 | 0.2453 | 0.1924 |
1645
+ | T020 | 2017-01-19 | S | 8.25 | F | T | 700 | 615 | 85 | 0.2453 | 0.1924 |
1646
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1647
+ | T021 | 2017-01-23 | P | 7.16 | T | T | 12100 | 11050 | 1050 | 0.2453 | 0.1924 |
1648
+ #+END_EXAMPLE
1649
+
1650
+ *** Uniq (aka Distinct)
1651
+
1652
+ The ~uniq~ method takes no arguments and simply removes any duplicate rows from
1653
+ the input table. The ~distinct~ method is an alias for ~uniq~. Any groups in
1654
+ the input table are lost.
1655
+
1656
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1657
+ #+BEGIN_SRC ruby
1658
+ tab1.uniq.to_aoa
1659
+ #+END_SRC
1660
+
1661
+ #+BEGIN_EXAMPLE
1662
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1663
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1664
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1665
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1666
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1667
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1668
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1669
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1670
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1671
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1672
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1673
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1674
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1675
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1676
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1677
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1678
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1679
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1680
+ #+END_EXAMPLE
1681
+
1682
+ *** Remove groups with degroup!
1683
+
1684
+ Finally, it is sometimes helpful to remove any group boundaries from a table.
1685
+ You can do this with ~.degroup!~, which is the only operation that mutates its
1686
+ receiver table by removing its groups.
1687
+
1688
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1689
+ #+BEGIN_SRC ruby
1690
+ tab1.degroup!.to_aoa
1691
+ #+END_SRC
1692
+
1693
+ #+BEGIN_EXAMPLE
1694
+ | Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
1695
+ |------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
1696
+ | T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1697
+ | T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1698
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1699
+ | T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
1700
+ | T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
1701
+ | T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
1702
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1703
+ | T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
1704
+ | T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
1705
+ | T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
1706
+ | T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
1707
+ | T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
1708
+ | T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
1709
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1710
+ | T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
1711
+ | T013 | 2016-11-02 | P | 7.35 | T | T | 53100 | 7656 | 45444 | 0.2453 | 0.1924 |
1712
+ | T014 | 2016-11-02 | P | 7.45 | F | T | 5847 | 835 | 5012 | 0.2453 | 0.1924 |
1713
+ | T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
1714
+ | T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
1715
+ #+END_EXAMPLE
1716
+
1717
+ ** Formatting Tables
1718
+
1719
+ Besides creating and operating on tables, you may want to display the resulting
1720
+ table. ~FatTable~ seeks to provide a set of formatting directives that are the
1721
+ most common across many output media. It provides directives for alignment, for
1722
+ color, for adding currency symbols and grouping commas to numbers, for padding
1723
+ numbers, and for formatting dates and booleans.
1724
+
1725
+ In addition, you can add any number of footers to a table, which appear at the
1726
+ end of the table, and any number of group footers, which appear after each group
1727
+ in the table. These can be formatted independently of the table body.
1728
+
1729
+ If the target output medium does not support a formatting directive or the
1730
+ directive does not make sense, it is simply ignored. For example, you can output
1731
+ an ~org-mode~ table as a String, and since ~org-mode~ does not support colors,
1732
+ any color directives are ignored. Some of the output targets are not strings,
1733
+ but ruby data structures, and for them, things such as alignment are irrelevant.
1734
+
1735
+ *** Available Formatters
1736
+
1737
+ ~FatTable~ supports the following output targets for its tables:
1738
+
1739
+ - Text :: form the table with ACSII characters,
1740
+ - Org :: form the table with ASCII characters but in the form used by Emacs
1741
+ org-mode for constructing tables,
1742
+ - Term :: form the table with ANSI terminal codes and unicode characters,
1743
+ possibly including colored text and cell backgrounds,
1744
+ - LaTeX :: form the table as input for LaTeX's longtable environment,
1745
+ - Aoh :: output the table as a ruby data structure, building the table as an
1746
+ array of hashes, and
1747
+ - Aoa :: output the table as a ruby data structure, building the table as an
1748
+ array of array,
1749
+
1750
+ These are all implemented by classes that inherit from ~FatTable::Formatter~
1751
+ class by defining about a dozen methods that get called at various places during
1752
+ the construction of the output table. The idea is that more classes can be
1753
+ defined by adding additional classes.
1754
+
1755
+ *** Table Locations
1756
+
1757
+ In the formatting methods, the table is divided into several "locations" for
1758
+ which separate formatting directives may be given. These locations are
1759
+ identified with the following symbols:
1760
+
1761
+ - :header :: the first row of the output table containing the headers,
1762
+ - :footer :: all rows of the table's footers,
1763
+ - :gfooter :: all rows of the table's group footers,
1764
+ - :body :: all the data rows of the table, that is, those that are neither part
1765
+ of the header, footers, or gfooters,
1766
+ - :bfirst :: the first row of the table's body, and
1767
+ - :gfirst :: the first row in each group in the table's body.
1768
+
1769
+ *** Formatting Directives
1770
+
1771
+ The formatting methods explained in the next section all take formatting
1772
+ directives as strings in which letters and other characters signify what
1773
+ formatting applies. For example, we may apply the formatting directive ~'R,$'~
1774
+ to numbers in a certain part of the table. Each of those characters, and in
1775
+ some cases a whole substring, is a single directive. They can appear in any
1776
+ order, so ~'$R,'~ and ~',$R'~ are equivalent.
1777
+
1778
+ Here is a list of all the formatting directives that apply to each cell type:
1779
+
1780
+ **** String
1781
+
1782
+ For a string element, the following instructions are valid. Note that these can
1783
+ also be applied to all the other cell types as well since they are all converted
1784
+ to a string in forming the output.
1785
+
1786
+ - u :: convert the element to all lowercase,
1787
+ - U :: convert the element to all uppercase,
1788
+ - t :: title case the element, that is, upcase the initial letter in
1789
+ each word and lower case the other letters
1790
+ - B ~B :: make the element bold, or turn off bold
1791
+ - I ~I :: make the element italic, or turn off italic
1792
+ - R :: align the element on the right of the column
1793
+ - L :: align the element on the left of the column
1794
+ - C :: align the element in the center of the column
1795
+ - c[color] :: render the element in the given color; the color can have
1796
+ the form fgcolor, fgcolor.bgcolor, or .bgcolor, to set the
1797
+ foreground or background colors respectively, and each of those can
1798
+ be an ANSI or X11 color name in addition to the special color,
1799
+ 'none', which keeps the terminal's default color.
1800
+ - _ ~_ :: underline the element, or turn off underline
1801
+ - * ~* :: cause the element to blink, or turn off blink
1802
+
1803
+ For example, the directive ~'tCc[red.yellow]'~ would title-case the element,
1804
+ center it, and color it red on a yellow background. The directives that are
1805
+ boolean have negating forms so that, for example, if bold is turned on for all
1806
+ columns of a given type, it can be countermanded in formatting directives for
1807
+ particular columns.
1808
+
1809
+ **** Numeric
1810
+
1811
+ For a numeric element, all the instructions valid for string are available, in
1812
+ addition to the following:
1813
+
1814
+ - , ~, :: insert grouping commas, or do not insert grouping commas,
1815
+ - $ ~$ :: format the number as currency according to the locale, or not,
1816
+ - m.n :: include at least m digits before the decimal point, padding on
1817
+ the left with zeroes as needed, and round the number to the n
1818
+ decimal places and include n digits after the decimal point,
1819
+ padding on the right with zeroes as needed,
1820
+ - H :: convert the number (assumed to be in units of seconds) to ~HH:MM:SS.ss~
1821
+ form. So a column that is the result of subtracting two :datetime forms
1822
+ will result in a :numeric expressed as seconds and can be displayed in
1823
+ hours, minutes, and seconds with this formatting instruction.
1824
+
1825
+ For example, the directive ~'R5.0c[blue]'~ would right-align the numeric
1826
+ element, pad it on the left with zeros, and color it blue.
1827
+
1828
+ **** DateTime
1829
+
1830
+ For a ~DateTime~, all the instructions valid for string are available, in
1831
+ addition to the following:
1832
+
1833
+ - d[fmt] :: apply the format to a ~Date~ or a ~DateTime~ that is a whole day,
1834
+ that is that has no or zero hour, minute, and second components, where fmt
1835
+ is a valid format string for ~Date#strftime~, otherwise, the datetime will
1836
+ be formatted as an ISO 8601 string, YYYY-MM-DD.
1837
+ - D[fmt] :: apply the format to a datetime that has at least a non-zero hour
1838
+ component where fmt is a valid format string for Date#strftime, otherwise,
1839
+ the datetime will be formatted as an ISO 8601 string, YYYY-MM-DD.
1840
+
1841
+ For example, ~'c[pink]d[%b %-d, %Y]C'~, would format a date element like 'Sep
1842
+ 22, 1957', center it, and color it pink.
1843
+
1844
+ **** Boolean
1845
+
1846
+ For a boolean cell, all the instructions valid for string are available, in
1847
+ addition to the following:
1848
+
1849
+ - Y :: print true as '~Y~' and false as '~N~',
1850
+ - T :: print true as '~T~' and false as '~F~',
1851
+ - X :: print true as '~X~' and false as an empty string '',
1852
+ - b[xxx,yyy] :: print true as the string given as ~xxx~ and false as the string
1853
+ given as ~yyy~,
1854
+ - c[tcolor,fcolor] :: color a true element with ~tcolor~ and a false element
1855
+ with ~fcolor~. Each of the colors may be specified in the same manner as
1856
+ colors for strings described above.
1857
+
1858
+ For example, the directive '~b[Yeppers,Nope]c[green.pink,red.pink]~' would
1859
+ render a true boolean as '~Yeppers~' colored green on pink and render a false
1860
+ boolean as '~Nope~' colored red on pink. See [[https://www.youtube.com/watch?v=oLdFFD8II8U][Yeppers]] for additional information.
1861
+
1862
+ **** NilClass
1863
+
1864
+ By default, ~nil~ elements are rendered as blank cells, but you can make them
1865
+ visible with the following, and in that case, all the formatting instructions
1866
+ valid for strings are also available:
1867
+
1868
+ - n[niltext] :: render a ~nil~ item with the given niltext.
1869
+
1870
+ For example, you might want to use ~'n[-]Cc[purple]'~ to make nils visible as a
1871
+ centered purple hyphen.
1872
+
1873
+ *** Footers Methods
1874
+
1875
+ You can call the ~footer~ and ~gfooter~ methods on ~Formatter~ objects to add
1876
+ footers and group footers. Their signatures are:
1877
+
1878
+ - ~footer(label, *sum_cols, **agg_cols)~ :: where ~label~ is a label to be
1879
+ placed in the first cell of the footer (unless that column is named as one
1880
+ of the ~sum_cols~ or ~agg_cols~, in which case the label is ignored),
1881
+ ~*sum_cols~ are zero or more symbols for columns to be summed, and
1882
+ ~**agg_cols~ is zero or more hash-like parameters with a column symbol as a
1883
+ key and a symbol for an aggregate method as the value. This causes a
1884
+ table-wide header to be added at the bottom of the table applying the
1885
+ ~:sum~ aggregate to the ~sum_cols~ and the named aggregate method to the
1886
+ ~agg_cols~. A table can have any number of footers attached, and they will
1887
+ appear at the bottom of the output table in the order they are given.
1888
+
1889
+ - ~gfooter(label, *sum_cols, **agg_cols)~ :: where the parameters have the same
1890
+ meaning as for the ~footer~ method, but result in a footer for each group
1891
+ in the table rather than the table as a whole. These will appear in the
1892
+ output table just below each group.
1893
+
1894
+ There are also a number of convenience methods for adding common footers:
1895
+
1896
+ - ~sum_footer(*cols)~ :: Add a footer summing the given columns with the label
1897
+ 'Total'.
1898
+ - ~sum_gfooter(*cols)~ :: Add a group footer summing the given columns with the
1899
+ label 'Group Total'.
1900
+ - ~avg_footer(*cols)~ :: Add a footer averaging the given columns with the label
1901
+ 'Average'.
1902
+ - ~avg_gfooter(*cols)~ :: Add a group footer averaging the given columns with the label
1903
+ 'Group Average'.
1904
+ - ~min_footer(*cols)~ :: Add a footer showing the minimum for the given columns
1905
+ with the label 'Minimum'.
1906
+ - ~min_gfooter(*cols)~ :: Add a group footer showing the minumum for the given
1907
+ columns with the label 'Group Minimum'.
1908
+ - ~max_footer(*cols)~ :: Add a footer showing the maximum for the given columns
1909
+ with the label 'Maximum'.
1910
+ - ~max_gfooter(*cols)~ :: Add a group footer showing the maximum for the given
1911
+ columns with the label 'Group Maximum'.
1912
+
1913
+ *** Formatting Methods
1914
+
1915
+ You can call methods on ~Formatter~ objects to specify formatting directives
1916
+ for specific columns or types. There are two methods for doing so, ~format_for~
1917
+ and ~format~.
1918
+
1919
+ **** Instantiating a Formatter
1920
+
1921
+ There are several ways to invoke the formatting methods on a table. First, you
1922
+ can instantiate a ~XXXFormatter~ object and feed it a table as a parameter.
1923
+ There is a Formatter subclass for each target output medium, for example,
1924
+ ~AoaFormatter~ will produce a ruby array of arrays. You can then call the
1925
+ ~output~ method on the ~XXXFormatter~.
1926
+
1927
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1928
+ #+BEGIN_SRC ruby
1929
+ FatTable::AoaFormatter.new(tab_a).output
1930
+ #+END_SRC
1931
+
1932
+ #+BEGIN_EXAMPLE
1933
+ | Id | Name | Age | Address | Salary | Join Date |
1934
+ |----+-------+-----+------------+--------+------------|
1935
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 |
1936
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
1937
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
1938
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 |
1939
+ | 2 | Allen | 25 | Texas | | 2005-07-13 |
1940
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
1941
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 |
1942
+ | 10 | James | 45 | Texas | 5000 | |
1943
+ #+END_EXAMPLE
1944
+
1945
+ The ~XXXFormatter.new~ method yields the new instance to any block given, and
1946
+ you can call methods on it to affect the formatting of the output:
1947
+
1948
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1949
+ #+BEGIN_SRC ruby
1950
+ FatTable::AoaFormatter.new(tab_a) do |f|
1951
+ f.format(numeric: '0.0,R', id: '3.0C')
1952
+ end.output
1953
+ #+END_SRC
1954
+
1955
+ #+BEGIN_EXAMPLE
1956
+ | Id | Name | Age | Address | Salary | Join Date |
1957
+ |-----+-------+-----+------------+--------+------------|
1958
+ | 001 | Paul | 32 | California | 20,000 | 2001-07-13 |
1959
+ | 003 | Teddy | 23 | Norway | 20,000 | 2007-12-13 |
1960
+ | 004 | Mark | 25 | Rich-Mond | 65,000 | 2007-12-13 |
1961
+ | 005 | David | 27 | Texas | 85,000 | 2007-12-13 |
1962
+ | 002 | Allen | 25 | Texas | | 2005-07-13 |
1963
+ | 008 | Paul | 24 | Houston | 20,000 | 2005-07-13 |
1964
+ | 009 | James | 44 | Norway | 5,000 | 2005-07-13 |
1965
+ | 010 | James | 45 | Texas | 5,000 | |
1966
+ #+END_EXAMPLE
1967
+
1968
+ **** ~FatTable~ module-level method calls
1969
+
1970
+ The ~FatTable~ module provides a set of methods of the form ~to_aoa~, ~to_text~,
1971
+ etc., to access a ~Formatter~ without having to create an instance yourself.
1972
+ Without a block, they apply the default formatting to the table and call the
1973
+ ~.output~ method automatically:
1974
+
1975
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1976
+ #+BEGIN_SRC ruby
1977
+ FatTable.to_aoa(tab_a)
1978
+ #+END_SRC
1979
+
1980
+ #+BEGIN_EXAMPLE
1981
+ | Id | Name | Age | Address | Salary | Join Date |
1982
+ |----+-------+-----+------------+--------+------------|
1983
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 |
1984
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
1985
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
1986
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 |
1987
+ | 2 | Allen | 25 | Texas | | 2005-07-13 |
1988
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
1989
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 |
1990
+ | 10 | James | 45 | Texas | 5000 | |
1991
+ #+END_EXAMPLE
1992
+
1993
+ With a block, these methods yield a ~Formatter~ instance on which you can call
1994
+ formatting and footer methods. The ~.output~ method is called on the ~Formatter~
1995
+ automatically after the block:
1996
+
1997
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
1998
+ #+BEGIN_SRC ruby
1999
+ FatTable.to_aoa(tab_a) do |f|
2000
+ f.format(numeric: '0.0,R', id: '3.0C')
2001
+ end
2002
+ #+END_SRC
2003
+
2004
+ #+BEGIN_EXAMPLE
2005
+ | Id | Name | Age | Address | Salary | Join Date |
2006
+ |-----+-------+-----+------------+--------+------------|
2007
+ | 001 | Paul | 32 | California | 20,000 | 2001-07-13 |
2008
+ | 003 | Teddy | 23 | Norway | 20,000 | 2007-12-13 |
2009
+ | 004 | Mark | 25 | Rich-Mond | 65,000 | 2007-12-13 |
2010
+ | 005 | David | 27 | Texas | 85,000 | 2007-12-13 |
2011
+ | 002 | Allen | 25 | Texas | | 2005-07-13 |
2012
+ | 008 | Paul | 24 | Houston | 20,000 | 2005-07-13 |
2013
+ | 009 | James | 44 | Norway | 5,000 | 2005-07-13 |
2014
+ | 010 | James | 45 | Texas | 5,000 | |
2015
+ #+END_EXAMPLE
2016
+
2017
+ **** Calling methods on Table objects
2018
+
2019
+ Finally, you can call methods such as ~to_aoa~, ~to_text~, etc., directly on a
2020
+ Table:
2021
+
2022
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
2023
+ #+BEGIN_SRC ruby
2024
+ tab_a.to_aoa
2025
+ #+END_SRC
2026
+
2027
+ #+BEGIN_EXAMPLE
2028
+ | Id | Name | Age | Address | Salary | Join Date |
2029
+ |----+-------+-----+------------+--------+------------|
2030
+ | 1 | Paul | 32 | California | 20000 | 2001-07-13 |
2031
+ | 3 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
2032
+ | 4 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
2033
+ | 5 | David | 27 | Texas | 85000 | 2007-12-13 |
2034
+ | 2 | Allen | 25 | Texas | | 2005-07-13 |
2035
+ | 8 | Paul | 24 | Houston | 20000 | 2005-07-13 |
2036
+ | 9 | James | 44 | Norway | 5000 | 2005-07-13 |
2037
+ | 10 | James | 45 | Texas | 5000 | |
2038
+ #+END_EXAMPLE
2039
+
2040
+ And you can supply a block to them as well to specify formatting or footers:
2041
+
2042
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
2043
+ #+BEGIN_SRC ruby
2044
+ tab_a.to_aoa do |f|
2045
+ f.format(numeric: '0.0,R', id: '3.0C')
2046
+ f.sum_footer(:salary, :age)
2047
+ end
2048
+ #+END_SRC
2049
+
2050
+ #+BEGIN_EXAMPLE
2051
+ | Id | Name | Age | Address | Salary | Join Date |
2052
+ |-------+-------+-----+------------+---------+------------|
2053
+ | 001 | Paul | 32 | California | 20,000 | 2001-07-13 |
2054
+ | 003 | Teddy | 23 | Norway | 20,000 | 2007-12-13 |
2055
+ | 004 | Mark | 25 | Rich-Mond | 65,000 | 2007-12-13 |
2056
+ | 005 | David | 27 | Texas | 85,000 | 2007-12-13 |
2057
+ | 002 | Allen | 25 | Texas | | 2005-07-13 |
2058
+ | 008 | Paul | 24 | Houston | 20,000 | 2005-07-13 |
2059
+ | 009 | James | 44 | Norway | 5,000 | 2005-07-13 |
2060
+ | 010 | James | 45 | Texas | 5,000 | |
2061
+ |-------+-------+-----+------------+---------+------------|
2062
+ | Total | | 245 | | 220,000 | |
2063
+ #+END_EXAMPLE
2064
+
2065
+ *** The ~format~ and ~format_for~ methods
2066
+
2067
+ Formatters take only two kinds of methods, those that attach footers to a
2068
+ table, which are discussed in the next section, and those that specify
2069
+ formatting for table cells, which are the subject of this section.
2070
+
2071
+ To set formatting directives for all locations in a table at once, use the
2072
+ ~format~ method; to set formatting directives for a particular location in the
2073
+ table, use the ~format_for~ method, giving the location as the first parameter.
2074
+
2075
+ Other than that first parameter, the two methods take the same types of
2076
+ parameters. The remaining parameters are hash-like parameters that use either a
2077
+ column name or a type as the key and a string with the formatting directives to
2078
+ apply as the value. The following example says to set the formatting for all
2079
+ locations in the table and to format all numeric fields as strings that are
2080
+ rounded to whole numbers (the '0.0' part), that are right-aligned (the 'R'
2081
+ part), and have grouping commas inserted (the ',' part). But the ~:id~ column is
2082
+ numeric, and the second parameter overrides the formatting for numerics in
2083
+ general and calls for the ~:id~ column to be padded to three digits with zeros
2084
+ on the left (the '3.0' part) and to be centered (the 'C' part).
2085
+
2086
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
2087
+ #+BEGIN_SRC ruby
2088
+ tab_a.to_aoa do |f|
2089
+ f.format(numeric: '0.0,R', id: '3.0C')
2090
+ end
2091
+ #+END_SRC
2092
+
2093
+ #+BEGIN_EXAMPLE
2094
+ | Id | Name | Age | Address | Salary | Join Date |
2095
+ |-----+-------+-----+------------+--------+------------|
2096
+ | 001 | Paul | 32 | California | 20,000 | 2001-07-13 |
2097
+ | 003 | Teddy | 23 | Norway | 20,000 | 2007-12-13 |
2098
+ | 004 | Mark | 25 | Rich-Mond | 65,000 | 2007-12-13 |
2099
+ | 005 | David | 27 | Texas | 85,000 | 2007-12-13 |
2100
+ | 002 | Allen | 25 | Texas | | 2005-07-13 |
2101
+ | 008 | Paul | 24 | Houston | 20,000 | 2005-07-13 |
2102
+ | 009 | James | 44 | Norway | 5,000 | 2005-07-13 |
2103
+ | 010 | James | 45 | Texas | 5,000 | |
2104
+ #+END_EXAMPLE
2105
+
2106
+ The ~numeric:~ directive affected the ~:age~ and ~:salary~ columns and the ~id:~
2107
+ directive affected only the ~:id~ column. All the other cells in the table had
2108
+ the default formatting applied.
2109
+
2110
+ **** Location priority
2111
+
2112
+ Formatting for any given cell depends on its location in the table. The
2113
+ ~format_for~ method takes a location to which its formatting directive are
2114
+ restricted as the first argument. It can be one of the following:
2115
+
2116
+ - ~:header~ :: directive apply only to the header row, that is the first row, of
2117
+ the output table,
2118
+
2119
+ - ~:footer~ :: directives apply to all the footer rows of the output table,
2120
+ regardless of how many there are,
2121
+
2122
+ - ~gfooter~ :: directives apply to all group footer rows of the output tables,
2123
+ regardless of how many there are,
2124
+
2125
+ - ~:body~ :: directives apply to all rows in the body of the table unless the
2126
+ row is the first row in the table or in a group and separate directives for
2127
+ those have been given, in which case those directives apply,
2128
+
2129
+ - ~:gfirst~ :: directives apply to the first row in each group in the body of
2130
+ the table, unless the row is also the first row in the table as a whole, in
2131
+ which case the ~:bfirst~ directives apply,
2132
+
2133
+ - ~:bfirst~ :: directives apply to the first row in the body of the table.
2134
+
2135
+ If you give directives for ~:body~, they are copied to ~:bfirst~ and ~:gfirst~
2136
+ as well and can be overridden by directives for those locations.
2137
+
2138
+ Directives given to the ~format~ method apply the directives to all locations in
2139
+ the table, but they can be overridden by more specific directives given in a
2140
+ ~format_for~ directive.
2141
+
2142
+ **** Type and Column priority
2143
+
2144
+ A directive based on type applies to all columns having that type unless
2145
+ overridden by a directive specific to a named column; a directive based on a
2146
+ column name applies only to cells in that column.
2147
+
2148
+ However, there is a twist. Since the end result of formatting is to convert all
2149
+ columns to strings, the formatting directives for the ~:string~ type applies to
2150
+ all columns. Likewise, since all columns may contain nils, the ~nil:~ type
2151
+ applies to nils in all columns regardless of the column's type.
2152
+
2153
+ #+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
2154
+ #+BEGIN_SRC ruby
2155
+ require 'fat_table'
2156
+ tab_a.to_text do |f|
2157
+ f.format(string: 'R', id: '3.0C', salary: 'n[N/A]')
2158
+ end
2159
+ #+END_SRC
2160
+
2161
+ #+BEGIN_EXAMPLE
2162
+ +=====+=======+=====+============+========+============+
2163
+ | Id | Name | Age | Address | Salary | Join Date |
2164
+ +-----+-------+-----+------------+--------+------------+
2165
+ | 001 | Paul | 32 | California | 20000 | 2001-07-13 |
2166
+ | 003 | Teddy | 23 | Norway | 20000 | 2007-12-13 |
2167
+ | 004 | Mark | 25 | Rich-Mond | 65000 | 2007-12-13 |
2168
+ | 005 | David | 27 | Texas | 85000 | 2007-12-13 |
2169
+ | 002 | Allen | 25 | Texas | N/A | 2005-07-13 |
2170
+ | 008 | Paul | 24 | Houston | 20000 | 2005-07-13 |
2171
+ | 009 | James | 44 | Norway | 5000 | 2005-07-13 |
2172
+ | 010 | James | 45 | Texas | 5000 | |
2173
+ +=====+=======+=====+============+========+============+
2174
+ #+END_EXAMPLE
2175
+
2176
+ The ~string: 'R'~ directive causes all the cells to be right-aligned except
2177
+ ~:id~ which specifies centering for the ~:id~ column only. The ~n[N/A]~
2178
+ directive for specifies how nil are displayed in the numeric column, ~:salary~,
2179
+ but not for other nils, such as in the last row of the ~:join_date~ column.
2180
+
2181
+ * Development
2182
+
2183
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
2184
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
2185
+ prompt that will allow you to experiment.
2186
+
2187
+ To install this gem onto your local machine, run `bundle exec rake install`. To
2188
+ release a new version, update the version number in `version.rb`, and then run
2189
+ `bundle exec rake release`, which will create a git tag for the version, push
2190
+ git commits and tags, and push the `.gem` file to
2191
+ [rubygems.org](https://rubygems.org).
2192
+
2193
+ * Contributing
2194
+
2195
+ Bug reports and pull requests are welcome on GitHub at
2196
+ https://github.com/ddoherty03/fat_table.