Index Lists, Grade Lists, and some simple joins

Principal Topics

{ (from) {. (take) , (append) ,. (stitch) ,: (laminate) ; (link) /: (grade up) # (tally, copy) >: (increment) β: (rank conjunction) β: (format) >. (larger of) merging lists, row and column headings, row and column proportions

The Index List

Every time a list is created, several other lists are automatically created whether the user realises it or not. The most obvious is the index-list i.@# . Next are the grade up and grade down lists which are permutations of the index-list which give the indices necessary to arrange the original list in either ascending or descending order. Go one step further and obtain the index-list of the result of appending two lists, and you have the basis for merging lists as the next section shows.

Merging lists with alternate items

1 2 3 4 merged with 5 6 is to become 1 5 2 6 3 4. This illustrates well the concept of fork.

`   malt=.mselect { ,`

that is start by joining the two lists as they stand (1 2 3 4 5 6), and then find the right selection list in order to put the items of the joined list in the required order. A momentβs thought shows that the required selection list is going to be 0 0 1 1 2 2 β¦

Every list has an index list given by

```   ilist=.i.@#
ilist 'abcde'
0 1 2 3 4```

Joining the index lists of the lists to be merged is a first step along the road

```   ijoin=.,& ilist
1 2 3 ijoin 4 5
0 1 2 0 1```

The positions of the items in this list in ascending order are provided by grade up, so

```   mselect=./:@ijoin
1 2 3 mselect 4 5
0 3 1 4 2```

and so finally

```   1 2 3 4 malt 5 6
1 5 2 6 3 4```

Other merge patterns are now easy to improvise, for example if two from the left are to be taken for every one from the right adjust two of the verbs

```   ijoin21=.dyad : '(2#ilist x),(ilist y)'
mselect21=./:@ijoin21
malt21=.dyad : '(x mselect21 y){ (2#x),y'
1 2 3 malt21 7 8
1 1 7 2 2 8 3 3```

The remaining sections deal with joining lists to tables.

Readers will be familiar with automatically adjusted headings generated in Excel and other spreadsheets. The algorithms for doing this can be conveniently analysed in J, and illustrate when it is appropriate to join and when to stitch when working with lists of lists. Start with some random data, a list or row headings and a list of column headings

```   ]a=.?3 4\$100
90 47 58 29
22 32 55  5
55 73 58 50
rh=.'London';'Paris';'Dublin'
ch=.'North';'South';'East';'West'```

The items in a can be boxed in a neat turn of phrase :

```      <&>a        NB. otherwise <each a
ββββ¬βββ¬βββ¬βββ
β90β47β58β29β
ββββΌβββΌβββΌβββ€
β22β32β55β5 β
ββββΌβββΌβββΌβββ€
β55β73β58β50β
ββββ΄βββ΄βββ΄βββ```

However to merge column headings with the data it is necessary to do some counting of character spaces as well as determining how many decimal places are to be displayed in the data. Assume a field width of 6 and 1 decimal place, and column headers can be dealt with by

```   ]b=.6j1":each a
ββββββββ¬βββββββ¬βββββββ¬βββββββ
β  90.0β  47.0β  58.0β  29.0β
ββββββββΌβββββββΌβββββββΌβββββββ€
β  22.0β  32.0β  55.0β   5.0β
ββββββββΌβββββββΌβββββββΌβββββββ€
β  55.0β  73.0β  58.0β  50.0β
ββββββββ΄βββββββ΄βββββββ΄βββββββ
Ch=._6{.each ch
Ch,b            NB. Column headers are merged
ββββββββ¬βββββββ¬βββββββ¬βββββββ
β Northβ Southβ  Eastβ  Westβ
ββββββββΌβββββββΌβββββββΌβββββββ€
β  90.0β  47.0β  58.0β  29.0β
ββββββββΌβββββββΌβββββββΌβββββββ€
β  22.0β  32.0β  55.0β   5.0β
ββββββββΌβββββββΌβββββββΌβββββββ€
β  55.0β  73.0β  58.0β  50.0β
ββββββββ΄βββββββ΄βββββββ΄βββββββ```

The row headers also require some enhancement to deal with the space in the North-west corner :

```   Rh=.' ';rh
Rh,.Ch,b            NB. Row headers are stitched (,.)
ββββββββ¬βββββββ¬βββββββ¬βββββββ¬βββββββ
β      β Northβ Southβ  Eastβ  Westβ
ββββββββΌβββββββΌβββββββΌβββββββΌβββββββ€
βLondonβ  90.0β  47.0β  58.0β  29.0β
ββββββββΌβββββββΌβββββββΌβββββββΌβββββββ€
βParis β  22.0β  32.0β  55.0β   5.0β
ββββββββΌβββββββΌβββββββΌβββββββΌβββββββ€
βDublinβ  55.0β  73.0β  58.0β  50.0β
ββββββββ΄βββββββ΄βββββββ΄βββββββ΄βββββββ```

Mission accomplished! A generalization of this process is to compute the maximum width required for a single column header (assuming that this will be no greater than the required header width), allowing for a space (>:), and use this to determine the file width in the data,

```   eqlise=.monad : '->:>./>#every y'
Ch=.(eqlise ch){.every ch
b=.<&>((-eqlise ch)j.1)":each a```

Rh,.Ch,b then gives the same result as above.

Some variations might be to eliminate the vertical grid lines within the data :

```    Rh,.<"1(,>Ch),6j1":a
ββββββββ¬βββββββββββββββββββββββββ
β      β North South  East  Westβ
ββββββββΌβββββββββββββββββββββββββ€
βLondonβ  90.0  47.0  58.0  29.0β
ββββββββΌβββββββββββββββββββββββββ€
βParis β  22.0  32.0  55.0   5.0β
ββββββββΌβββββββββββββββββββββββββ€
βDublinβ  55.0  73.0  58.0  50.0β
ββββββββ΄βββββββββββββββββββββββββ```

Alternatively for a display without grid lines

```     (>Rh),.(,>Ch),6j1":a
North South  East  West
London  90.0  47.0  58.0  29.0
Paris   22.0  32.0  55.0   5.0
Dublin  55.0  73.0  58.0  50.0```

In summary to decorate a basic table a with grids and row and column headers requires equalisation for the column headers and augmentation for the row headers leading to the definition of Rh,.Ch,b .

Row and Column Totals

Appending row and column totals to a table :

```   a=.2 3\$1 7 4 5 2 0
colsum=., +/
rowsum=.,. +/"1
colsum a
1 7 4
5 2 0
6 9 4
rowsum a
1 7 4 12
5 2 0  7
totsum a
1 7 4 12
5 2 0  7
6 9 4 19```

To dress this with nice grid lines, proceed as follows :

```   f=.5j1&":            NB. decide width and dec pl
Rs=.f,.+/"1 a        NB. formatted rows
Cs=.f+/a             NB. formatted columns
Ts=.f+/,a            NB. formatted total
((<f a),.<Rs),:<every Cs;Ts
βββββββββββββββββ¬ββββββ
β  1.0  7.0  5.0β 13.0β
β  5.0  2.0  0.0β  7.0β
βββββββββββββββββΌββββββ€
β  6.0  9.0  5.0β 20.0β
βββββββββββββββββ΄ββββββ```

all of which can be brought together in a user-defined verb :

```   ttable=.dyad :0
f=.x&":             NB. x is (width)j(dec places)
Rs=.f,.+/"1 y       NB. formatted rows
Cs=.f+/y            NB. formatted columns
Ts=.f+/,y           NB. formatted total
((<f y),.<Rs),:<every Cs;Ts
)
5j1 ttable a
βββββββββββββββββ¬ββββββ
β  1.0  7.0  5.0β 13.0β
β  5.0  2.0  0.0β  7.0β
βββββββββββββββββΌββββββ€
β  6.0  9.0  5.0β 20.0β
βββββββββββββββββ΄ββββββ```

If row and column proportions are wanted :

```   colpro=.%"1 +/
rowpro=.% +/"1
colpro a
0.166667 0.777778 1
0.833333 0.222222 0
rowpro a
0.0833333 0.583333 0.333333
0.714286 0.285714        0```

which might be clearer in rational (extended precision) notation :

```   b=.x: a
colpro b
1r6 7r9 1
5r6 2r9 0
rowpro b
1r12 7r12 1r3
5r7  2r7   0```

Code Summary

```malt=: mselect { ,                      NB. merge alternate
mselect=: /:@ijoin
ijoin=: ,& ilist
ilist=: i.@#
malt21=: dyad : '(x mselect21 y){ (2#x),y'  NB. 2/1 merge
mselect21=: /:@ijoin21
ijoin21=: dyad : '(2#ilist x),(ilist y)'

eqlise=: monad : '->:>./>#every y'      NB. equalise column widths
colsum=: , +/
rowsum=: ,. +/"1
totsum=: colsum@rowsum

NB. Adding row and column totals
ttable=: dyad :0                        NB.table with row & col totals
f=. x&":                                NB. x is (width)j(dec places)
Rs=. f,.+/"1 y                          NB. formatted rows
Cs=. f+/y                               NB. formatted columns
Ts=. f+/,y                              NB. formatted total
((<f y),.<Rs),:<every Cs;Ts
)

NB. Row and column proportions
colpro=: %"1 +/                         NB. column proportions
rowpro=: % +/"1                         NB. row proportions```