Skip to contents

Following the TDA User’s Manual

In memoriam Götz Rohwer

„So, wir fangen jetzt an.“

TDA, and this package

TDA – Transition Data Analysis – is Götz Rohwer’s program for the statistical analysis of event histories: transition rate models, parametric and semi-parametric, with episode splitting, competing risks, multi-episode data and time-dependent covariates, and around them the rest of a working statistician’s toolkit – descriptive statistics, life tables and product-limit estimation, regression of every ordinary kind, sequence analysis, loglinear models, graphs, spatial data, and its own PostScript plotting. It is a command-file program: a text file of commands, a data file, and a listing of what it computed.

Its history, as the manual’s preface and TDA’s history.txt give it: begun in 1989 with early support from the Hamburger Institut für Sozialforschung; grown inside Hans-Peter Blossfeld’s project Household Dynamics and Social Inequality at the European University Institute in Florence, with European Commission support, and then at the University of Bremen. Version 5.7 (June 1994) was the first the authors called mature, and it is the program behind Blossfeld and Rohwer’s Techniques of Event History Modeling (1995; 2nd ed. 2002). The 6.x line was a redesign, and from version 6.2 the program and the manual have two authors, Rohwer and Ulrich Pötter. The 6.x releases, 6.1 in 1997 to 6.4p in March 2009, fall in Rohwer’s years as professor at Ruhr-Universität Bochum (1997–2013), where TDA’s homepage still is. TDA has always been a non-commercial project under the GNU General Public License; its source is C, and the User’s Manual of some 800 pages is its documentation.

tdaR is that program, compiled into an R package. The C is TDA’s, made re-entrant so that it can run inside R and be called more than once; nothing in it has been rewritten, and where it has been changed the change is a repair of a demonstrated fault, listed in the package’s doc/changes-from-tda.md. Each R function writes the command file TDA would have been given, runs it, and returns what TDA produced – the printed listing, the numbers behind it, and the tables – as an R object with the usual methods. The results are TDA’s, at TDA’s precision; the vignette Beyond the Manual shows how that is checked.

This vignette follows the TDA User’s Manual of Götz Rohwer and Ulrich Pötter and shows the same examples written against this package. The section numbers are the manual’s own, so that a page of the manual and the corresponding part of this document can be read side by side, and the data are the manual’s files, shipped with the package under extdata/exam. Boxes hold code and output, as they do there.

Chapter 3 is the data structures, episode, sequence and graph data; chapter 4 TDA’s PostScript plotting; chapter 5 the matrix language and its expression, smoothing and minimisation commands; chapter 6, statistical procedures, is the bulk of the document; chapter 7 is relational data, graphs and clustering, and chapter 8, set-valued data, closes it.

3 Data Structures

3.3.3 An Example Data Set

The manual’s main example data set is taken from the German Life History Study (GLHS), collected at the Max-Planck-Institut für Bildungsforschung in Berlin in 1981–1983 (Mayer and Brückner 1989); the manual thanks Karl Ulrich Mayer and Hans-Peter Blossfeld for providing it, and it is the data set used in Blossfeld and Rohwer, Techniques of Event History Modeling. It holds 600 job episodes from 201 respondents. Each record is one employment episode, and the consecutive jobs of a respondent’s career follow each other in the file.

tda_rrdat() reads rrdat.1, names its twelve columns, and derives the variables the manual’s nvar block derives.

Box 1The data file rrdat.1, read and named

d <- tda_rrdat()
head(d[, 1:12], 9)
  ID NOJ TStart TFin SEX  TI  TB  TE TMAR PRES PRESN EDU
1  1   1    555  982   1 982 351 555  679   34    -1  17
2  2   1    593  638   2 982 357 593  762   22    46  10
3  2   2    639  672   2 982 357 593  762   46    46  10
4  2   3    673  892   2 982 357 593  762   46    -1  10
5  3   1    688  699   2 982 473 688  870   41    41  11
6  3   2    700  729   2 982 473 688  870   41    44  11
7  3   3    730  741   2 982 473 688  870   44    44  11
8  3   4    742  816   2 982 473 688  870   44    44  11
9  3   5    817  828   2 982 473 688  870   44    -1  11

All dates are century months: 1 is January 1900, 13 is January 1901, and so on. TStart and TFin are the first and last month in the job, TI the interview date, TB the date of birth, TE the date of entry into the labour market, and TMAR the date of marriage, zero for the still unmarried. PRES is the prestige score of the current job and PRESN that of the next one, -1 when there is no next job.

Three derived variables carry the episode: TFP is the duration, TFin - TStart + 1; DES is the destination state, 1 for a job change and 0 for an episode still running at the interview; LFX and PNOJ are labour force experience and the number of previous jobs.

Example 1 Where TDA needs an edef command to turn columns into episode data, the episode is named in R by the left hand side of a formula: Surv(TFP, DES) is ts = 0, tf = TFP, org = 0, des = DES. Every command that takes episode data reports what it was given.

Box 2Episode data, single destination state

tda_rate(Surv(TFP, DES) ~ 1, d, model = 2)$episodes
  sn org des episodes weighted mean.duration ts.min tf.max excluded
1  1   0   0      142      142     128.17606      0    428    FALSE
2  1   0   1      458      458      49.30349      0    350    FALSE

458 episodes end in an event, a transition from 0 to 1, with a mean duration of 49 months; 142 are right censored, and their mean of 128 months is no estimate of anything but still says something about the data.

Example 2 The manual’s ed2.cf changes nothing but the destination state, splitting the job changes by the prestige of the next job. tda_rrdat(states = 4) is that data set.

Box 3Episode data, alternative destination states

d4 <- tda_rrdat(states = 4)
tda_rate(Surv(TFP, DES) ~ 1, d4, model = 2)$episodes
  sn org des episodes weighted mean.duration ts.min tf.max excluded
1  1   0   0      142      142     128.17606      0    428    FALSE
2  1   0   1       84       84      45.04762      0    326    FALSE
3  1   0   2      219      219      45.91324      0    350    FALSE
4  1   0   3      155      155      56.40000      0    332    FALSE

84 episodes end in an upward move, 155 in a downward move, and 219 do not change the prestige level. The number of censored episodes has not changed.

ed3.cf defines the same data as multi-episode instead: one episode per job rather than one per person, told apart by the spell number. TDA prints a table per spell, each closed by its own total (the manual’s Box 8).

Box 4ed3.cf: multi-episode data

eb <- suppressWarnings(
    tda_rate(Surv(TFP, DES) ~ 1, d, id = "ID", spell = "NOJ"))
eb$episodes
   sn org des episodes weighted mean.duration ts.min tf.max excluded
1   1   0   0       16       16     233.43750      0    428    FALSE
2   1   0   1      185      185      52.72973      0    326    FALSE
3   2   0   0       36       36     150.16667      0    407    FALSE
4   2   0   1      126      126      52.30159      0    350    FALSE
5   3   0   0       38       38     121.73684      0    329    FALSE
6   3   0   1       69       69      50.36232      0    332    FALSE
7   4   0   0       24       24      85.33334      0    340    FALSE
8   4   0   1       38       38      37.71053      0    146    FALSE
9   5   0   0        9        9     110.22222      0    328    FALSE
10  5   0   1       23       23      22.60870      0     70    FALSE
11  6   0   0        8        8      45.75000      0    127    FALSE
12  6   0   1       12       12      43.75000      0    112    FALSE
13  7   0   0        7        7     114.14286      0    295    FALSE
14  7   0   1        4        4      52.75000      0     96    FALSE
15  8   0   0        3        3      65.00000      0    172    FALSE
16  8   0   1        1        1      72.00000      0     72    FALSE
17  9   0   0        1        1      34.00000      0     34     TRUE
attr(eb$episodes, "sums")   # the totals under each spell; sn = NA is all spells
   sn episodes weighted
1   1      201      201
2   2      162      162
3   3      107      107
4   4       62       62
5   5       32       32
6   6       20       20
7   7       11       11
8   8        4        4
9   9        1        1
10 NA      600      600

3.6.2 Graph Data

A graph is given to TDA as an edge list, gdd = I, J, V: two columns of node numbers and one value per edge, a negative value standing for no edge at all, which is how an isolated node is declared. The manual’s Example 1 has five nodes and four edges, node 9 the isolated one, and its Figure 1 is gd2.cf’s drawing of it as a directed, valued graph.

Box 5gd2.cf

p <- tda_ps(xlim = c(0, 6), ylim = c(0, 4), width = 60, height = 30)
nodes <- data.frame(id = c(1, 7, 3, 4, 9), x = c(1, 3, 5, 2, 4),
                    y = c(3, 3, 3, 1, 1), grey = 0.8)
edges <- data.frame(from = c(1, 3, 1, 4), to = c(7, 7, 4, 7),
                    label = c(10, 0, 5, 13), arrow = "1.5,1.0")
p <- tda_pl_graph(p, nodes, edges)

plot(p)

3.6.5 Drawing Graphs

TDA draws a graph with plg, nodes placed by hand and edges with optional curvature, arrowheads and values; tda_pl_graph() takes the nodes and the edges as data frames. This is the manual’s Figure 3.6.2-2, the graph of gd1.dat drawn by gd3.cf: dashed edges carry the second value variable.

Box 6plg: the graph of gd1.dat (gd3.cf)

p <- tda_ps(xlim = c(0, 9), ylim = c(0, 4), width = 80, height = 40)
nodes <- data.frame(id = c(1, 7, 8, 5, 11, 12, 9),
                    x = c(3, 6, 8, 1, 3, 6, 8),
                    y = c(3, 3, 3, 1, 1, 1, 1), grey = 0.8)
edges <- data.frame(from  = c(1, 1, 7, 8, 11, 5, 7, 8, 12),
                    to    = c(5, 7, 1, 8, 12, 1, 1, 7, 11),
                    curve = c(3, 0, 3, 0, 0, 0, -3, 0, 3),
                    label = c(3, 5, 6, 2, 13, 4, 15, 0, 14),
                    lty   = c(1, 1, 1, 1, 1, 5, 5, 5, 5),
                    arrow = "1.5,1.0")
plot(tda_pl_graph(p, nodes, edges))

3.6.6 Trees

The same tree as gd15.cf, laid out radially: pl = 1 rather than gd15’s pl = 3.

Box 7gd15a.cf

edges <- data.frame(
    from = c(6, 6, 7, 7, 8, 8, 9, 9, 1, 1, 1, 7),
    to   = c(2, 4, 3, 5, 1, 6, 7, 8, 10, 11, 12, 13),
    v    = c(1.0, 1.5, 2.0, 2.5, 1.5, 1.0, 3.0, 1.2, 1.6, 2.0, 3.0, 4.0))
g <- tda_graph(edges, directed = FALSE)

plot(g, width = 50, height = 70, layout = "tree", rt = 9, pl = 1, nc = 100)

The same tree again, hierarchically: pl = 2.

Box 8gd15b.cf

edges <- data.frame(
    from = c(6, 6, 7, 7, 8, 8, 9, 9, 1, 1, 1, 7),
    to   = c(2, 4, 3, 5, 1, 6, 7, 8, 10, 11, 12, 13),
    v    = c(1.0, 1.5, 2.0, 2.5, 1.5, 1.0, 3.0, 1.2, 1.6, 2.0, 3.0, 4.0))
g <- tda_graph(edges, directed = FALSE)

plot(g, width = 90, height = 50, layout = "tree", rt = 9, pl = 2, nc = 100)

pltree lays a tree out by itself from a root; plot(g, layout = "tree") is that command, and gd15.cf’s tree of gd1.dat is the manual’s figure.

Box 9pltree (gd15.cf)

tree <- data.frame(from = c(6, 6, 7, 7, 8, 8, 9, 9, 1, 1, 1, 7),
                   to   = c(2, 4, 3, 5, 1, 6, 7, 8, 10, 11, 12, 13),
                   v    = c(1.0, 1.5, 2.0, 2.5, 1.5, 1.0, 3.0, 1.2, 1.6, 2.0, 3.0, 4.0))
plot(tda_graph(tree, directed = FALSE), width = 90, height = 50,
     layout = "tree", rt = 9, pl = 3, nc = 100)

3.4.2 Sequence Data Types

TDA reads sequence data two ways. Type 1 is one column per time point. Type 2 reads the columns as state/time pairs, one pair per spell, and builds the time axis from the times rather than from the column count – so the same six columns become three variables over 0 to 8 instead of six over 0 to 5. type = 2 is TDA’s seqdef(m = 2).

Box 10seq.d2 read both ways (seq3.cf)

seqd2 <- read.table(ex("seq.d2"),
                    col.names = c("ID", "Y1", "T1", "Y2", "T2", "Y3", "T3"))
tda_seq_info(seqd2[, -1])
  sn type variables tmin tmax nstates       states
1  1    1         6    0    5       9 0, 1, 2,....
# seq3.cf declares both structures at once, the second as type 2; the
# manual's box shows them in one table, with their own time axes
seqd3i <- read.table(ex("seq.d3"),
                     col.names = c("ID", paste0("Y", 0:7),
                                   "S1", "T1", "S2", "T2", "S3", "T3"))
tda_seq_info(list(paste0("Y", 0:7), c("S1", "T1", "S2", "T2", "S3", "T3")),
             data = seqd3i, type = c(1, 2))
  sn type variables tmin tmax nstates  states
1  1    1         8    0    7       3 1, 3, 7
2  2    2         3    0    8       3 1, 2, 3
tda_seqpd(seqd2, type = 2, id = "ID")
  case y0_0 y0_1 y0_2 y0_3 y0_4 y0_5 y0_6 y0_7 y0_8 id
1    1    1    1    1    1   -1   -1   -1   -1   -1  1
2    2    1    1    1    1    1    2    2   -1   -1  2
3    3    2    2    2    2    2    2    2    1   -1  3
4    4    1    1    1    1    1    1    2   -1   -1  4
5    5   -1   -1   -1   -1    1    1    2    2    3  5

3.4.3 Printing Sequence Data

seqpd writes the sequence data back out in one of four layouts, TDA’s own m=: every structure side by side on one row per case, one row per case and structure, one row per case and position, or one row per spell with its origin and destination state and the times it spans. The manual’s example defines two sequence structures and so prints two sets of columns; this one has a single structure.

Box 11seqpd (seq5.cf)

# seq5.cf declares two structures over seq.d3: Y0..Y7 one column per
# time point, and three state/time pairs read as a type 2 sequence. The
# tables then carry a column per structure, and the time axis runs to 8
# rather than to 7.
seqd3 <- read.table(ex("seq.d3"),
                    col.names = c("ID", paste0("Y", 0:7),
                                  "S1", "T1", "S2", "T2", "S3", "T3"))
pairs <- c("S1", "T1", "S2", "T2", "S3", "T3")
tda_seqpd(seqd3, id = "ID", variables = paste0("Y", 0:7), second = pairs)
  case y0_0 y0_1 y0_2 y0_3 y0_4 y0_5 y0_6 y0_7 y0_8 y1_0 y1_1 y1_2 y1_3 y1_4 y1_5 y1_6 y1_7 y1_8 id
1    1    7    3    3    1    3    3    7    7   -1    1    1    1    1   -1   -1   -1   -1   -1  1
2    2    3    3    7    1    1   -1   -1    3   -1    1    1    1    1    1    2    2   -1   -1  2
3    3   -1    3    3   -1    1    1    1    1   -1    2    2    2    2    2    2    2    1   -1  3
4    4    1    1    3    3    3    3   -1   -1   -1    1    1    1    1    1    1    2   -1   -1  4
5    5    7    3    3    1    3    3    7    7   -1   -1   -1   -1   -1    1    1    2    2    3  5
tda_seqpd(seqd3, layout = 3, id = "ID", variables = paste0("Y", 0:7),
          second = pairs)
   case time y0 y1 id
1     1    0  7  1  1
2     1    1  3  1  1
3     1    2  3  1  1
4     1    3  1  1  1
5     1    4  3 -1  1
6     1    5  3 -1  1
7     1    6  7 -1  1
8     1    7  7 -1  1
9     1    8 -1 -1  1
10    2    0  3  1  2
11    2    1  3  1  2
12    2    2  7  1  2
13    2    3  1  1  2
14    2    4  1  1  2
15    2    5 -1  2  2
16    2    6 -1  2  2
17    2    7  3 -1  2
18    2    8 -1 -1  2
19    3    0 -1  2  3
20    3    1  3  2  3
21    3    2  3  2  3
22    3    3 -1  2  3
23    3    4  1  2  3
24    3    5  1  2  3
25    3    6  1  2  3
26    3    7  1  1  3
27    3    8 -1 -1  3
28    4    0  1  1  4
29    4    1  1  1  4
30    4    2  3  1  4
31    4    3  3  1  4
32    4    4  3  1  4
33    4    5  3  1  4
34    4    6 -1  2  4
35    4    7 -1 -1  4
36    4    8 -1 -1  4
37    5    0  7 -1  5
38    5    1  3 -1  5
39    5    2  3 -1  5
40    5    3  1 -1  5
41    5    4  3  1  5
42    5    5  3  1  5
43    5    6  7  2  5
44    5    7  7  2  5
45    5    8 -1  3  5
tda_seqpd(seqd3, layout = 4, id = "ID", variables = paste0("Y", 0:7),
          second = pairs)
   case sn org des ts tf id
1     1  1   7   3  0  1  1
2     1  2   3   1  1  3  1
3     1  3   1   3  3  4  1
4     1  4   3   7  4  6  1
5     1  5   7  -1  6  8  1
6     1  6  -1  -1  8  9  1
7     2  1   3   7  0  2  2
8     2  2   7   1  2  3  2
9     2  3   1  -1  3  5  2
10    2  4  -1   3  5  7  2
11    2  5   3  -1  7  8  2
12    2  6  -1  -1  8  9  2
13    3  1  -1   3  0  1  3
14    3  2   3  -1  1  3  3
15    3  3  -1   1  3  4  3
16    3  4   1  -1  4  8  3
17    3  5  -1  -1  8  9  3
18    4  1   1   3  0  2  4
19    4  2   3  -1  2  6  4
20    4  3  -1  -1  6  9  4
21    5  1   7   3  0  1  5
22    5  2   3   1  1  3  5
23    5  3   1   3  3  4  5
24    5  4   3   7  4  6  5
25    5  5   7  -1  6  8  5
26    5  6  -1  -1  8  9  5

3.4.5 State Indicators

seqsi turns a sequence into an indicator matrix: for each case and each time point asked for, one column per state, 1 where the case is in that state. m = 2 counts how often each state has been visited up to that point instead.

Box 12seqsi (seq6.cf)

seqd1i <- read.table(ex("seq.d1"))
names(seqd1i) <- c("ID", paste0("Y", seq_len(ncol(seqd1i) - 1L)))
tda_seqsi(seqd1i, tp = 1:5, id = "ID")
  case s1_1 s3_1 s7_1 s1_2 s3_2 s7_2 s1_3 s3_3 s7_3 s1_4 s3_4 s7_4 s1_5 s3_5 s7_5 id
1    1    0    1    0    0    1    0    1    0    0    0    1    0    0    1    0  1
2    2    0    1    0    0    0    1    1    0    0    1    0    0    0    0    0  2
3    3    0    1    0    0    1    0    0    0    0    1    0    0    1    0    0  3
4    4    1    0    0    0    1    0    0    1    0    0    1    0    0    1    0  4
5    5    0    1    0    0    1    0    1    0    0    0    1    0    0    1    0  5
tda_seqsi(seqd1i, tp = 1:5, id = "ID", options = list(m = 2))
  case s1_1 s3_1 s7_1 s1_2 s3_2 s7_2 s1_3 s3_3 s7_3 s1_4 s3_4 s7_4 s1_5 s3_5 s7_5 id
1    1    0    1    0    0    1    0    1    0    0    0    1    0    0    1    0  1
2    4    1    0    0    0    1    0    0    1    0    0    1    0    0    1    0  4
3    5    0    1    0    0    1    0    1    0    0    0    1    0    0    1    0  5

3.3.5 Joining Episode Data

ejoin merges two episode files onto one time axis: every point where either file changes state starts a new sub-episode, and each side’s state is carried into it (-3 where that side has none). The manual’s own example is two files, six episodes and two, joined into sixteen.

Box 13ejoin (ej1.cf)

ej1 <- read.table(ex("ej1.dat"),
                  col.names = c("ID", "NS", "SN", "TS", "TF", "S", "X1", "X2"))
ej2 <- read.table(ex("ej2.dat"),
                  col.names = c("ID", "NS", "SN", "TS", "TF", "S", "Y1"))
# every column past the structural six is carried through, filled -3
# where that side has no spell
tda_ejoin(ej1[, c("ID", "TS", "TF", "S", "X1", "X2")],
          id = "ID", start = "TS", end = "TF", state = "S",
          with = ej2[, c("ID", "TS", "TF", "S", "Y1")],
          with_id = "ID", with_start = "TS", with_end = "TF",
          with_state = "S",
          options = list(nw = 3, fmt0 = 4, fmt1 = "8.4"))
   id nspell spellno level start end state1 state2   X1   X2   Y1
1   1      8       1     0     0   1      1     -3  1.1 11.1 -3.0
2   1      8       2     0     1   2      1      3  1.1 11.1  5.5
3   1      8       2     1     1   2      2     -3  2.2 22.2 -3.0
4   1      8       3     0     2   3      2      3  2.2 22.2  5.5
5   1      8       4     0     3   4      2      3  2.2 22.2  5.5
6   1      8       4     1     3   4     -3      4 -3.0 -3.0  6.6
7   1      8       5     0     4   5      2      4  2.2 22.2  6.6
8   1      8       6     0     5   6     -3      4 -3.0 -3.0  6.6
9   2      8       1     0     3   4      3     -3  3.3 33.3 -3.0
10  2      8       2     0     4   5      3     -3  3.3 33.3 -3.0
11  2      8       2     1     4   5      4     -3  4.4 44.4 -3.0
12  2      8       3     0     5   6      3     -3  3.3 33.3 -3.0
13  2      8       3     1     5   6      5     -3  5.5 55.5 -3.0
14  2      8       4     0     6   8      5     -3  5.5 55.5 -3.0
15  2      8       5     0     8   9     -3     -3 -3.0 -3.0 -3.0
16  2      8       6     0     9  10      6     -3  6.6 66.6 -3.0

4 PostScript Plots

TDA draws its PostScript. The command files of the manual’s chapter 4 are reproduced here through the tda_pl_* family; each box names the command file it follows, and the package’s inst/examples/gallery/ carries the same code as a standalone script beside a PNG of TDA’s output for comparison.

4.1 Preparing the Plot Environment

The manual’s first plot: axes, a frame, the sine function and, by plotf1, its first derivative – deriv = 1 in tda_pl_function() draws the derivative of the expression it is given, so the second call repeats sin(x1) as the command file does.

Box 14plot1.cf

p <- tda_ps(xlim = c(0, 6), ylim = c(-1, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1), ic = c(10, 0))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, "sin(x1)", range = c(0, 6), step = 0.1)
p <- tda_pl_function(p, "sin(x1)", range = c(0, 6), step = 0.1, deriv = 1)
p <- tda_pl_text(p, "sine", at = c(2.7, 0.6))
p <- tda_pl_text(p, "cosine", at = c(4.6, 0.6))

plot(p)

Box 15plot2.cf

p <- tda_ps(xlim = c(0, 595), ylim = c(0, 842), width = 100, height = 150)
p <- tda_pl_axes(p, sc = 72)
p <- tda_pl_frame(p, gray = 0.95)
p <- tda_pl_grid(p, at_x = seq(72, 576, 72), at_y = seq(72, 792, 72))

plot(p)

4.2 Plotting Coordinate Systems

Box 16plot3.cf

p <- tda_ps(xlim = c(-2, 2), ylim = c(0, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 0.5), ic = c(2, 0))
p <- tda_pl_frame(p)
p <- tda_pl_grid(p, at_x = c(-1, 0, 1), at_y = 0.5)
p <- tda_pl_labels(p, "Linear Coordinate System", which = "title")
p <- tda_pl_labels(p, "X axis", which = "x")
p <- tda_pl_labels(p, "Y axis", which = "y", sc = 3)

plot(p)

Box 17plot4.cf

p <- tda_ps(xlim = c(1, 1000), ylim = c(2, 500), width = 90, height = 50,
           log = "xy")
p <- tda_pl_axes(p, sc = c(10, 5), ic = c(10, 0))
p <- tda_pl_frame(p)
p <- tda_pl_grid(p, at_x = c(10, 100, 1000), at_y = c(10, 50, 250))
p <- tda_pl_labels(p, "Logarithmic Coordinate System", which = "title")
p <- tda_pl_labels(p, "X axis", which = "x")
p <- tda_pl_labels(p, "Y axis", which = "y", sc = 3)

plot(p)

A second pair of axes, drawn at a position given in user coordinates rather than on the boundary of the plot: at = and dir = in tda_pl_axis() are plx’s and ply’s parameters.

Box 18plot5.cf

p <- tda_ps(xlim = c(-2, 2), ylim = c(0, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 0.5), ic = c(2, 0))
p <- tda_pl_axis(p, "x", sc = 1, ic = 5, dir = 1, at = c(-1, 1, 1, 1))
p <- tda_pl_axis(p, "y", sc = 0.5, ic = 5, dir = 1, at = c(2, 0, 2, 1))

plot(p)

A second panel on the same page, placed with tda_pl_panel().

Box 19plot6.cf

# plot6.cf places the first plot with psorg = 100,600, in PostScript
# points; origin = is in mm like width = and height =
p <- tda_ps(xlim = c(0, 6), ylim = c(-1, 1), width = 50, height = 40,
            origin = c(100, 600) * 25.4 / 72)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_function(p, "sin(x1)", range = c(0, 6), step = 0.1)
p <- tda_pl_text(p, "Plot 1", at = c(4, 0.2))

# the second plot has psorg = 270,600: 170 points to the right of the
# first, and a panel's origin is relative to the previous one's
p <- tda_pl_panel(p, origin = c(170, 0) * 25.4 / 72, width = 50, height = 30)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_function(p, "sin(x1)", range = c(0, 6), step = 0.1)
p <- tda_pl_text(p, "Plot 2", at = c(4, 0.2))

plot(p)

4.3 Combining Plot Files

dplot puts PostScript files that already exist into a grid. The two coordinate systems of 4.2 are drawn again as files, and tda_combine_ps() places each of them twice on one page.

Box 20plot7.cf

p3 <- tda_ps(xlim = c(-2, 2), ylim = c(0, 1), width = 90, height = 50)
p3 <- tda_pl_axes(p3, sc = c(1, 0.5), ic = c(2, 0))
p3 <- tda_pl_frame(p3)
p3 <- tda_pl_grid(p3, at_x = c(-1, 0, 1), at_y = 0.5)
p3 <- tda_pl_labels(p3, "Linear Coordinate System", which = "title")
p3 <- tda_pl_labels(p3, "X axis", which = "x")
p3 <- tda_pl_labels(p3, "Y axis", which = "y", sc = 3)
f3 <- tda_ps_file(p3)

p4 <- tda_ps(xlim = c(1, 1000), ylim = c(2, 500), width = 90, height = 50,
             log = "xy")
p4 <- tda_pl_axes(p4, sc = c(10, 5), ic = c(10, 0))
p4 <- tda_pl_frame(p4)
p4 <- tda_pl_grid(p4, at_x = c(10, 100, 1000), at_y = c(10, 50, 250))
p4 <- tda_pl_labels(p4, "Logarithmic Coordinate System", which = "title")
p4 <- tda_pl_labels(p4, "X axis", which = "x")
p4 <- tda_pl_labels(p4, "Y axis", which = "y", sc = 3)
f4 <- tda_ps_file(p4)

r <- tda_combine_ps(list(c(f3, f3), c(f4, f4)), width = 100, height = 60,
                    file = "plot7.ps")

tda_plot_ps(file.path(r$dir, "plot7.ps"))

4.4.1 Polygons and Scatterplots

plot8.cf draws twenty points with rd(-2, 2), sorts them, and takes nd() of each – TDA’s standard normal distribution function. Both are used here, so the points are the manual’s.

Box 21plot8.cf

pdat <- tda_derive(data.frame(RDS = sort(tda_rd(20, -2, 2))),
                   ND = "nd(RDS)")

p <- tda_ps(pdat, xlim = c(-2, 2), ylim = c(0, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(0.5, 1), ic = c(0, 10), fmt = c("4.1", NA))
p <- tda_pl_frame(p)
p <- tda_pl_points(p, "RDS", "ND", symbol = 4, size = 1.5, lty = 1)

plot(p)

The nine line types, lty = 1 to 9.

Box 22plot9.cf

p <- tda_ps(xlim = c(-2, 2), ylim = c(0, 1), width = 100, height = 60)
p <- tda_pl_frame(p)
for (lt in 1:9)
    p <- tda_pl_lines(p, x = c(-1.5, 0.5), y = c(lt, lt) / 10, lty = lt)
for (lt in 1:9)
    p <- tda_pl_text(p, sprintf("line type %d", lt), at = c(0.75, lt / 10))

plot(p)

The seventeen marker symbols: symbol = in tda_pl_text() is pltext’s s=, and puts the marker at the text position.

Box 23plot10.cf

p <- tda_ps(xlim = c(-2, 2), ylim = c(0, 1), width = 100, height = 60)
p <- tda_pl_frame(p)

for (s in 9:1)
    p <- tda_pl_text(p, sprintf("marker %d", s), at = c(-1.5, (10 - s) / 10),
                     symbol = s)
for (s in 17:10)
    p <- tda_pl_text(p, sprintf("marker %d", s), at = c(0, (19 - s) / 10),
                     symbol = s)

plot(p)

4.4.3 Plotting Text

TDA writes special characters as @nnn (the Symbol font) or \\nnn (Times-Roman, through its own re-encoding table); both round-trip, a literal UTF-8 character does not. plots.cf shows each escape and, beside it, the escape written out literally: \\100 is the @ sign and \\\\ a backslash, so \\100141 prints as @141, and \\324 is the quotation mark around each. The R strings below carry one more level of backslashes than the command file, R’s own.

Box 24plots.cf

p <- tda_ps(xlim = c(0, 10), ylim = c(0, 5), width = 120, height = 60)
p <- tda_pl_axes(p, sc = 1, ic = c(0, 2))
p <- tda_pl_frame(p)
p <- tda_pl_text(p, "@141 = 2.5 \\245 @142", at = c(1, 4))
p <- tda_pl_text(p, "pltext (xy=1,4) = \\324\\100141 = 2.5 \\\\245 \\100142\\324",
                 at = c(4, 4))
p <- tda_pl_text(p, "@123 = @173 @161 @174 @161 @316 @121 @175", at = c(1, 2))
p <- tda_pl_text(p, paste("pltext (xy=1,2) = \\324\\100123 = \\100173 \\100161",
                          "\\100174 \\100161 \\100316 \\100121 \\100175\\324"),
                 at = c(2.2, 0.9))
p <- tda_pl_text(p, "Sm\\277rebr\\277d", at = c(1, 3))
p <- tda_pl_text(p, "pltext (xy=1,3) = \\324Sm\\\\277rebr\\\\277d\\324", at = c(4, 3))

p <- tda_pl_lines(p, x = c(3.5, 2.7), y = c(4.05, 4.05), a = "1,1")
p <- tda_pl_lines(p, x = c(3.5, 2.7), y = c(3.05, 3.05), a = "1,1")
p <- tda_pl_arc(p, x = c(1.5, 3.2, 3.2), y = c(1.5, 1.5, 2.1), curvature = -90)
p <- tda_pl_lines(p, x = c(3.2, 2.9), y = c(2.1, 2.1), a = "1,1")
p <- tda_pl_arc(p, x = c(1.5, 1.5), y = c(1.5, 0.95), curvature = -90)
p <- tda_pl_lines(p, x = c(1.5, 2), y = c(0.95, 0.95))

plot(p)

4.4.5 Step Functions

plot11.cf is plot8.cf with dir=1 on the plot command: the pen moves along the x axis first and then along y, and the twenty points on the normal distribution function become a step function. dir keeps its name in tda_pl_lines().

Box 25plot11.cf

pdat <- tda_derive(data.frame(RDS = sort(tda_rd(20, -2, 2))),
                   ND = "nd(RDS)")

p <- tda_ps(pdat, xlim = c(-2, 2), ylim = c(0, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(0.5, 1), ic = c(0, 10), fmt = c("4.1", NA))
p <- tda_pl_frame(p)
p <- tda_pl_lines(p, "RDS", "ND", dir = 1, gray = 0.9)

plot(p)

4.4.6 General Functions

Two normal densities, one of them filled, and a line at each mean. The expression string goes to TDA’s parser, not R’s, so it is written with TDA’s functions – here ndf(), the standard normal density.

Box 26plot12.cf

p <- tda_ps(xlim = c(-3, 3), ylim = c(0, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(0.5, 1), ic = c(0, 10), fmt = c("4.1", NA))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, "ndf((x1 - 1.5) / 0.5) / 0.5", range = c(0, 3),
                     step = 0.05)
p <- tda_pl_lines(p, x = c(1.5, 1.5), y = c(0, 0.798))
p <- tda_pl_function(p, "ndf(x1)", range = c(-3, 3), step = 0.1, gray = 0.8)
p <- tda_pl_lines(p, x = c(0, 0), y = c(0, 0.399))

plot(p)

4.4.7 Arrows

A polygon can end in an arrow: a = xs, ys gives the length and width of the head in millimetres. plot13.cf draws eight of them from one point, in two line types and with heads of growing size; a keeps its name in tda_pl_lines().

Box 27plot13.cf

p <- tda_ps(xlim = c(0, 10), ylim = c(0, 6), width = 90, height = 50)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_frame(p)

p <- tda_pl_lines(p, x = c(5, 8),   y = c(3, 3),   a = "1,1")
p <- tda_pl_lines(p, x = c(5, 7),   y = c(3, 5),   a = "1.5,1.5")
p <- tda_pl_lines(p, x = c(5, 5),   y = c(3, 5.5), a = "2,2")
p <- tda_pl_lines(p, x = c(5, 3),   y = c(3, 5),   a = "2.5,2.5")
p <- tda_pl_lines(p, x = c(5, 2),   y = c(3, 3),   a = "1,1",   lty = 5)
p <- tda_pl_lines(p, x = c(5, 3),   y = c(3, 1),   a = "1.5,1.5", lty = 5)
p <- tda_pl_lines(p, x = c(5, 5),   y = c(3, 0.5), a = "2,2",   lty = 5)
p <- tda_pl_lines(p, x = c(5, 7),   y = c(3, 1),   a = "2.5,2.5", lty = 5)

p <- tda_pl_text(p, "[1,1]", at = c(8.3, 3))
p <- tda_pl_text(p, "[1.5,1.5]", at = c(7.3, 5))
p <- tda_pl_text(p, "[2,2]", at = c(5.3, 5.6))
p <- tda_pl_text(p, "[2.5,2.5]", at = c(1.6, 5))

plot(p)

4.4.8 Circles, Arcs, Ellipses

ploto, which is tda_pl_circle(), draws a circle or, with angles =, an arc of one; plotk, which is tda_pl_arc(), connects points by circular arcs that can carry an arrowhead. The manual’s Figure 3 labels one of them with the Symbol font’s alpha, which TDA writes as @141; the same escape is used here (see 4.4.3).

Box 28plot14.cf

p <- tda_ps(xlim = c(0, 10), ylim = c(0, 6), width = 90, height = 50)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_frame(p)

p <- tda_pl_lines(p, x = c(1, 4), y = c(2, 5), lty = 5)
p <- tda_pl_arc(p, x = c(1, 4), y = c(2, 5), curvature = 45, arrow = c(1, 1))
p <- tda_pl_lines(p, x = c(1, 1), y = c(2, 4), lty = 5)
p <- tda_pl_circle(p, at = c(1, 2), r = 10, angles = c(45, 90))
p <- tda_pl_text(p, "plotk(a=1,1,sc=45) = a,b,c,d;", at = c(0.5, 1))
p <- tda_pl_text(p, "(a,b)", at = c(0.7, 1.6))
p <- tda_pl_text(p, "(c,d)", at = c(4.2, 5.1))
p <- tda_pl_circle(p, at = c(1, 2), r = 0.8, angles = c(45, 90), lty = 2)
p <- tda_pl_text(p, "@141", at = c(1.15, 2.4))

p <- tda_pl_arc(p, x = c(6, 6, 7), y = c(2, 4, 5), curvature = 45)
p <- tda_pl_arc(p, x = c(9, 9, 8), y = c(2, 4, 5), curvature = -45)
p <- tda_pl_text(p, "plotk(sc=45) = 6,2,6,4,7,5;", at = c(5.5, 1.3))
p <- tda_pl_text(p, "plotk(sc=-45) = 9,2,9,4,8,5;", at = c(5.5, 0.8))
p <- tda_pl_lines(p, x = c(5.3, 5.3, 5.7), y = c(1.4, 2.2, 2.5), a = "1,1")
p <- tda_pl_lines(p, x = c(9.6, 9.6, 9.2), y = c(1.2, 2.2, 2.5), a = "1,1")

plot(p)

A shaded circle and ellipse, dimension lines, and labelled arrows.

Box 29plot15.cf

p <- tda_ps(xlim = c(0, 10), ylim = c(0, 2), width = 90, height = 50)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_frame(p)

p <- tda_pl_circle(p, at = c(2, 1), r = 1, gray = 0.9)
p <- tda_pl_ellipse(p, at = c(7, 1), axes = c(2, 0.5), gray = 0.9)
p <- tda_pl_lines(p, x = c(0, 7), y = c(0.5, 0.5), lty = 5)
p <- tda_pl_lines(p, x = c(0, 7), y = c(1.5, 1.5), lty = 5)
p <- tda_pl_lines(p, x = c(1, 1), y = c(0, 1), lty = 5)
p <- tda_pl_lines(p, x = c(3, 3), y = c(0, 1), lty = 5)
p <- tda_pl_lines(p, x = c(5, 5), y = c(0, 1), lty = 5)
p <- tda_pl_lines(p, x = c(9, 9), y = c(0, 1), lty = 5)
p <- tda_pl_text(p, "ploto (xy=2,1,gs=0.9) = 1;", at = c(1.1, 1.7))
p <- tda_pl_text(p, "plote (xy=7,1,gs=0.9) = 2,0.5;", at = c(5.1, 1.7))
p <- tda_pl_lines(p, x = c(2, 3), y = c(1, 1), a = "1,1")
p <- tda_pl_lines(p, x = c(7, 9), y = c(1, 1), a = "1,1")
p <- tda_pl_lines(p, x = c(7, 7), y = c(1, 1.5), a = "1,1")
p <- tda_pl_text(p, "1", at = c(2.3, 0.85))
p <- tda_pl_text(p, "2", at = c(7.8, 0.85))
p <- tda_pl_text(p, "0.5", at = c(7.2, 1.2))

plot(p)

Three ellipses at one centre, two of them rotated: rotate = in tda_pl_ellipse() is the third value on plote’s right-hand side, the angle in degrees.

Box 30plot16.cf

p <- tda_ps(xlim = c(0, 10), ylim = c(0, 2), width = 90, height = 50)
p <- tda_pl_axes(p, sc = 1)

p <- tda_pl_ellipse(p, at = c(4, 1), axes = c(3, 0.3), gray = 0.9)
p <- tda_pl_ellipse(p, at = c(4, 1), axes = c(3, 0.3), rotate = 45)
p <- tda_pl_ellipse(p, at = c(4, 1), axes = c(3, 0.3), rotate = -45)

p <- tda_pl_text(p, "plote(xy=4,1,gs=0.9) = 3,0.3;", at = c(7.3, 1.0))
p <- tda_pl_text(p, "plote(xy=4,1) = 3,0.3,-45;", at = c(7.3, 0.5))
p <- tda_pl_text(p, "plote(xy=4,1) = 3,0.3,45;", at = c(7.3, 1.5))
p <- tda_pl_rect(p, x = c(11.5, 11.5), y = c(0, 0))

plot(p)

4.4.9 Convex Hull

plot17.cf draws one hundred points from X = rd, Y = rd and puts a convex hull around them. The draws come from TDA’s generator at its default seed, so these are the manual’s points: the PostScript this writes carries the same drawing operations, in the same order, as TDA’s own run of the command file.

Box 31plot17.cf

gen <- tda_rng()
pdat <- data.frame(X = numeric(100), Y = numeric(100))
for (i in 1:100) {
    pdat$X[i] <- gen$rd()
    pdat$Y[i] <- gen$rd()
}
p <- tda_ps(pdat, xlim = c(-0.5, 1.5), ylim = c(-0.5, 1.5),
            width = 90, height = 50)
p <- tda_pl_axes(p, sc = 0.5)
p <- tda_pl_frame(p)
plot(tda_pl_hull(p, "X", "Y", gray = 0.95, symbol = 5, size = 0.7))

plot18.cf smooths the same hull. smooth is plotch’s ns=, the number of intervals for its Akima smoothing, and expand is ic=, a margin in millimetres: each point of the smoothed outline is pushed away from the hull’s centroid in proportion to its distance from it, so the outline grows without changing shape.

Box 32plot18.cf

plot(tda_pl_hull(p, "X", "Y", gray = 0.95, symbol = 5, size = 0.7,
                 smooth = 10, expand = 2))

4.4.10 Smoothing Polygons

plots draws an Akima spline through the points of two variables, and plotsp the same through literal coordinates; in the C both reach the same function, as plot and plotp do. tda_pl_smooth() is plots, and the points are given as a data frame.

Box 33plot19.cf

pdat <- data.frame(x = c(1, 2, 3, 4, 5, 6), y = c(1, 2.5, 2, 2.5, 2, 1))

p <- tda_ps(pdat, xlim = c(0, 7), ylim = c(0, 3), width = 90, height = 50)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_frame(p)
p <- tda_pl_smooth(p, "x", "y", ns = 10, gray = 0.9)
p <- tda_pl_points(p, "x", "y", symbol = 5)

plot(p)

plot20.cf closes the same curve: sc = 1, which keeps its name.

Box 34plot20.cf

pdat <- data.frame(x = c(1, 2, 3, 4, 5, 6), y = c(1, 2.5, 2, 2.5, 2, 1))

p <- tda_ps(pdat, xlim = c(0, 7), ylim = c(0, 3), width = 90, height = 50)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_frame(p)
p <- tda_pl_smooth(p, "x", "y", ns = 10, gray = 0.9, sc = 1)
p <- tda_pl_points(p, "x", "y", symbol = 5)

plot(p)

4.4.11 Contour Plots

plotc draws contour lines of a function of two variables, x1 and x2 in TDA’s expression syntax, over a grid of the given resolution; plot21.cf’s function is the sum of two normal densities.

Box 35plot21.cf

p <- tda_ps(xlim = c(-2, 2), ylim = c(-2, 2), width = 70, height = 70)
p <- tda_pl_axes(p, sc = 1)
p <- tda_pl_frame(p)
p <- tda_pl_contour(p,
    "exp(-((x1+1)^2+(x2+1)^2)/2)/(2*pi)+exp(-((x1-1)^2+(x2-1)^2)/2)/(2*pi)",
    levels = c(0.01, 0.05, 0.1, 0.125, 0.15), resolution = c(50, 50),
    gray = 0.9)

plot(p)

5 Mathematical Procedures

5.1.2 Matrices

TDA has its matrix language, and a fair part of it exists because TDA has no other way to get a matrix into a session. R does, so those commands have no wrapper and need none:

TDA R
mdef (a literal matrix) matrix()
mdefc (constant) matrix(d, m, n)
mdefi (rectangular identity) diag(1, m, n)
mdeff (from a file) as.matrix(read.table(...))
mpr (print an expression) printing the return value
mplog, mppar, mpcov, mpgrad logLik(), $estimates, vcov()

Two do compute something and do have wrappers: mdefg, a graph’s adjacency matrix, is tda_mdefg(), and mbrr is tda_brr(). mdefb extracts a block of the data matrix and is reached through the commands= argument of tda_dblock(). Everything else in 5.1.4 takes and returns a plain R matrix.

The manual’s matrix chapter prints with mfmt = 13.10; these boxes print with ten digits too, so a number can be read off against it.

5.1.4.1 Elementary Matrix Commands

Box 36Shape, product, transpose, trace

op <- options(digits = 10)
A <- matrix(1:6, 3, 2)
c(tda_mnrow(A), tda_mncol(A))
[1] 3 2
tda_mmul(A, tda_mtransp(A))
     [,1] [,2] [,3]
[1,]   17   22   27
[2,]   22   29   36
[3,]   27   36   45
tda_mtrace(tda_mcross(A))
[1] 91

mcross is t(A) %*% A, and mdiag, mdrow, mdcol and mdiagd make or take diagonals.

Box 37Diagonals

tda_mdiag(1:4)
     [,1] [,2] [,3] [,4]
[1,]    1    0    0    0
[2,]    0    2    0    0
[3,]    0    0    3    0
[4,]    0    0    0    4
tda_mdrow(A)
     [,1] [,2] [,3]
[1,]    5    0    0
[2,]    0    7    0
[3,]    0    0    9
tda_mdcol(A)
     [,1] [,2]
[1,]    6    0
[2,]    0   15
tda_mdiagd(A)
[1] 1 5

msqrtd and msqrti are the elementwise square root and its inverse.

Box 38Elementwise square roots

tda_msqrtd(matrix(1:4, 2, 2))
     [,1] [,2]
[1,]    1    0
[2,]    0    2
tda_msqrti(matrix(1:4, 2, 2))
     [,1] [,2]
[1,]    1  0.0
[2,]    0  0.5

Concatenation, row and column sums, selection and trimming.

Box 39Concatenating, summing, selecting, trimming

tda_mcath(A, A)
     [,1] [,2] [,3] [,4]
[1,]    1    4    1    4
[2,]    2    5    2    5
[3,]    3    6    3    6
tda_mcatv(A, A)
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
[4,]    1    4
[5,]    2    5
[6,]    3    6
tda_mrsum(A)
[1] 5 7 9
tda_mcsum(A)
[1]  6 15
tda_msrow(A, 1:2)
     [,1] [,2]
[1,]    1    4
[2,]    2    5
tda_mscol(A, 1)
     [,1]
[1,]    1
[2,]    2
[3,]    3
tda_mtrim(A, 1)
     [,1]
[1,]    4
[2,]    5
[3,]    6
tda_mtrim(A, leading_rows = 1)
     [,1] [,2]
[1,]    2    5
[2,]    3    6

Vectorisation, the Kronecker product, and the permutation commands. mpinv inverts a permutation rather than a matrix.

Box 40Vectorising, Kronecker product, permutations

tda_mcvec(A)
[1] 1 2 3 4 5 6
tda_mrvec(A)
[1] 1 4 2 5 3 6
tda_mkp(A, A)
      [,1] [,2] [,3] [,4]
 [1,]    1    4    4   16
 [2,]    2    5    8   20
 [3,]    3    6   12   24
 [4,]    2    8    5   20
 [5,]    4   10   10   25
 [6,]    6   12   15   30
 [7,]    3   12    6   24
 [8,]    6   15   12   30
 [9,]    9   18   18   36
tda_mprow(A, c(2, 1, 3))
     [,1] [,2]
[1,]    2    5
[2,]    1    4
[3,]    3    6
tda_mpcol(A, c(2, 1))
     [,1] [,2]
[1,]    4    1
[2,]    5    2
[3,]    6    3
tda_mpsym(matrix(1:4, 2, 2), c(2, 1))
     [,1] [,2]
[1,]    4    2
[2,]    3    1
tda_mpinv(c(4, 2, 1, 3))
[1] 3 2 4 1

mag solves a system by Gaussian elimination, and reports what it found when there is no solution or more than one.

Box 41Gaussian elimination

G <- matrix(c(1, 2, 3, 5, 6, 7, 1, 2, 1), 3, 3, byrow = TRUE)
tda_mag(G, c(1, 0, 1), c(1, 2, 3))
     [,1] [,2] [,3]
[1,]    2    4    4
tda_mag(G, c(1, 0, 1), c(5, 4, 4))
     [,1] [,2]
[1,]    8    2

5.1.4.2 Sorting and Ranking

msort sorts the rows of a matrix by a column, msort1 a single column, and mrank ranks.

Box 42msort, msort1, mrank

tda_msort(matrix(rev(A), 3, 2), 1)
     [,1] [,2]
[1,]    4    1
[2,]    5    2
[3,]    6    3
tda_msort1(matrix(c(1, 2, 1, 2, 1, 3, 1, 4), ncol = 2), 2)
     [,1] [,2]
[1,]    1    1
[2,]    2    3
[3,]    2    4
tda_mrank(matrix(c(1, 2, 1, 2, 1, 3, 1, 4), 2), 1)
[1] 1 2

5.1.4.3 Standardization and Scaling

Box 43mcent, mstand, mscal1

tda_mcent(A)
     [,1] [,2]
[1,]   -1   -1
[2,]    0    0
[3,]    1    1
tda_mstand(A)
             [,1]         [,2]
[1,] -1.224744871 -1.224744871
[2,]  0.000000000  0.000000000
[3,]  1.224744871  1.224744871
tda_mscal1(A)
              [,1]         [,2]
[1,] 0.04761904762 0.1904761905
[2,] 0.09523809524 0.2380952381
[3,] 0.14285714286 0.2857142857

5.1.4.4 Matrix Norms

Box 44The three norms

c(tda_mnorm(A), tda_mnorm1(A), tda_mnorm2(A))
[1]  6.000000000 21.000000000  9.539392014

5.1.4.5 Inverse Matrices

minvd and minvs invert a general and a symmetric matrix. mginv is the generalized inverse, and what makes it one is that A B A is A again – which is the manual’s check.

Box 45minvd, minvs

A
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
tda_minvd(A)
     [,1] [,2]
[1,]    1  0.0
[2,]    0  0.2
[3,]    0  0.0
diag(1:2)
     [,1] [,2]
[1,]    1    0
[2,]    0    2
tda_minvs(diag(1:2))
     [,1] [,2]
[1,]    1  0.0
[2,]    0  0.5

Box 46A generalized inverse, and the identity that defines it

B0 <- matrix(c(1, 5, 0, 2, 6, 0, 3, 7, 0, 4, 8, 0), 4, 3, byrow = TRUE)
Bg <- tda_mginv(B0)
Bg
      [,1]   [,2]            [,3]   [,4]
[1,] -0.55 -0.225 1.000000000e-01  0.425
[2,]  0.25  0.125 4.857225733e-17 -0.125
[3,]  0.00  0.000 0.000000000e+00  0.000
tda_mmul(tda_mmul(B0, Bg), B0)
     [,1] [,2] [,3]
[1,]    1    5    0
[2,]    2    6    0
[3,]    3    7    0
[4,]    4    8    0

5.1.4.6 Cholesky Decomposition

Box 47mchol

tda_mchol(diag(1:4))
     [,1]        [,2]        [,3] [,4]
[1,]    1 0.000000000 0.000000000    0
[2,]    0 1.414213562 0.000000000    0
[3,]    0 0.000000000 1.732050808    0
[4,]    0 0.000000000 0.000000000    2

5.1.4.7 Least Squares Regression

mls takes the design and the response in one matrix, the response last.

Box 48mls

mlsA <- matrix(c(1, 2, 3, 4, 7, 11, -1, 1, 0, 5, 6, 11), 4, 3, byrow = TRUE)
mlsA   # the design and the response in one matrix, as the manual shows
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    7   11
[3,]   -1    1    0
[4,]    5    6   11
tda_mls(mlsA)
$x
[1] 1 1

$residual
[1] 5.551115123e-16

$rank
[1] 2

5.1.4.8 Linear Equations

Box 49mlse

mlseA <- matrix(c(-0.4744, -0.4993, -0.7250, -0.8615, 0.5840, -0.7947,
                  0.1652, 0.0, -0.6587, -0.3450, 0.6687, 0.0), 3, 4,
                byrow = TRUE)
mlseA   # the manual's box prints the input too
        [,1]    [,2]    [,3]    [,4]
[1,] -0.4744 -0.4993 -0.7250 -0.8615
[2,]  0.5840 -0.7947  0.1652  0.0000
[3,] -0.6587 -0.3450  0.6687  0.0000
tda_mlse(mlseA)
$x
[1] 0.4087254230 0.4301912621 0.6245602237

$residual
[1] 3.705655206e-16

$rank
[1] 3

5.1.4.9 Linear Inequalities

Box 50mlsi

mlsiA <- matrix(c(1, 2, 3, 5, 6, 11, -1, -2, -3, 0, 1, 1), 4, 3, byrow = TRUE)
mlsiA   # the manual's box prints the input too
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    5    6   11
[3,]   -1   -2   -3
[4,]    0    1    1
tda_mlsi(mlsiA)
$x
[1] 1 1

$residual
[1] NA

$rank
[1] 0

5.1.4.11 Eigenvalues and Eigenvectors

mev is the general case, which may have complex eigenvalues, and mevs the symmetric one. The manual’s mev example is a defective problem – 5.236 and 0.764 are each a double eigenvalue – so the digits beyond the seventh, and which two vectors span each repeated eigenvalue’s space, depend on the machine’s arithmetic; the manual’s box was computed on a different one and differs there, not in the eigenvalues themselves.

Box 51mev and mevs

tda_mev(matrix(c(6, -3, 4, 1, 4, 2, 4, 0, 4, -2, 3, 1, 4, 2, 3, 1),
               4, 4, byrow = TRUE))
$values
[1] 5.2360678384+0i 5.2360681166+0i 0.7639319802+0i 0.7639320648+0i

$vectors
                 [,1]             [,2]             [,3]             [,4]
[1,] -0.2627643793+0i -0.2627644056+0i  0.3311926976+0i  0.3311926619+0i
[2,] -0.6152998198+0i -0.6152998173+0i -0.1131489151+0i -0.1131489401+0i
[3,] -0.2350236102+0i -0.2350236246+0i -0.2962277587+0i -0.2962277177+0i
[4,] -0.7050708584+0i -0.7050708460+0i -0.8886832028+0i -0.8886832266+0i

$er
[1] 5.2360678384 5.2360681166 0.7639319802 0.7639320648

$ei
[1] 0 0 0 0

$evr
              [,1]          [,2]          [,3]          [,4]
[1,] -0.2627643793 -0.2627644056  0.3311926976  0.3311926619
[2,] -0.6152998198 -0.6152998173 -0.1131489151 -0.1131489401
[3,] -0.2350236102 -0.2350236246 -0.2962277587 -0.2962277177
[4,] -0.7050708584 -0.7050708460 -0.8886832028 -0.8886832266

$evi
     [,1] [,2] [,3] [,4]
[1,]    0    0    0    0
[2,]    0    0    0    0
[3,]    0    0    0    0
[4,]    0    0    0    0
tda_mevs(matrix(c(1, 1, 1, 1, 2, 0, 1, 0, 10), 3, 3, byrow = TRUE))
$values
[1] 10.1112597744  2.5823542359  0.3063859897

$vectors
              [,1]           [,2]           [,3]
[1,] 0.11056720082 -0.50208545527 -0.85772086934
[2,] 0.01363132286 -0.86216502656  0.50644412724
[3,] 0.99377516629  0.06768797962  0.08848308468

5.1.4.12 Singular Value Decomposition

On a cross-product matrix the singular values are the eigenvalues, which is the manual’s way of showing the two agree. msvd1 prints as in the manual’s box; mevs returns its eigenvalues in descending order where the manual’s box (2002) shows them ascending – TDA 6.4p’s evectf() sorts descending, so that is the program’s answer, not the manual’s.

Box 52msvd1 against mevs

A <- matrix(c(1, 0, 0, 5, 7, 0, 1, 0, 1), 3, 3, byrow = TRUE)
S <- tda_mcross(A)
S            # the cross-product matrix the manual's box prints
     [,1] [,2] [,3]
[1,]   27   35    1
[2,]   35   49    0
[3,]    1    0    1
tda_msvd1(S)
$d
[1] 74.6926238609  1.9752562176  0.3321199215

$u
                [,1]          [,2]          [,3]
[1,] -0.591733290081  0.6195362372  0.5157776305
[2,] -0.806093813732 -0.4611140127 -0.3709267188
[3,] -0.008029749235  0.6352548449 -0.7722608401

$v
                [,1]          [,2]          [,3]
[1,] -0.591733290081  0.6195362372  0.5157776305
[2,] -0.806093813732 -0.4611140127 -0.3709267188
[3,] -0.008029749235  0.6352548449 -0.7722608401
tda_mevs(S)
$values
[1] 74.6926238609  1.9752562176  0.3321199215

$vectors
                [,1]          [,2]          [,3]
[1,] -0.591733290081 -0.6195362372 -0.5157776305
[2,] -0.806093813732  0.4611140127  0.3709267188
[3,] -0.008029749235 -0.6352548449  0.7722608401

5.1.4.13 Linear Programming

The manual’s example is mlp(T,X,Y,1): maximise x1 subject to x1 <= 1 and one equality, x2 = 1, which is the last row of T. That is tda_mlp1(), with the equality given as its row.

Box 53mlp with one equality row

# the manual writes the whole problem as one matrix T: the objective in
# the first row, then the inequality, then the equality
Tlp <- matrix(c(1, 0, 0,
                1, 0, 1,
                0, 1, 1), 3, 3, byrow = TRUE)
Tlp
     [,1] [,2] [,3]
[1,]    1    0    0
[2,]    1    0    1
[3,]    0    1    1
tda_mlp1(objective = c(1, 0), constraints = matrix(c(1, 0), 1), bounds = 1,
         equalities = matrix(c(0, 1), 1), equalities_bounds = 1)
$value
[1] 1

$x
[1] 1 1

$y
[1] 1 0

5.1.4.14 Some Further Commands

mproc is a Procrustes rotation: the rotation, scaling and translation that brings one configuration onto another. The two squares below differ by a factor of two, and that is what it recovers.

Box 54mproc, mnum

X <- matrix(c(0, 0, 1, 0, 1, 1, 0, 1), 4, 2, byrow = TRUE)
Y <- matrix(c(0, 0, 2, 0, 2, 2, 0, 2), 4, 2, byrow = TRUE)
tda_mproc(X, Y)
     [,1] [,2]
[1,]    0    0
[2,]    1    0
[3,]    1    1
[4,]    0    1
tda_mnum(3.14159, 6, 3)
[1]  3.14159  9.14159 15.14159

5.2.2 Evaluating Expressions

tda_evalf() evaluates an expression in TDA’s language, with the variables given as options.

Box 55evalf

tda_evalf("sin(x) + cos(x)", options = list(x = 1))$value
[1] 1.381773
sin(1) + cos(1)
[1] 1.381773

5.2.4 Random Numbers

tda_rng() is TDA’s generator – multiplicative congruential, with a fixed seed – so a sequence drawn from it is the sequence TDA drew. rd() is uniform, rdn() normal. tda_runif() is the same generator as a plain vector.

Box 56TDA’s random numbers

g <- tda_rng()
c(g$rd(), g$rd(a = 0, b = 10), g$rdn())
[1]  0.02910383  9.49470177 -0.42916868
tda_runif(5)
[1] 0.02910383 0.94947018 0.09430404 0.70012523 0.89135106

5.2.6.6 Counting Changes

The operators of the expression language that have no R counterpart are reached with tda_derive(): it runs nvar on a data frame with new variables given as TDA expressions and returns the frame with them added. The manual’s example for change and cntch is a small sequence of states per person, in record mode and then in block mode (block = "ID", TDA’s dblock=), where the counts are per person.

Box 57change and cntch, record mode and block mode

seqs <- data.frame(ID = c(1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3),
                   S  = c(1, 3, 2, 2, 3, 3, 1, 2, 2, 2, 1),
                   T  = c(10, 15, 20, 12, 18, 8, 12, 15, 17, 18, 19))
# the manual's five: whether the state changed, how many changes in
# all, the running count, how far back the last change was, and the time
# at that change
tda_derive(seqs, CH = "change(S)", CNT = "cntch(S)", CCNT = "ccntch(S)",
           LAGCH = "lagch(S)", LAGT = "lag(T,-lagch(S))")
   ID S  T CH CNT CCNT LAGCH LAGT
1   1 1 10  1   7    1     0   10
2   1 3 15  1   7    2     0   15
3   1 2 20  1   7    3     0   20
4   2 2 12  0   7    3     1   20
5   2 3 18  1   7    4     0   18
6   3 3  8  0   7    4     1   18
7   3 1 12  1   7    5     0   12
8   3 2 15  1   7    6     0   15
9   3 2 17  0   7    6     1   15
10  3 2 18  0   7    6     2   15
11  3 1 19  1   7    7     0   19
tda_derive(seqs, CH = "change(S)", CNT = "cntch(S)", CCNT = "ccntch(S)",
           LAGCH = "lagch(S)", LAGT = "lag(T,-lagch(S))", block = "ID")
   ID S  T CH CNT CCNT LAGCH LAGT
1   1 1 10  1   3    1     0   10
2   1 3 15  1   3    2     0   15
3   1 2 20  1   3    3     0   20
4   2 2 12  1   2    1     0   12
5   2 3 18  1   2    2     0   18
6   3 3  8  1   4    1     0    8
7   3 1 12  1   4    2     0   12
8   3 2 15  1   4    3     0   15
9   3 2 17  0   4    3     1   15
10  3 2 18  0   4    3     2   15
11  3 1 19  1   4    4     0   19

5.2.6.7 Aggregates

The g operators aggregate within blocks: gcnt counts the block, grec numbers the record within it, gmean and gstd average a variable over it.

Box 58gcnt, grec, gmean, gstd

agg <- data.frame(V = c(1, 4, 5, 3, 6, 2), A = c(1, 1, 1, 2, 7, 7))
# the manual's five: the block's size, the record's number in it,
# the block's number, and whether the record is its first or last
tda_derive(agg, GCNT = "gcnt(A)", GREC = "grec(A)", GSN = "gsn(A)",
           GFIRST = "gfirst(A)", GLAST = "glast(A)")
  V A GCNT GREC GSN GFIRST GLAST
1 1 1    3    1   1      1     0
2 4 1    3    2   1      0     0
3 5 1    3    3   1      0     1
4 3 2    1    1   2      1     1
5 6 7    2    1   3      1     0
6 2 7    2    2   3      0     1
# gmean and gstd average a variable over the block
tda_derive(agg, GM = "gmean(V,A)", GS = "gstd(V,A)", block = "A")
  V A       GM       GS
1 1 1 3.333333 2.081666
2 4 1 3.333333 2.081666
3 5 1 3.333333 2.081666
4 3 2 3.000000 0.000000
5 6 7 4.000000 2.828427
6 2 7 4.000000 2.828427

5.3.3 Evaluating Functions

tda_evalf() evaluates a function; derivatives = 2 is evalf2, the value with the gradient and the Hessian, which TDA computes analytically – the manual’s deriv1.cf.

Box 59evalf2: a function, its gradient and Hessian (deriv1.cf)

tda_evalf("x * y^2", options = list(x = 2, y = 5), derivatives = 2)
<tda_evalf>
value: 50 
gradient: 25 20 
hessian: 2 x 2 matrix
(full TDA output in $output, 23 lines)

5.4.1 Commands for Numerical Integration

int1.cf integrates sqrt(x) over 0..1 with the QNG method, which should give 2/3; the manual’s box shows the approximation and the number of function calls.

Box 60integrate: int1.cf

int1 <- tda_integrate("sqrt(x)", from = 0, to = 1)
int1$value
[1] 0.6666669
# the rest of what TDA reports: the rule, the relative error it worked
# to, and how many times it called the function
c(method = int1$method, rel_error = int1$rel_error, calls = int1$calls)
    method  rel_error      calls 
"1 (QNG)."    "1e-04"       "43" 

5.4.2 Operators for Numerical Integration

int(a, b, f) is the integral as an operator inside an expression, so its limits may be expressions themselves; int2.cf and int3.cf.

Box 61int() inside an expression (int2.cf, int3.cf)

tda_evalf("int(0, 1, sqrt(t))")$value
[1] 0.6666669
tda_evalf("int(x, y * z, 2 * z^2)", options = list(x = 0, y = 2, z = 1.5))$value
[1] 13.5

5.5.1 Moving Averages

The manual’s ds3.cf smooths Y = 1, 3, 7, 6, 6 three times with the weights gss = 0.5, 0.25: with the copy-on end-value rule, with opt = 2 (the end values repeated), and with opt = 2 applied twice (the repeat factor r=2). The three output files are the manual’s Box 3.

Box 62sma: the three smoothings of ds3.cf

y <- c(1, 3, 7, 6, 6)
tda_sma(y, weights = c(0.5, 0.25))$table
  sel index value smoothed
1   0     1     1     1.00
2   0     2     3     3.50
3   0     3     7     5.75
4   0     4     6     6.25
5   0     5     6     6.00
tda_sma(y, weights = c(0.5, 0.25), options = list(opt = 2))$table
  sel index value smoothed
1   0     1     1     1.50
2   0     2     3     3.50
3   0     3     7     5.75
4   0     4     6     6.25
5   0     5     6     6.00
tda_sma(y, weights = c(0.5, 0.25), options = list(opt = 2, r = 2))$table
  sel index value smoothed
1   0     1     1   2.0000
2   0     2     3   3.5625
3   0     3     7   5.3125
4   0     4     6   6.0625
5   0     5     6   6.0625

5.5.2 Running Median Smoothers

A running median is resistant to the single wild value a moving average would spread over its whole window.

The manual’s ds4.cf draws 20 uniform numbers with TDA’s generator and smooths them three ways with the smd operator inside nvar – 3R, 4253Ht and 3RSSHt – then plots the three; tda_derive() is the operator form, tda_smd() the command.

Box 63smd: the three smoothers of ds4.cf

gen <- tda_rng()
ds4 <- data.frame(C = 1:20, A = sapply(1:20, function(i) gen$rd()))
ds4 <- tda_derive(ds4, M1 = "smd[sm=[3R]](A)", M2 = "smd[sm=[4253Ht]](A)",
                  M3 = "smd[sm=[3RSSHt]](A)")
head(ds4)
  C          A         M1         M2         M3
1 1 0.02910383 0.02910383 0.02910383 0.02910383
2 2 0.94947018 0.09430404 0.28925601 0.11060409
3 3 0.09430404 0.70012522 0.53366834 0.25730455
4 4 0.70012523 0.70012522 0.62828529 0.53050137
5 5 0.89135106 0.70012522 0.57828712 0.70215678
6 6 0.47205962 0.47205961 0.46866766 0.51609015

Box 64smd: ds4.cf, drawn

p <- tda_ps(ds4, width = 90, height = 50, xlim = c(0, 21), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 1)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_points(p, "C", "M1", symbol = 5, size = 1)  # ds4.cf marks M1
p <- tda_pl_lines(p, "C", "M1")
p <- tda_pl_lines(p, "C", "M2", lty = 5)
plot(tda_pl_lines(p, "C", "M3", lty = 8))

5.5.3 Smoothing Splines

sig= is the smoothing parameter and deg= the degree; the derivatives come back alongside the fit.

The manual’s ds5.cf is a sine curve with uniform noise, 100 points, smoothed with sig = 1, and plotted against the curve.

Box 65spl: ds5.cf

gen <- tda_rng()
ds5 <- data.frame(X = (1:100) / 10)
ds5$Z <- sin(ds5$X)
ds5$Y <- ds5$Z + sapply(1:100, function(i) gen$rd(a = -0.2, b = 0.2))
sp5 <- tda_spl(ds5$X, ds5$Y, sig = 1)
head(sp5$table)
  index   x           y     fitted        d1         d2
1     1 0.1 -0.08852505 0.01562586 1.5306227 -1.5167364
2     2 0.2  0.37845740 0.16110445 1.3789490 -1.5167364
3     3 0.3  0.13324182 0.29141567 1.2272754 -1.5167364
4     4 0.4  0.46946844 0.40655953 1.0756018 -1.5167364
5     5 0.5  0.63596596 0.50653602 0.9239281 -1.5167364
6     6 0.6  0.55346632 0.59134515 0.7722545 -0.2058181

ds5.cf then draws the data, the true sine and the smoother’s fitted values.

Box 66spl: ds5.cf, drawn

p <- tda_ps(ds5, width = 90, height = 50, xlim = c(0, 10), ylim = c(-1.5, 1.5))
p <- tda_pl(p, "plxa", sc = 1)
p <- tda_pl(p, "plya", sc = 0.5, ic = 5)
p <- tda_pl_points(p, "X", "Y", symbol = 5, size = 0.5)
p <- tda_pl_lines(p, "X", "Z", lty = 5)          # the sine the noise hides
plot(tda_pl_lines(p, x = sp5$table$x, y = sp5$table$fitted))

5.6.1 The fmin Command

The manual’s fmin1.cf minimises (x - 5)^2 + (y + 6)^2 from the starting values 0, 0; its box shows convergence in two iterations to x = 5, y = -6.

Box 67fmin: fmin1.cf

tda_minimize("(x - 5)^2 + (y + 6)^2")
Call: tda_minimize("(x - 5)^2 + (y + 6)^2")

Value: 0 

 Idx Parameter Value Error Value_E Signif
   1         x     5    NA      NA     NA
   2         y    -6    NA      NA     NA

fmin2.cf minimises a function of the data, the sum of (x - A)^2 over A = 1, …, 101 – fmin with v = A. With data, fmin is tda_freg()’s nonlinear least squares; the minimum is at x = 51.

Box 68fmin with data: fmin2.cf

tda_freg({ fn = (x - A)^2 }, data = data.frame(A = 1:101))
Call: tda_freg(definitions = {
    fn = (x - A)^2
}, data = data.frame(A = 1:101))

Cases: 101
Converged in 2 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1         x 51.0000 2.9300 17.4060 1.0000

6 Statistical Procedures

6.2.1 Mean and Range of Variables

tda_dstat() is the dstat command: mean, standard deviation and range for every variable it is given. The data files of chapter 6 are read straight from the package.

Box 69dstat on ds1.dat

ds1 <- read.table(ex("ds1.dat"), col.names = c("X1", "X2", "X3", "X4"))
tda_dstat(ds1)
Call: tda_dstat(ds1)

Cases: 4 

 Variable Minimum Maximum      Mean   Std.Dev.     Sum
       X1   1e+00     4.0  2.500000 1.29099445 10.0000
       X2   1e+00     7.0  4.000000 3.46410162 16.0000
       X3  -1e+00     2.0 -0.250000 1.50000000 -1.0000
       X4   1e-04     0.1  0.027775 0.04835703  0.1111

6.2.2 Empirical Distribution Functions

tda_gdf() estimates the distribution function, and what= asks for the survivor function or the expected values instead. The manual’s gdf1.dat holds three small examples told apart by its first column (SEL, TDA’s tsel): exact observations (Box 3), right-censored ones with cen= (Boxes 4 and 5), and a two-dimensional one with grp=.

Box 70gdf: exact data (the manual’s Box 3)

gdf1 <- read.table(ex("gdf1.dat"))
names(gdf1) <- c("SEL", "ID", "L1", "YL", "DELTA")
ex0 <- gdf1[gdf1$SEL == 0, ]
tda_gdf(~YL, data = ex0)$table
  dimension value distribution_function
1         1    -1             0.1666667
2         1     1             0.3333333
3         1     5             0.5000000
4         1     7             0.8333333
5         1     9             1.0000000
tda_gdf(~YL, data = ex0, what = "survivor")$table
  dimension value survivor_function
1         1    -1      8.333333e-01
2         1     1      6.666667e-01
3         1     5      5.000000e-01
4         1     7      1.666667e-01
5         1     9      1.110223e-16
tda_gdf(~YL, data = ex0, what = "expected_values")$table
  dimension  y exact expected
1         1  1     1        1
2         1  7     1        7
3         1 -1     1       -1
4         1  5     1        5
5         1  7     1        7
6         1  9     1        9

TDA’s cen= variable, DELTA in the manual, is 1 for an exact observation; censor= here is the other way round, 1 (or TRUE) for a censored one, so the manual’s indicator is passed negated.

Box 71gdf: right censored data (Boxes 4 and 5)

ex1 <- gdf1[gdf1$SEL == 1, ]
tda_gdf(~YL, data = ex1, censor = ex1$DELTA == 0)$table
  dimension value distribution_function
1         1     1             0.1666667
2         1     3             0.3750000
3         1     4             0.6875000
4         1     5             1.0000000
tda_gdf(~YL, data = ex1, censor = ex1$DELTA == 0, what = "expected_values")$table
  dimension y exact expected
1         1 3     1    3.000
2         1 3     0    4.500
3         1 2     0    4.125
4         1 1     1    1.000
5         1 4     1    4.000
6         1 5     1    5.000

The third block of gdf1.dat is the two-dimensional case: four units, each observed in two dimensions. id= and dimension= are TDA’s grp=, and method= its opt= – "joint1" for the joint distribution over the observed points.

Box 72gdf: the joint distribution (Box 6)

ex2 <- gdf1[gdf1$SEL == 2, ]
tda_gdf(~YL, data = ex2, id = ex2$ID, dimension = ex2$L1,
        method = "joint1")$table
  pattern margin1 margin2 value1 value2 distribution_function
1       1       1       2     -1    1.0                  0.25
2       1       1       2      1    2.0                  0.50
3       1       1       2      2    1.0                  0.50
4       1       1       2      3    1.5                  0.75
tda_gdf(~YL, data = ex2, id = ex2$ID, dimension = ex2$L1,
        method = "joint1", what = "survivor")$table
  pattern margin1 margin2 value1 value2 survivor_function
1       1       1       2      3    1.5              0.00
2       1       1       2      2    1.0              0.25
3       1       1       2      1    2.0              0.00
4       1       1       2     -1    1.0              0.50

With censoring the same estimator needs a grid: n_boxes= is TDA’s n=, the number of boxes per dimension, and sc= the scaling. The data are Pruitt’s, the manual’s Box 8.

Box 73gdf: joint distribution, censored (Boxes 9 and 10)

pruitt <- read.table(ex("gdf2.dat"))
names(pruitt) <- c("ID", "L1", "Y", "CEN")
tda_gdf(~Y, data = pruitt, censor = pruitt$CEN == 0, id = pruitt$ID,
        dimension = pruitt$L1, method = "joint1",
        n_boxes = 64, options = list(sc = 0.5))$table
  pattern margin1 margin2 value1 value2 distribution_function
1       1       1       2      1      6          4.333310e-07
2       1       1       2      2      4          3.960414e-05
3       1       1       2      3      5          7.920829e-05
4       1       1       2      4      3          1.250000e-01
5       1       1       2      5      2          5.872780e-08
6       1       1       2      6      7          6.289940e-01
7       1       1       2      7      1          3.830525e-10
8       1       1       2      8      8          1.000000e+00
tda_gdf(~Y, data = pruitt, censor = pruitt$CEN == 0, id = pruitt$ID,
        dimension = pruitt$L1, method = "joint1", n_boxes = 64,
        options = list(sc = 0.5), what = "expected_values")$table
  pattern margin1 margin2 observed1 observed2 exact1 exact2 expected1 expected2
1       1       1       2         1         6      0      1  5.619719  6.000000
2       1       1       2         2         4      0      1  5.583929  4.000000
3       1       1       2         3         5      0      0  6.610054  6.984936
4       1       1       2         4         3      1      1  4.000000  3.000000
5       1       1       2         5         2      1      0  5.000000  5.156116
6       1       1       2         6         7      1      1  6.000000  7.000000
7       1       1       2         7         1      0      0  7.849248  7.154006
8       1       1       2         8         8      1      1  8.000000  8.000000

The manual’s Box 12 sets the two joint methods against each other on a hundred generated units. tda_rng() is TDA’s generator, so the data come out the same here as in gdf3.cf.

Both methods reproduce the box: 37.3177, 38.0035 and 46.2348 for method 2, and 37.6603, 37.6603 and 46.2104 for method 1.

A few of the later rows differ in the last digit or two – 103.47752 here against the manual’s 103.4777. gdf3.cf writes its generated data to a file and gdf4.cf reads it back at [10.6], so its input is rounded to six decimals; this passes R’s doubles straight to TDA. The deviance in 6.15.2.1 differs for the same reason.

Box 74gdf: the two joint methods (Box 12)

gen <- tda_rng()
X <- 1:100
Y <- X + sapply(X, function(i) gen$rd(a = -10, b = 10))
RD1 <- sapply(X, function(i) gen$rd())
RD2 <- sapply(X, function(i) gen$rd(a = 10, b = 90))
IS <- RD1 <= 0.2 & Y > RD2
two <- rbind(data.frame(ID = X, L1 = 1L, Y1 = as.numeric(X), CEN = 1L),
             data.frame(ID = X, L1 = 2L, Y1 = ifelse(IS, RD2, Y),
                        CEN = as.integer(!IS)))
ev <- function(m)
    tda_gdf(~Y1, data = two, censor = two$CEN == 0, id = two$ID,
            dimension = two$L1, method = m, what = "expected_values")$table
j1 <- ev("joint1")
j2 <- ev("joint2")

# the box lists every censored unit, the thirteen whose second
# dimension had to be estimated
i <- which(j1$exact2 == 0)
data.frame(j1[i, c("observed1", "observed2", "exact1", "exact2")],
           joint1 = j1$expected2[i], joint2 = j2$expected2[i],
           row.names = NULL)
   observed1 observed2 exact1 exact2   joint1    joint2
1         34  27.34643      1      0 37.66034  37.31767
2         36  30.39963      1      0 37.66034  38.00346
3         43  15.35934      1      0 46.21040  46.23480
4         45  43.53655      1      0 48.92635  48.47346
5         49  50.67826      1      0 54.37489  51.91668
6         68  50.51117      1      0 61.43484  71.75636
7         69  47.40821      1      0 61.43484  71.75636
8         73  49.90086      1      0 74.52141  73.02770
9         75  65.25909      1      0 74.52141  73.79130
10        79  38.74952      1      0 74.52141  79.16228
11        84  53.56255      1      0 82.37462  83.42253
12        85  62.95928      1      0 82.37462  85.37421
13        93  33.88362      1      0 99.94734 103.47752

gdf3.cf does not run to completion through the real TDA binary, so it has no box here.

6.2.3 Quantiles

Box 75quant

tda_quant(cars)
Call: tda_quant(cars)

Cases: 50 

       p10  p20  p25  p30 p40 p50 p60  p70   p75  p80  p90
speed  8.1 11.0 12.0 12.3  14  15  17 18.7 19.25 20.0 23.9
dist  14.2 20.4 25.5 26.0  32  36  46 54.0 57.00 65.6 83.6

6.2.4 Frequency Tables

tda_freq() is the one-way table, tda_freq2() the two-way one.

Box 76freq and freq2

tda_freq(ds1[c("X1", "X2", "X3")])
Call: tda_freq(ds1[c("X1", "X2", "X3")])

Cases: 4 

 index X1 X2 X3 count percent cum_count cum_percent
     1  1  1 -1     1      25         1          25
     2  2  7 -1     1      25         2          50
     3  3  1 -1     1      25         3          75
     4  4  7  2     1      25         4         100
tda_freq2(ds1$X2, ds1$X1)
Call: tda_freq2(ds1$X2, ds1$X1)

Frequency|
Percent  |
Row Pct  |
Col Pct  |       1 |       2 |       3 |       4 |   Total
----------------------------------------------------------
       1 |    1.00 |    0.00 |    1.00 |    0.00 |    2.00
         |   25.00 |    0.00 |   25.00 |    0.00 |   50.00
         |   50.00 |    0.00 |   50.00 |    0.00 |
         |  100.00 |    0.00 |  100.00 |    0.00 |
----------------------------------------------------------
       7 |    0.00 |    1.00 |    0.00 |    1.00 |    2.00
         |    0.00 |   25.00 |    0.00 |   25.00 |   50.00
         |    0.00 |   50.00 |    0.00 |   50.00 |
         |    0.00 |  100.00 |    0.00 |  100.00 |
----------------------------------------------------------
Total         1.00      1.00      1.00      1.00      4.00
             25.00     25.00     25.00     25.00    100.00

6.2.5 Aggregated Tables

tda_atab() groups a variable into classes and aggregates a second one over them.

Box 77atab

tda_atab(ds1[c("X3", "X4")], breaks = seq(-10, 10, 0.5))
Call: tda_atab(ds1[c("X3", "X4")], breaks = seq(-10, 10, 0.5))

Cases: 4 

 lower upper count weighted mean_X3 mean_X4
  -1.0  -0.5     3        3      -1  0.0370
  -0.5   0.0     0        0       0  0.0000
   0.0   0.5     0        0       0  0.0000
   0.5   1.0     0        0       0  0.0000
   1.0   1.5     0        0       0  0.0000
   1.5   2.0     0        0       0  0.0000
   2.0   2.5     1        1       2  0.0001

6.2.6 Covariance and Correlation

Box 78corr

tda_corr(ds1)
Call: tda_corr(ds1)

Cases: 4 

           X1         X2         X3         X4
X1  1.0000000  0.4472136  0.7745967 -0.8241407
X2  0.4472136  1.0000000  0.5773503 -0.5426423
X3  0.7745967  0.5773503  1.0000000 -0.3815371
X4 -0.8241407 -0.5426423 -0.3815371  1.0000000

6.2.7 Contingency Measures

The same two-way table as in 6.2.4, with contingency = TRUE adding the measures of association.

Box 79freq2 with contingency measures

tda_freq2(ds1$X2, ds1$X1, contingency = TRUE)
Call: tda_freq2(ds1$X2, ds1$X1, contingency = TRUE)

Frequency|
Percent  |
Row Pct  |
Col Pct  |       1 |       2 |       3 |       4 |   Total
----------------------------------------------------------
       1 |    1.00 |    0.00 |    1.00 |    0.00 |    2.00
         |   25.00 |    0.00 |   25.00 |    0.00 |   50.00
         |   50.00 |    0.00 |   50.00 |    0.00 |
         |  100.00 |    0.00 |  100.00 |    0.00 |
----------------------------------------------------------
       7 |    0.00 |    1.00 |    0.00 |    1.00 |    2.00
         |    0.00 |   25.00 |    0.00 |   25.00 |   50.00
         |    0.00 |   50.00 |    0.00 |   50.00 |
         |    0.00 |  100.00 |    0.00 |  100.00 |
----------------------------------------------------------
Total         1.00      1.00      1.00      1.00      4.00
             25.00     25.00     25.00     25.00    100.00

----------------------------------------------------------------------------

Contingency measures for table: X (Vds1_X2) x Y (Vds1_X1)

Number of cells with less than 5 elements:   8 (100.0 %)
Cells with expected frequency less than 5:   8 (100.0 %)
Degrees of freedom: 3

Chi-square (Pearson)               4.0000
Prob:                              0.2615
Chi-square (likelihood ratio)      5.5452
Prob:                              0.1360
Phi                                1.0000
Cramer's V                         1.0000
Contingency coefficient            0.7071
Lambda (X dependent)               1.0000
Lambda (Y dependent)               0.3333
Lambda (symmetric)                 0.6000
Goodman/Kruskal Tau (X dependent)  1.0000
Goodman/Kruskal Tau (Y dependent)  0.3333
Uncertainty coeff (X dependent)    1.0000
Uncertainty coeff (Y dependent)    0.5000
Kendall's tau-b                    0.4082
Kendall's tau-c                    0.5000
Gamma                              0.5000
Somers' D (X dependent)            0.3333
Somers' D (Y dependent)            0.5000

6.2.8 Scatterplots

The points come from TDA’s random number generator, tda_rng(), so that the picture is the manual’s and not one of R’s.

Box 80Points from TDA’s generator

gen <- tda_rng()
n <- 200
X <- Y <- numeric(n)
for (i in seq_len(n)) {
    X[i] <- gen$rd(a = 0, b = 3)
    Y[i] <- sin(X[i]) + gen$rd()
}
df <- data.frame(x = X, y = Y)

Box 81A scatterplot

p <- tda_ps(data = df, width = 90, height = 50, xlim = c(0, 3), ylim = c(-1, 2.5))
p <- tda_pl_scatter(p, "x", "y", symbol = 5, size = 1)
p <- tda_pl_axes(p, sc = c(1), ic = c(10, 0))
plot(p)

Example 1 A sunflower plot bins the same points and draws a petal per point in the bin, which is what to reach for when the points overlap.

Box 82A sunflower plot

p <- tda_ps(data = df, width = 90, height = 50, xlim = c(0, 3), ylim = c(-1, 2.5))
p <- tda_pl_scatter(p, "x", "y", type = "sunflower", grid = c(15, 10), size = 3)
p <- tda_pl_axes(p, sc = c(1), ic = c(10, 1))
plot(p)

Example 2 type = "lowess" adds the smooth.

Box 83A scatterplot with a lowess smooth

p <- tda_ps(data = df, width = 90, height = 50, xlim = c(0, 3), ylim = c(-1, 2.5))
p <- tda_pl_scatter(p, "x", "y", symbol = 5, size = 1, type = "lowess")
p <- tda_pl(p, "plxa", sc = 1, ic = 10)
p <- tda_pl(p, "plya", sc = 1, ic = 0)
plot(p)

6.2.9 Histogram Plots

Box 84A histogram (dh1.cf)

dh1 <- read.table(ex("dh1.dat"))
p <- tda_ps(data = dh1, width = 90, height = 50, xlim = c(0, 8), ylim = c(0, 0.4))
p <- tda_pl(p, "plxa", sc = 1)
p <- tda_pl(p, "plya", sc = 0.1)
p <- tda_pl_histogram(p, x = "V1", breaks = c(0, 2, 4, 6, 8))
plot(p)

6.2.10 Density Plots

The same data smoothed with a triangular kernel.

Box 85A kernel density estimate (dh2.cf)

p <- tda_ps(data = dh1, width = 90, height = 50, xlim = c(0, 8), ylim = c(0, 0.6))
p <- tda_pl(p, "plxa", sc = 1)
p <- tda_pl(p, "plya", sc = 0.1)
p <- tda_pl_density(p, x = "V1", kernel = "triangle", at = seq(0, 8, 0.1))
plot(p)

6.4.2 Inequality Measures

Box 86ineq

tda_ineq(ds1)
Call: tda_ineq(ds1)

Cases: 4 

 index cases minimum maximum     mean         sd    vcoeff      gini
    X1     4   1e+00     4.0 2.500000 1.29099445 0.5163978 0.3333333
    X2     4   1e+00     7.0 4.000000 3.46410162 0.8660254 0.5000000
    X3     1   2e+00     2.0 2.000000 0.00000000 0.0000000 0.0000000
    X4     4   1e-04     0.1 0.027775 0.04835703 1.7410272 0.9261926

6.4.3 Segregation Indices

Box 87segr

ds2 <- read.table(ex("ds2.dat"))
names(ds2) <- c("G", paste0("X", 1:4))
tda_segr(ds2, group = "G")
Call: tda_segr(data = ds2, group = "G")

Cases: 

 variable classes cases group0 group1   d_index   v_ratio      gini
       X1       2     7      3      4 1.0000000 1.0000000 1.0000000
       X2       7     7      3      4 1.0000000 1.0000000 1.0000000
       X3       3     6      3      3 0.0000000 0.0000000 0.0000000
       X4       2     7      3      4 0.4166667 0.1736111 0.4166667

6.5.1 Life Tables

tda_ltb() is the ltb command, and tp= gives the interval boundaries.

Box 88A life table, 30-month intervals

lt <- tda_ltb(Surv(TFP, DES) ~ 1, data = d, tp = seq(0, 500, 30))
lt$summary
  sn origin cases weighted   median
1  1      0   600      600 47.27858
lt$table
   start midpoint entering censored exposed events       prob
1      0       15      600       28   586.0    223 0.38054608
2     30       45      349       23   337.5    113 0.33481481
3     60       75      213       15   205.5     51 0.24817518
4     90      105      147       16   139.0     25 0.17985612
5    120      135      106       15    98.5     24 0.24365482
6    150      165       67        5    64.5      9 0.13953488
7    180      195       53        9    48.5      4 0.08247423
8    210      225       40        5    37.5      3 0.08000000
9    240      255       32        5    29.5      0 0.00000000
10   270      285       27        7    23.5      2 0.08510638
11   300      315       18        5    15.5      2 0.12903226
12   330      345       11        1    10.5      2 0.19047619
13   360      375        8        3     6.5      0 0.00000000
14   390      405        5        4     3.0      0 0.00000000
15   420      435        1        1     0.5      0 0.00000000
# ltb.1 holds a second block: the estimates themselves, with their
# standard errors
lt$survivor
   start midpoint   survivor survivor_se      density   density_se        rate     rate_se
1      0       15 1.00000000  0.00000000 0.0126848692 0.0006685564 0.015665613 0.001019673
2     30       45 0.61945392  0.02005669 0.0069134117 0.0005757229 0.013404508 0.001235237
3     60       75 0.41205157  0.02076559 0.0034086992 0.0004481030 0.009444444 0.001309149
4     90      105 0.30979060  0.01994731 0.0018572578 0.0003570178 0.006587615 0.001311075
5    120      135 0.25407287  0.01922194 0.0020635360 0.0003982041 0.009248555 0.001869599
6    150      165 0.19216679  0.01822472 0.0008937990 0.0002890738 0.005000000 0.001661973
7    180      195 0.16535282  0.01773858 0.0004545782 0.0002231094 0.002867384 0.001432365
8    210      225 0.15171547  0.01753725 0.0004045746 0.0002288721 0.002777778 0.001602358
9    240      255 0.13957823  0.01747828 0.0000000000           NA 0.000000000          NA
10   270      285 0.13957823  0.01747828 0.0003959666 0.0002723627 0.002962963 0.002093061
11   300      315 0.12769923  0.01789568 0.0005492440 0.0003705354 0.004597701 0.003243325
12   330      345 0.11122191  0.01900462 0.0007061709 0.0004651940 0.007017544 0.004934585
13   360      375 0.09003679  0.02045360 0.0000000000           NA 0.000000000          NA
14   390      405 0.09003679  0.02045360 0.0000000000           NA 0.000000000          NA
15   420      435 0.09003679  0.02045360 0.0000000000           NA 0.000000000          NA

tda_survivor() reads the estimate back as a data frame carrying the survivor function, the density and the rate, which is what the plotting commands take.

Box 89The estimate as a data frame (ple1.cf)

tda_survivor(lt)
   group time   survivor    std.err      density        rate      lower     upper
1      1    0 1.00000000 0.00000000 0.0126848692 0.015665613 1.00000000 1.0000000
2      1   30 0.61945392 0.02005669 0.0069134117 0.013404508 0.58014353 0.6587643
3      1   60 0.41205157 0.02076559 0.0034086992 0.009444444 0.37135177 0.4527514
4      1   90 0.30979060 0.01994731 0.0018572578 0.006587615 0.27069459 0.3488866
5      1  120 0.25407287 0.01922194 0.0020635360 0.009248555 0.21639855 0.2917472
6      1  150 0.19216679 0.01822472 0.0008937990 0.005000000 0.15644700 0.2278866
7      1  180 0.16535282 0.01773858 0.0004545782 0.002867384 0.13058584 0.2001198
8      1  210 0.15171547 0.01753725 0.0004045746 0.002777778 0.11734310 0.1860878
9      1  240 0.13957823 0.01747828 0.0000000000 0.000000000 0.10532142 0.1738350
10     1  270 0.13957823 0.01747828 0.0003959666 0.002962963 0.10532142 0.1738350
11     1  300 0.12769923 0.01789568 0.0005492440 0.004597701 0.09262435 0.1627741
12     1  330 0.11122191 0.01900462 0.0007061709 0.007017544 0.07397354 0.1484703
13     1  360 0.09003679 0.02045360 0.0000000000 0.000000000 0.04994847 0.1301251
14     1  390 0.09003679 0.02045360 0.0000000000 0.000000000 0.04994847 0.1301251
15     1  420 0.09003679 0.02045360 0.0000000000 0.000000000 0.04994847 0.1301251

6.5.2 Kaplan-Meier Procedure

tda_ple() is the product-limit estimator, tda_km() an alias for it. Case weights are TDA’s cwt command, and here a column name.

Box 90Product-limit estimation with case weights

rrdat2 <- read.table(ex("rrdat.2"))
names(rrdat2) <- c("DUR", "CEN", "WT", "GRP")
fit <- tda_ple(Surv(DUR, CEN) ~ as.factor(GRP), data = rrdat2, weights = "WT")
fit$blocks
$`0`
  id index time events censored n.risk  survivor    std.err  cum.rate
1  0     0    0      0        0     21 1.0000000 0.00000000 0.0000000
2  0     1    6      3        0     21 0.8571429 0.07636035 0.1541507
3  0     2    7      1        1     17 0.8067227 0.08693529 0.2147753
4  0     3   10      1        1     15 0.7529412 0.09634965 0.2837682
5  0     4   13      1        2     12 0.6901961 0.10681471 0.3707796
6  0     5   16      1        0     11 0.6274510 0.11405387 0.4660897
7  0     6   22      1        3      7 0.5378151 0.12823375 0.6202404
8  0     7   23      1        0      6 0.4481793 0.13459146 0.8025620
9  0     8   35      0        5     NA        NA         NA        NA

$`1`
   id index time events censored n.risk   survivor    std.err  cum.rate
1   1     0    0      0        0     21 1.00000000 0.00000000 0.0000000
2   1     1    1      2        0     21 0.90476190 0.06405645 0.1000835
3   1     2    2      2        0     19 0.80952381 0.08568909 0.2113091
4   1     3    3      1        0     17 0.76190476 0.09294286 0.2719337
5   1     4    4      2        0     16 0.66666667 0.10286890 0.4054651
6   1     5    5      2        0     14 0.57142857 0.10798985 0.5596158
7   1     6    8      4        0     12 0.38095238 0.10597117 0.9650809
8   1     7   11      2        0      8 0.28571429 0.09858079 1.2527630
9   1     8   12      2        0      6 0.19047619 0.08568909 1.6582281
10  1     9   15      1        0      4 0.14285714 0.07636035 1.9459101
11  1    10   17      1        0      3 0.09523810 0.06405645 2.3513753
12  1    11   22      1        0      2 0.04761905 0.04647143 3.0445224
13  1    12   23      1        0      1 0.00000000        NaN       NaN

ple2.cf is the estimate on the job data with the single destination state; the manual’s Box 5 shows the first rows of its output file.

Box 91ple2.cf: the job data, one destination

ple2 <- tda_ple(Surv(TFP, DES) ~ 1, data = d)

# the manual prints part of ple.2: its first rows, a slice from the
# middle, and its last
b <- ple2$blocks[[1L]]
b[c(1:8, 41:46, 128:132), ]
    id index time events censored n.risk   survivor     std.err    cum.rate
1    0     0    0      0        0    600 1.00000000 0.000000000 0.000000000
2    0     1    2      2        0    600 0.99666667 0.002353091 0.003338901
3    0     2    3      5        1    597 0.98831937 0.004389012 0.011749380
4    0     3    4      9        2    590 0.97324332 0.006599178 0.027121160
5    0     4    5      3        0    581 0.96821796 0.007174619 0.032298048
6    0     5    6     10        1    577 0.95143776 0.008796359 0.049781011
7    0     6    7      9        0    567 0.93633557 0.009993927 0.065781352
8    0     7    8      6        1    557 0.92624937 0.010700980 0.076611783
41   0    40   41      3        1    277 0.50584708 0.020924866 0.681520865
42   0    41   42      1        1    273 0.50399416 0.020930097 0.685190594
43   0    42   43      2        0    272 0.50028832 0.020939593 0.692570702
44   0    43   44      5        1    269 0.49098928 0.020959274 0.711332978
45   0    44   45      1        1    263 0.48912240 0.020962559 0.715142506
46   0    45   46      2        0    262 0.48538864 0.020968140 0.722805379
128  0   127  312      1        3     16 0.11980376 0.018459344 2.121900191
129  0   128  326      1        1     14 0.11124635 0.019021207 2.196008163
130  0   129  332      1        2     11 0.10113305 0.019798847 2.291318343
131  0   130  350      1        1      9 0.08989604 0.020541766 2.409101379
132  0   131  428      0        8     NA         NA          NA          NA
# the table ends with the last observation, where only censoring
# happened: TDA writes no estimate there, so those columns are NA
tail(ple2$blocks[[1]], 2)
    id index time events censored n.risk   survivor    std.err cum.rate
131  0   130  350      1        1      9 0.08989604 0.02054177 2.409101
132  0   131  428      0        8     NA         NA         NA       NA

ple3.cf has the four destination states (Example 3): the estimate is per transition, one sub-survivor function for each of the upward, lateral and downward job changes, and $summary holds one row for each. ple3p.cf plots the three: tda_survivor() gives the estimated functions as a data frame, and each transition is selected by its origin and destination.

Box 92Sub-survivor functions, four destination states

ple <- tda_ple(Surv(TFP, DES) ~ 1, data = d4)
ple$summary
  transition   median limit cases weighted
1        0,1       NA    NA   600      600
2        0,2 122.2409   350   600      600
3        0,3 215.9553   332   600      600

Box 93One survivor function per destination state

srv <- tda_survivor(ple)
p <- tda_ps(srv, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "0,1", lty = 1)
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "0,2", lty = 5)
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "0,3", lty = 8)
plot(p)

6.5.3 Survivor Function Quantiles

at_survival= is TDA’s qo=: survival probabilities, in decreasing order, and the time each is reached at, one row per transition and probability. A transition whose survivor function never drops to a probability has no row for it, which is why the three blocks are of different length. (quantiles= is the other direction, qt=: time points, and the survivor function reached at each.)

Box 94Quantiles of the survivor function

tda_ple(Surv(TFP, DES) ~ 1, data = d4,
        at_survival = seq(0.9, 0.1, by = -0.1))$quantiles
   sn org des survivor  quantile
1   1   0   1      0.9  30.03523
2   1   0   1      0.8  85.06017
3   1   0   1      0.7 233.87848
4   1   0   2      0.9  16.87488
5   1   0   2      0.8  27.30904
6   1   0   2      0.7  41.61867
7   1   0   2      0.6  73.19972
8   1   0   2      0.5 122.24094
9   1   0   2      0.4 208.56549
10  1   0   3      0.9  22.67992
11  1   0   3      0.8  47.12627
12  1   0   3      0.7  75.75391
13  1   0   3      0.6 131.53781
14  1   0   3      0.5 215.95529
15  1   0   3      0.4 321.98809

6.5.4 Comparing Survivor Functions

compare = TRUE runs TDA’s four tests of whether the group survivor functions differ. The manual’s example, ple6.cf, is Kalbfleisch and Prentice’s two-group data (rrdat.3): 40 cases, survival time, status and group.

Box 95ple6.cf: comparing two survivor functions

rrdat3 <- read.table(ex("rrdat.3"), col.names = c("TF", "Status", "Group"))
tda_ple(Surv(TF, Status) ~ as.factor(Group), data = rrdat3,
        compare = TRUE)$comparison
  sn org des                   test statistic df    signif
1  1   0   1      Log-Rank (Savage)  3.122712  1 0.9227918
2  1   0   1     Wilcoxon (Breslow)  2.651042  1 0.8965180
3  1   0   1 Wilcoxon (Tarone-Ware)  2.976660  1 0.9155266
4  1   0   1    Wilcoxon (Prentice)  3.001409  1 0.9168079

The same four tests on the job data by sex, whose survivor functions the next box draws:

Box 96Men and women

pl2 <- tda_ple(Surv(TFP, DES) ~ as.factor(SEX), data = d, compare = TRUE)
pl2$comparison
  sn org des                   test statistic df    signif
1  1   0   1      Log-Rank (Savage) 20.603209  1 0.9999943
2  1   0   1     Wilcoxon (Breslow)  9.364723  1 0.9977880
3  1   0   1 Wilcoxon (Tarone-Ware) 14.326703  1 0.9998463
4  1   0   1    Wilcoxon (Prentice) 10.695087  1 0.9989258

The bands are Greenwood’s, and band= shades them. They widen where few are still at risk, so the manual cuts the plot at 290 months.

Box 97Survivor functions with confidence bands

sb <- tda_survivor(pl2)
sb <- sb[sb$time < 290, ]
p <- tda_ps(sb, width = 90, height = 50, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "1",
                  band = c("upper", "lower"), lty = 1)
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "2",
                  band = c("upper", "lower"), lty = 5)
p <- tda_pl(p, "plxa", sc = 60, ic = 1)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_frame(p)
plot(p)

6.5.5 State Distributions

tda_state_dist() is epsdat: at each time point, how many episodes are in each state. The manual’s ed5.cf runs it on the small multi-state file ed1.dat; with the origin state on the left-hand side (Surv(ts, tf, org, des)) the counts are those of its Box 2, with the file’s Total and Missing columns.

Box 98epsdat: state distributions (ed5.cf)

ed1 <- read.table(ex("ed1.dat"),
                  col.names = c("ID", "SN", "ORG", "DES", "TS", "TF", "S1", "S2"))
tda_state_dist(Surv(TS, TF, ORG, DES) ~ 1, ed1, times = seq(0, 30, 5))
  time state1 state2 state3 total missing
1    0      1      1      0     2       3
2    5      1      1      0     2       3
3   10      0      2      0     2       3
4   15      0      1      1     2       3
5   20      1      0      1     2       3
6   25      1      0      1     2       3
7   30      1      0      0     1       4
# Box 2's second table: the same file read as multi-episode. Naming the
# id (and the spell number) puts a case's later spells with it instead
# of counting them as missing.
tda_state_dist(Surv(TS, TF, ORG, DES) ~ 1, ed1, times = seq(0, 30, 5),
               id = "ID", spell = "SN")
  time state1 state2 state3 total missing
1    0      1      1      0     2       0
2    5      1      1      0     2       0
3   10      0      2      0     2       0
4   15      0      1      1     2       0
5   20      1      0      1     2       0
6   25      1      0      1     2       0
7   30      1      0      0     1       1

6.6.1 Sequence Length and Gaps

Sequence data are one row per case, one column per position.

Box 99seqlg: length and gaps

seqd1 <- read.table(ex("seq.d1"))
names(seqd1) <- c("ID", paste0("Y", seq_len(ncol(seqd1) - 1L)))
tda_seqlg(seqd1, id = "ID")
  case ts tf slen glen gmin gmax ngap id
1    1  0  7    8    0    0    0    0  1
2    2  0  7    8    2    2    2    1  2
3    3  1  7    7    1    1    1    1  3
4    4  0  5    6    0    0    0    0  4
5    5  0  7    8    0    0    0    0  5

6.6.2 General Characteristics

Box 100seqgc

tda_seqgc(seqd1, id = "ID")
  case slen nds nev dur1 dur3 dur7 durm nep1 nep3 nep7 nepm id
1    1    8   3   4    1    4    3    0    1    2    2    0  1
2    2    8   3   4    2    3    1    2    1    2    1    1  2
3    3    7   2   2    4    2    0    1    1    1    0    1  3
4    4    6   2   1    2    4    0    0    1    1    0    0  4
5    5    8   3   4    1    4    3    0    1    2    2    0  5

6.6.3 State Distributions

Box 101seqsd and seqen

tda_seqsd(seqd1[, -1])
  time nst1 nst3 nst7 valid nmiss total
1    0    1    1    2     4     1     5
2    1    1    4    0     5     0     5
3    2    0    4    1     5     0     5
4    3    3    1    0     4     1     5
5    4    2    3    0     5     0     5
6    5    1    3    0     4     1     5
7    6    1    0    2     3     2     5
8    7    1    1    2     4     1     5
tda_seqen(seqd1[, -1])
  time n       ent
1    0 4 1.0397208
2    1 5 0.5004024
3    2 5 0.5004024
4    3 4 0.5623351
5    4 5 0.6730117
6    5 4 0.5623351
7    6 3 0.6365142
8    7 4 1.0397208

6.6.4 Pattern Matching

Patterns are given as a list: "-" is a gap, a vector is a run of states, and "*" matches anything.

Box 102seqpm

tda_seqpm(seqd1[, -1L], patterns = list("-", c(3, 3), c(3, "*", 3)))
Call: tda_seqpm(sequences = seqd1[, -1L], patterns = list("-", c(3, 
    3), c(3, "*", 3)))

Cases: 5 

 case length matches1 matches2 matches3
    1      8        5        2        1
    4      6        2        2        1
    5      8        5        2        1

6.7.2.2 The seqm Command

tda_seqm() is seqm: the optimal matching distance between every pair of sequences.

Box 103seqm (seqm1.cf)

seqmd1 <- read.table(ex("seqm.d1"))
tda_seqm(seqmd1)
  1 2 3
2 2    
3 2 2  
4 4 2 2

print= asks for the alignment itself rather than the distance, either as the sequence of operations or as the longest common subsequence.

Box 104The alignment behind the distance

s <- tda_seqm(seqmd1, print = "sequential")
attr(s, "pairs")
  i j len.i len.j dist_t1 dist_t2 dist_t3 dist_t4 dist_t5 distance
1 2 1     5     5       0       0       2       2       2        2
2 3 1     5     5       0       0       0       2       2        2
3 3 2     5     5       0       0       2       2       2        2
4 4 1     5     5       0       0       2       4       4        4
5 4 2     5     5       0       0       0       2       2        2
6 4 3     5     5       0       0       2       2       2        2
attr(s, "dist_by_time")[, , "t4"]
     [,1] [,2] [,3] [,4]
[1,]    0    2    2    4
[2,]    2    0    2    2
[3,]    2    2    0    2
[4,]    4    2    2    0
s <- tda_seqm(seqmd1, print = "lcs")
attr(s, "lcs")
NULL

6.7.2.3 Examples

With subcost= the substitution cost is a matrix rather than a constant, and dp_matrix = TRUE keeps the dynamic programming table behind each distance.

Box 105A substitution cost matrix

seqmd3 <- read.table(ex("seqm.d3"))
scost <- matrix(0, 10, 10)
for (i in 1:10) for (j in 1:10) scost[i, j] <- 0.1 * abs(i - j)

r <- tda_seqm(seqmd3, indel = 1, subcost = scost, dp_matrix = TRUE)
dp <- attr(r, "dp_matrix")[["2_1"]]
dp$seqA        # the two sequences the matrix aligns
[1] 1 1 2 3 4 5 6
dp$seqB
[1] 4 5 6 7 7 7 7 7
dp$D
  B   4   5   6   7   7   7   7   7
A 0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0
1 1 0.3 1.3 2.3 3.3 4.3 5.3 6.3 7.3
1 2 1.3 0.7 1.7 2.7 3.7 4.7 5.7 6.7
2 3 2.2 1.6 1.1 2.1 3.1 4.1 5.1 6.1
3 4 3.1 2.4 1.9 1.5 2.5 3.5 4.5 5.5
4 5 4.0 3.2 2.6 2.2 1.8 2.8 3.8 4.8
5 6 5.0 4.0 3.3 2.8 2.4 2.0 3.0 4.0
6 7 6.0 5.0 4.0 3.4 2.9 2.5 2.1 3.1

s= asks seqm to print, for every pair, more than the one distance. print = "sequential" gives the distance after each time point (seqm1b.cf); print = "lcs" gives the length of the longest common subsequence and the positions it occupies (seqm1c.cf).

Box 106seqm’s s= option (seqm1b.cf, seqm1c.cf)

seqmd1 <- read.table(ex("seqm.d1"), col.names = paste0("Y", 1:5))
attr(tda_seqm(seqmd1, print = "sequential"), "pairs")
  i j len.i len.j dist_t1 dist_t2 dist_t3 dist_t4 dist_t5 distance
1 2 1     5     5       0       0       2       2       2        2
2 3 1     5     5       0       0       0       2       2        2
3 3 2     5     5       0       0       2       2       2        2
4 4 1     5     5       0       0       2       4       4        4
5 4 2     5     5       0       0       0       2       2        2
6 4 3     5     5       0       0       2       2       2        2
lcs <- attr(tda_seqm(seqmd1, print = "lcs"), "pairs")
lcs
  i j len.i len.j distance lcs_length lcs_t1 lcs_t2 lcs_t3 lcs_t4 lcs_t5
1 2 1     5     5        2          4      0      0      0      0     -1
2 3 1     5     5        2          4      0      0      0      0     -1
3 3 2     5     5        2          4      0      0      1      0     -1
4 4 1     5     5        4          3      0      0      0     -1     -1
5 4 2     5     5        2          4      0      0      1      0     -1
6 4 3     5     5        2          4      0      0      1      0     -1

6.7.2.4 Consecutive Indel Operations

A second indel cost charges consecutive insertions or deletions differently from the first.

Box 107indel costs (seqm4.cf)

seqmd4 <- read.table(ex("seqm.d4"))
tda_seqm(seqmd4)
  1 2 3 4
2 2      
3 4 2    
4 6 4 2  
5 8 6 4 2
tda_seqm(seqmd4, indel = c(1, 0.5))
  1 2 3 4
2 2      
3 3 2    
4 4 3 2  
5 5 4 3 2
tda_seqm(seqmd4, indel = c(1, 0))
  1 2 3 4
2 2      
3 2 2    
4 2 2 2  
5 2 2 2 2

6.7.2.5 Data-based Substitution Costs

subcost = 2 is TDA’s scost = 2: substitution costs derived from the data, 2 - p(a,b) - p(b,a) with p the observed transition probabilities between the two states, the manual’s seqm5.cf. The distances are those of its output file seqm5.d.

Box 108seqm5.cf: substitution costs based on the data

tda_seqm(seqmd4, subcost = 2)
         1        2        3        4
2 1.333333                           
3 2.666667 1.333333                  
4 4.000000 2.666667 1.333333         
5 5.333333 4.000000 2.666667 1.333333

6.9.1 The lsreg Command

tda_lsreg() is least squares regression, and summary() prints it the way R prints a regression.

Box 109lsreg

lsreg1 <- read.table(ex("lsreg1.dat"))
names(lsreg1) <- c("Height", "Weight")
summary(tda_lsreg(Weight ~ Height, data = lsreg1))
Call: tda_lsreg(formula = Weight ~ Height, data = lsreg1)

Cases: 19

            Estimate Std. Error C/Error  Pr(>|t|)    
Intercept -143.02692   32.27459 -4.4316 0.0003656 ***
Height       3.89903    0.51609  7.5549 7.887e-07 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 11.23 on 17 degrees of freedom
Sum of squared residuals: 2142.49
Multiple R-squared: 0.7705, Adjusted R-squared: 0.757
F-statistic: 57.0763,   p-value: 7.887e-07

6.9.1.2 Parameter Constraints

Constraints are written the way TDA writes them, as strings over the coefficient names b1, b2 and so on.

Box 110Equality and inequality constraints

lsreg2 <- read.table(ex("lsreg2.dat"))
names(lsreg2) <- c("X1", "X2", "Y")
tda_lsreg(Y ~ 0 + X1 + X2, data = lsreg2,
          equality   = "b1 + b2 = 6",
          inequality = c("b1 = 2", "b2 = 2"))
Call: tda_lsreg(formula = Y ~ 0 + X1 + X2, data = lsreg2, equality = "b1 + b2 = 6", 
    inequality = c("b1 = 2", "b2 = 2"))

Cases: 3

 Idx   Wave Variable  Coeff Error C/Error Signif
   1 1.0000       X1 3.0000    NA      NA     NA
   2 1.0000       X2 3.0000    NA      NA     NA

6.9.1.3 Dummy Variables

dgroup= is TDA’s group-of-dummies parameter: one regression per group, reported in one table.

Box 111A regression per region

lsreg3 <- read.table(ex("lsreg3.dat"))
names(lsreg3) <- c("X1", "G", "Y", "NE", "NC", "SO", "WE")
lsreg3$Y2 <- lsreg3$Y * lsreg3$Y
reg <- tda_lsreg(G ~ Y + Y2, data = lsreg3,
                 dgroup = c("NE", "NC", "SO", "WE"))
summary(reg)
Call: tda_lsreg(formula = G ~ Y + Y2, data = lsreg3, dgroup = c("NE", 
    "NC", "SO", "WE"))

Cases: 48

             Estimate  Std. Error  C/Error  Pr(>|t|)    
Intercept  1.0452e+00  1.9787e-01   5.2823 4.238e-06 ***
Y          7.7782e-04  2.9829e-05  26.0758 < 2.2e-16 ***
Y2        -1.3325e-08  7.9539e-10 -16.7526 < 2.2e-16 ***
dgroupNC  -3.9291e-02  1.3511e-01  -0.2908  0.772638    
dgroupNE  -7.8736e-01  1.3512e-01  -5.8272 7.039e-07 ***
dgroupSO   4.2968e-01  1.3517e-01   3.1789  0.002775 ** 
dgroupWE   3.9697e-01  1.3508e-01   2.9387  0.005334 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.5402 on 42 degrees of freedom
Sum of squared residuals: 12.2578
Multiple R-squared: 0.9739, Adjusted R-squared: 0.9708
# the per-group table TDA prints for dgrp=: the cases in each and its
# weight
reg$groups
  variable cases weight
1   DgrpNC    12   0.25
2   DgrpNE    12   0.25
3   DgrpSO    12   0.25
4   DgrpWE    12   0.25
# and the fit statistics the manual's box reports alongside them
unlist(reg$stats)
        df        rss     sigma2         r2        adj       rank      rnorm 
42.0000000 12.2578038  0.2918525  0.9738779  0.9707681  6.0000000  3.5011147 

6.10.1 L1-Norm Regression

Box 112l1reg

l1reg4 <- read.table(ex("l1reg4.dat"))
names(l1reg4) <- c("Y", "X1", "X2")
tda_l1reg(Y ~ X1 + X2, data = l1reg4)
Call: tda_l1reg(formula = Y ~ X1 + X2, data = l1reg4)

Cases: 6

 Idx   Wave  Variable   Coeff   Error C/Error Signif
   1     NA Intercept 32.7143 16.8621  1.9401 0.8523
   2 1.0000        X1  1.5952  0.9462  1.6860 0.8096
   3 1.0000        X2 -0.0952  0.5537 -0.1720 0.1256

6.10.2 Nonparametric Regression

tda_npreg() smooths with a kernel, against the curve the data were generated from.

Box 113A curve that is hard to recover, plus noise

gen <- tda_rng()
n <- 256
X <- E <- numeric(n)
for (i in seq_len(n)) {
    X[i] <- gen$rd()
    E[i] <- 0.32 * gen$rdn()
}
Z <- sin(2 * pi * X * X * X)
npreg <- data.frame(X = X, Y = Z * Z * Z + E)

Box 114npreg: the kernel estimate

np <- tda_npreg(Y ~ X, data = npreg, method = "mean", kernel = "quartic",
                bandwidth = 0.1, x = seq(0, 1, 0.01))
head(np$table)
  RECN    X NX         XM         YM       YSD
1    1 0.00 20 0.02631351 0.06019139 0.3340911
2    2 0.01 20 0.02631351 0.05075058 0.3340911
3    3 0.02 23 0.03162400 0.03975492 0.3444770
4    4 0.03 23 0.03162400 0.03203011 0.3444770
5    5 0.04 27 0.03943625 0.02852610 0.3244078
6    6 0.05 31 0.04654109 0.02926144 0.3091753

Box 115npreg, against the curve it is estimating

p <- tda_ps(npreg, width = 90, height = 50, xlim = c(0, 1), ylim = c(-1.2, 1.8))
p <- tda_pl_axes(p, sc = c(0.1, 0.5), ic = 0)
p <- tda_pl_points(p, "X", "Y", symbol = 5, size = 0.5, lty = 0)
p <- tda_pl_function(p, "sin(2*pi*x*x*x)*sin(2*pi*x*x*x)*sin(2*pi*x*x*x)",
                     range = c(0, 1), step = 0.01, lty = "dashed")
p <- tda_pl_lines(p, x = np$table$X, y = np$table$YM, lty = "solid", lw = 1)
plot(p)

6.11.2 The fml Command

tda_fml() maximises a likelihood the user writes. The block in braces is TDA’s function definition, with fn the value summed over the data matrix; the names that are neither variables nor already defined are the parameters.

The likelihood below is the exponential transition rate model of 6.17.2.1 written out by hand, and it reaches the estimates tda_rate(model = 2) reaches.

Box 116An exponential model written as a likelihood (frml1.cf)

d$DUR <- d$TFin - d$TStart + 1L
d$W <- as.integer(d$SEX == 2)

tda_fml({
    rate = exp(a0 + COHO2 * a1 + COHO3 * a2 + W * a3)
    l1 = ifelse(DES, log(rate), 0)
    fn = l1 - rate * DUR
}, data = d, start = c(-4, 0, 0, 0))
Call: tda_fml(definitions = {
    rate = exp(a0 + COHO2 * a1 + COHO3 * a2 + W * a3)
    l1 = ifelse(DES, log(rate), 0)
    fn = l1 - rate * DUR
}, data = d, start = c(-4, 0, 0, 0))

Cases: 600
logLik (starting values): -2578.948
logLik:  -2475.438
Converged in 6 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        a0 -5.0114 0.0843 -59.4446 1.0000
   2        a1  0.5341 0.1120   4.7686 1.0000
   3        a2  0.6738 0.1152   5.8472 1.0000
   4        a3  0.5065 0.0942   5.3746 1.0000

6.12.2 Binary Logit and Probit

tda_qreg() is the qreg command, model 1 the logit and model 2 the probit. predictions= and standardized= ask for the two further tables.

Box 117Binary logit (qr1.cf)

qr1 <- read.table(ex("qr1.dat"))
names(qr1) <- c("Dose", "Weight", "Response")
qr1$Log10Dose <- log(qr1$Dose) / log(10)

logit <- tda_qreg(Response ~ Log10Dose, data = qr1, weights = "Weight",
                  predictions = TRUE, standardized = TRUE)
logit
Call: tda_qreg(formula = Response ~ Log10Dose, data = qr1, standardized = TRUE, 
    predictions = TRUE, weights = "Weight")

Cases: 13
Model:   logit (TDA 1)
logLik (starting values): -51.29289
logLik:  -37.11065
Converged in 6 iterations

 Idx    Cat Term  Variable   Coeff  Error C/Error Signif
   1 1.0000    I Intercept -3.2246 0.8861 -3.6393 0.9997
   2 1.0000    X Log10Dose  5.9702 1.4492  4.1197 1.0000
logit$categories
  category  n      pct
1        0 36 48.64865
2        1 38 51.35135
attr(logit$categories, "observations")
 1 
74 
# qr1.cf fits the probit beside the logit, on the same data
tda_qreg(Response ~ Log10Dose, data = qr1, weights = "Weight",
         model = "probit")
Call: tda_qreg(formula = Response ~ Log10Dose, data = qr1, model = "probit", 
    weights = "Weight")

Cases: 13
Model:   probit (TDA 2)
logLik (starting values): -51.29289
logLik:  -37.28039
Converged in 5 iterations

 Idx    Cat Term  Variable   Coeff  Error C/Error Signif
   1 1.0000    I Intercept -1.8127 0.4493 -4.0341 0.9999
   2 1.0000    X Log10Dose  3.4181 0.7455  4.5847 1.0000
logit$standardized
  Idx Cat Term  Variable     Coeff       Exp(C) Exp(C*SD)  Std.Dev.
1   1   1    I Intercept -3.224644   0.03976993   1.00000 0.0000000
2   2   1    X Log10Dose  5.970180 391.57614388   5.12469 0.2737053
logit$predictions
   CaseID Wave Response Log10Dose CWt       PROB     PROB0      PROB1
1       1    1        0 0.0000000   9 0.96175122 0.9617512 0.03824878
2       2    1        1 0.0000000   1 0.03824878 0.9617512 0.03824878
3       3    1        0 0.3010300  10 0.80650228 0.8065023 0.19349772
4       4    1        1 0.3010300   2 0.19349772 0.8065023 0.19349772
5       5    1        0 0.4771213   6 0.59294286 0.5929429 0.40705714
6       6    1        1 0.4771213   4 0.40705714 0.5929429 0.40705714
7       7    1        0 0.6020600   5 0.40859846 0.4085985 0.59140154
8       8    1        1 0.6020600   5 0.59140154 0.4085985 0.59140154
9       9    1        0 0.6989700   4 0.27922029 0.2792203 0.72077971
10     10    1        1 0.6989700   8 0.72077971 0.2792203 0.72077971
11     11    1        0 0.7781513   2 0.19449565 0.1944957 0.80550435
12     12    1        1 0.7781513   8 0.80550435 0.1944957 0.80550435
13     13    1        1 0.8450980  10 0.86065528 0.1393447 0.86065528

Box 118The same data, probit

tda_qreg(Response ~ Log10Dose, data = qr1, weights = "Weight", model = 2)
Call: tda_qreg(formula = Response ~ Log10Dose, data = qr1, model = 2, 
    weights = "Weight")

Cases: 13
Model:   probit (TDA 2)
logLik (starting values): -51.29289
logLik:  -37.28039
Converged in 5 iterations

 Idx    Cat Term  Variable   Coeff  Error C/Error Signif
   1 1.0000    I Intercept -1.8127 0.4493 -4.0341 0.9999
   2 1.0000    X Log10Dose  3.4181 0.7455  4.5847 1.0000

The two link functions side by side, the logistic and, through TDA’s nd(), the standard normal distribution function.

Box 119plot-bin.cf

p <- tda_ps(xlim = c(-4, 4), ylim = c(0, 1), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1), ic = c(5, 10))
p <- tda_pl_function(p, "exp(x1)/(1+exp(x1))", range = c(-4, 4), step = 0.05)
p <- tda_pl_function(p, "nd(x1)", range = c(-4, 4), step = 0.05, lty = 6)
p <- tda_pl_function(p, "nd(x1*sqrt(3)/pi)", range = c(-4, 4), step = 0.05,
                     lty = 2)

plot(p)

6.12.2.1 Grouped Data

With grouped data the likelihood is binomial, and a random effect can be integrated out with intn, TDA’s numerical integration operator. The starting values come from the fit without the random effect.

Box 120A grouped logit, with and without a random effect (qr5.cf)

qr5 <- read.table(ex("qr5.dat"))
names(qr5) <- c("Y", "N", "S", "X")
qr5$SX <- qr5$S * qr5$X

fml <- tda_fml({
    xb = beta0 + S * beta1 + X * beta2 + SX * beta3
    ee = bc(N, Y) * (exp(xb)^Y) / ((1 + exp(xb))^N)
    fn = log(ee)
}, data = qr5)
fml
Call: tda_fml(definitions = {
    xb = beta0 + S * beta1 + X * beta2 + SX * beta3
    ee = bc(N, Y) * (exp(xb)^Y)/((1 + exp(xb))^N)
    fn = log(ee)
}, data = qr5)

Cases: 21
logLik (starting values): -87.83176
logLik:  -54.93702
Converged in 4 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1     beta0 -0.5582 0.1260 -4.4292 1.0000
   2     beta1  0.1459 0.2232  0.6539 0.4868
   3     beta2  1.3182 0.1775  7.4277 1.0000
   4     beta3 -0.7781 0.3064 -2.5392 0.9889
tda_fml({
    xb = beta0 + S * beta1 + X * beta2 + SX * beta3
    ee = intn(7, bc(N, Y) * (exp(xb + t * gam)^Y) / ((1 + exp(xb + t * gam))^N))
    fn = log(ee)
}, data = qr5, start = c(coef(fml), gam = 0.1), options = list(mina = 4))
Call: tda_fml(definitions = {
    xb = beta0 + S * beta1 + X * beta2 + SX * beta3
    ee = intn(7, bc(N, Y) * (exp(xb + t * gam)^Y)/((1 + exp(xb + 
        t * gam))^N))
    fn = log(ee)
}, data = qr5, start = c(coef(fml), gam = 0.1), options = list(mina = 4))

Cases: 21
logLik (starting values): -54.43748
logLik:  -53.75618
Converged in 17 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1     beta0 -0.5481 0.1668 -3.2861 0.9990
   2     beta1  0.0965 0.2785  0.3464 0.2710
   3     beta2  1.3369 0.2369  5.6442 1.0000
   4     beta3 -0.8104 0.3853 -2.1034 0.9646
   5       gam  0.2368 0.1110  2.1335 0.9671

6.12.3 Ordinal Logit and Probit

Models 3 and 4.

Box 121Ordinal logit and probit (qr2.cf)

qr2 <- read.table(ex("qr2.dat"), col.names = c("Y", "X1", "X2"))

qr_ol <- tda_qreg(Y ~ X1 + X2, data = qr2, model = 3, predictions = TRUE)
qr_ol
Call: tda_qreg(formula = Y ~ X1 + X2, data = qr2, model = 3, predictions = TRUE)

Cases: 20
Model:   ordinal_logit (TDA 3)
logLik (starting values): -27.52453
logLik:  -24.46419
Converged in 5 iterations

 Idx   Cat Term Variable   Coeff  Error C/Error Signif
   1    NA  I 1  Alpha 1 -1.7466 1.3020 -1.3414 0.8202
   2    NA  I 2  Alpha 2 -3.1531 1.4312 -2.2032 0.9724
   3    NA  I 3  Alpha 3 -4.2253 1.5565 -2.7146 0.9934
   4    NA    X       X1  1.0121 0.4637  2.1826 0.9709
   5    NA    X       X2  1.3100 0.8949  1.4639 0.8568
qr_ol$categories
  category n pct
1        0 5  25
2        1 5  25
3        2 4  20
4        3 6  30
# the manual's Box 5 is this table in full: the observed values, the
# fitted probability of the observed category, and the cumulative
# probabilities per category
qr_ol$predictions
   CaseID Wave Y    X1 X2      PROB     PROB0     PROB1      PROB2 PROB3
1       1    1 2 1.163  1 0.1495466 0.6771098 0.3393837 0.14954660     0
2       2    1 2 3.453  0 0.3250870 0.8517246 0.5845861 0.32508700     0
3       3    1 2 3.724  0 0.3878845 0.8831361 0.6492870 0.38788453     0
4       4    1 0 1.090  0 0.3444810 0.3444810 0.1140573 0.04220597     0
5       5    1 3 3.819  0 0.0000000 0.8926997 0.6708552 0.41094402     0
6       6    1 1 2.838  1 0.7367641 0.9195154 0.7367641 0.48927598     0
7       7    1 3 1.516  1 0.0000000 0.7498459 0.4234144 0.20086577     0
8       8    1 3 1.417  1 0.0000000 0.7305852 0.3991592 0.18526292     0
9       9    1 0 2.436  1 0.8838000 0.8838000 0.6507540 0.38941669     0
10     10    1 2 3.300  1 0.6046038 0.9480127 0.8170975 0.60460383     0
11     11    1 1 1.058  1 0.3159792 0.6534528 0.3159792 0.13652803     0
12     12    1 0 2.607  1 0.9004277 0.9004277 0.6889946 0.43126384     0
13     13    1 0 2.162  0 0.6086348 0.6086348 0.2758811 0.11536180     0
14     14    1 1 2.145  0 0.2724572 0.6045289 0.2724572 0.11361750     0
15     15    1 0 1.103  0 0.3474582 0.3474582 0.1153936 0.04274106     0
16     16    1 1 1.914  0 0.2286438 0.5475008 0.2286438 0.09211290     0
17     17    1 3 3.610  0 0.0000000 0.8706920 0.6225833 0.36086955     0
18     18    1 3 3.579  1 0.0000000 0.9602942 0.8555955 0.66975113     0
19     19    1 3 3.379  1 0.0000000 0.9518150 0.8287452 0.62354929     0
20     20    1 1 3.175  0 0.5150621 0.8125750 0.5150621 0.26661731     0
tda_qreg(Y ~ X1 + X2, data = qr2, model = 4)
Call: tda_qreg(formula = Y ~ X1 + X2, data = qr2, model = 4)

Cases: 20
Model:   ordinal_probit (TDA 4)
logLik (starting values): -29.52842
logLik:  -24.57412
Converged in 5 iterations

 Idx   Cat Term Variable   Coeff  Error C/Error Signif
   1    NA  I 1  Alpha 1 -1.0868 0.8207 -1.3243 0.8146
   2    NA  I 2  Alpha 2 -1.9059 0.8692 -2.1927 0.9717
   3    NA  I 3  Alpha 3 -2.5413 0.9193 -2.7645 0.9943
   4    NA    X       X1  0.6122 0.2801  2.1853 0.9711
   5    NA    X       X2  0.7563 0.5225  1.4475 0.8522

The same ordinal logit written as a likelihood, which is how the manual shows what the model is. eexp is TDA’s logistic function.

Box 122The ordinal logit as a likelihood

tda_fml({
    xb = X1 * beta1 + X2 * beta2
    theta0 = 1
    theta1 = eexp(alpha1 + xb)
    theta2 = eexp(alpha2 + xb)
    theta3 = eexp(alpha3 + xb)
    ll = ifelse(Y == 0, theta0 - theta1,
         ifelse(Y == 1, theta1 - theta2,
         ifelse(Y == 2, theta2 - theta3, theta3)))
    fn = log(ll)
}, data = qr2, start = c(-2, -3, -4, 0, 0))
Call: tda_fml(definitions = {
    xb = X1 * beta1 + X2 * beta2
    theta0 = 1
    theta1 = eexp(alpha1 + xb)
    theta2 = eexp(alpha2 + xb)
    theta3 = eexp(alpha3 + xb)
    ll = ifelse(Y == 0, theta0 - theta1, ifelse(Y == 1, theta1 - 
        theta2, ifelse(Y == 2, theta2 - theta3, theta3)))
    fn = log(ll)
}, data = qr2, start = c(-2, -3, -4, 0, 0))

Cases: 20
logLik (starting values): -52.01614
logLik:  -24.46419
Converged in 7 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1    alpha1 -1.7466 1.3020 -1.3414 0.8202
   2    alpha2 -3.1531 1.4312 -2.2032 0.9724
   3    alpha3 -4.2253 1.5565 -2.7146 0.9934
   4     beta1  1.0121 0.4637  2.1826 0.9709
   5     beta2  1.3100 0.8949  1.4639 0.8568

6.12.4 Multinomial Logit

Model 5, with nq= the number of response categories.

Box 123Multinomial logit (qr3.cf)

qr3 <- read.table(ex("qr3.dat"), col.names = c("X", "Weight", "Y"))
ml <- tda_qreg(Y ~ X, data = qr3, weights = "Weight", model = 5, nq = 5)
ml
Call: tda_qreg(formula = Y ~ X, data = qr3, model = 5, nq = 5, weights = "Weight")

Cases: 45
Model:   multinomial_logit (TDA 5)
logLik (starting values): -935.0834
logLik:  -826.7484
Converged in 7 iterations

 Idx    Cat Term  Variable   Coeff  Error C/Error Signif
   1 2.0000    I Intercept  7.2428 1.3578  5.3342 1.0000
   2 2.0000    X         X -0.5035 0.1024 -4.9187 1.0000
   3 3.0000    I Intercept  8.3752 1.3438  6.2326 1.0000
   4 3.0000    X         X -0.5842 0.1014 -5.7582 1.0000
   5 4.0000    I Intercept 11.3662 1.3897  8.1788 1.0000
   6 4.0000    X         X -0.8723 0.1073 -8.1304 1.0000
   7 5.0000    I Intercept 11.1873 1.5042  7.4373 1.0000
   8 5.0000    X         X -0.9375 0.1202 -7.7979 1.0000
# the distribution over the response's categories, which qreg prints
# before it estimates
ml$categories
  category   n       pct
1        1  70 12.048193
2        2 149 25.645439
3        3 175 30.120482
4        4 132 22.719449
5        5  55  9.466437
attr(ml$categories, "observations")   # the weighted total TDA prints
  1 
581 

lvl() is TDA’s level operator, which takes a set of columns as the levels of one response.

Box 124A multinomial logit over lvl() columns (qr4.cf)

qr4 <- read.table(ex("qr4.dat"), col.names = c("Z1", "Z2", "Z3", "Y"))
cml <- tda_qreg(Y ~ 0 + lvl(Z1, Z2, Z3), data = qr4, model = 5, nq = 3,
                predictions = TRUE)
cml
Call: tda_qreg(formula = Y ~ 0 + lvl(Z1, Z2, Z3), data = qr4, model = 5, 
    nq = 3, predictions = TRUE)

Cases: 50
Model:   multinomial_logit (TDA 5)
logLik (starting values): -54.93061
logLik:  -33.35814
Converged in 6 iterations

 Idx   Cat Term Variable   Coeff  Error C/Error Signif
   1    NA   Z1   Lvl1_1 -0.3568 0.0776 -4.5966 1.0000
cml$categories
  category  n pct
1        1 14  28
2        2 29  58
3        3  7  14
attr(cml$categories, "observations")
 1 
50 
# the manual's Box 5 is this table in full
cml$predictions
   CaseID Wave Y Lvl1_1 Lvl1_2 Lvl1_3       PROB       PROB0      PROB1       PROB2
1       1    1 2 16.481 16.196 23.890 0.50825625 0.459107699 0.50825625 0.032636051
2       2    1 2 15.123 11.373 14.182 0.61374895 0.161001851 0.61374895 0.225249200
3       3    1 2 19.469  8.822 20.819 0.96505207 0.021603374 0.96505207 0.013344553
4       4    1 2 18.847 15.649 21.280 0.68799081 0.219770542 0.68799081 0.092238651
5       5    1 2 12.578 10.671 18.335 0.63643005 0.322263785 0.63643005 0.041306161
6       6    1 1 11.513 20.582 27.838 0.95945062 0.959450618 0.03771773 0.002831652
7       7    1 1 10.651 15.537 17.418 0.79095984 0.790959836 0.13833811 0.070702054
8       8    1 1  8.359 15.675 21.050 0.92227190 0.922271898 0.06777270 0.009955405
9       9    1 1 11.679 12.668 23.104 0.58153318 0.581533182 0.40860461 0.009862205
10     10    1 2 23.237 10.356 21.346 0.97097408 0.009794046 0.97097408 0.019231879
11     11    1 3 13.236 16.019 10.087 0.69180771 0.224887888 0.08330440 0.691807713
12     12    1 3 20.052 16.861 14.168 0.66444655 0.081391947 0.25416150 0.664446554
13     13    1 2 18.917 14.764 21.564 0.76015053 0.172696842 0.76015053 0.067152626
14     14    1 2 18.200  6.868 19.095 0.97061987 0.017016157 0.97061987 0.012363971
15     15    1 1 10.777 16.554 15.938 0.77771716 0.777717162 0.09897500 0.123307841
16     16    1 2 20.003  6.377  9.314 0.73618664 0.005692268 0.73618664 0.258121097
17     17    1 2 19.768  8.523 18.960 0.95949794 0.017351589 0.95949794 0.023150471
18     18    1 2  8.151 13.845 17.643 0.11253321 0.858447817 0.11253321 0.029018969
19     19    1 1 22.173 18.045 15.535 0.06231900 0.062318999 0.27187000 0.665811004
20     20    1 2 13.134 11.067 19.108 0.65146835 0.311571688 0.65146835 0.036959964
21     21    1 1 14.051 14.247 15.764 0.40402308 0.404023080 0.37673059 0.219246332
22     22    1 3 14.685 10.811 12.361 0.31496031 0.137433204 0.54760649 0.314960309
23     23    1 1 11.666 10.758 16.445 0.38995855 0.389958552 0.53918355 0.070857901
24     24    1 3 17.211 15.201 17.059 0.25721184 0.243632153 0.49915601 0.257211837
25     25    1 1 13.930 16.227 22.024 0.66833940 0.668339401 0.29445386 0.037206734
26     26    1 2 15.237 14.345 19.984 0.53732601 0.390840266 0.53732601 0.071833721
27     27    1 1 10.840 11.071 10.188 0.31418570 0.314185698 0.28932567 0.396488636
28     28    1 2 16.841 11.224 13.417 0.62815088 0.084637718 0.62815088 0.287211398
29     29    1 3 13.913 16.991 26.618 0.00799063 0.743961963 0.24804741 0.007990630
30     30    1 2 13.089  9.822 19.162 0.74219373 0.231318722 0.74219373 0.026487545
31     31    1 3 16.626 10.725 15.285 0.14904522 0.092362412 0.75859237 0.149045217
32     32    1 2 13.477 15.509 24.421 0.32190207 0.664714178 0.32190207 0.013383753
33     33    1 2 20.851 14.557 19.800 0.79377546 0.083999927 0.79377546 0.122224608
34     34    1 2 11.365 12.673 22.212 0.38051021 0.606840956 0.38051021 0.012648835
35     35    1 2 13.296 10.076 17.810 0.72451063 0.229626567 0.72451063 0.045862804
36     36    1 1 15.417 14.103 21.050 0.36600531 0.366005308 0.58495952 0.049035176
37     37    1 2 15.938 11.180 19.851 0.81407769 0.149035656 0.81407769 0.036886649
38     38    1 2 19.034 14.125 19.764 0.76501914 0.132707436 0.76501914 0.102273425
39     39    1 1 10.466 12.481 18.540 0.64796751 0.647967508 0.31570150 0.036330990
40     40    1 3 15.799 16.979 13.074 0.61486143 0.232523970 0.15261460 0.614861430
41     41    1 2 12.713 15.105 13.629 0.19835896 0.465751592 0.19835896 0.335889446
42     42    1 2 16.908 10.958 19.713 0.85938894 0.102821215 0.85938894 0.037789847
43     43    1 2 17.098  6.853 14.502 0.91651470 0.023681576 0.91651470 0.059803724
44     44    1 2 18.608 14.268 18.301 0.68982183 0.146602447 0.68982183 0.163575721
45     45    1 1 11.059 10.812 20.121 0.46914173 0.469141731 0.51236934 0.018488932
46     46    1 2 15.641 10.754 24.669 0.84615894 0.147939578 0.84615894 0.005901485
47     47    1 1  7.822 18.949 16.904 0.94518707 0.945187070 0.01782790 0.036985032
48     48    1 2 12.824  5.697 19.183 0.92018353 0.072337036 0.92018353 0.007479430
49     49    1 2 11.852 12.147 15.672 0.41749018 0.463835709 0.41749018 0.118674106
50     50    1 2 15.557  8.307 22.286 0.92416940 0.069530571 0.92416940 0.006300028

6.12.5 Multivariate Probit

Model 6. The constraints zero the terms that are not wanted.

Box 125Multivariate probit (qr6.cf)

mvp <- tda_qreg(Y ~ X, data = qr3, model = 6, nq = 5, weights = "Weight",
         constraints = c("b9 = 0", "b10 = 0", "b11 = 0", "b12 = 0", "b13 = 0",
                         "b14 = 0", "b15 = 0", "b16 = 0", "b17 = 0",
                         "b18 = 0"))
mvp
Call: tda_qreg(formula = Y ~ X, data = qr3, model = 6, nq = 5, constraints = c("b9 = 0", 
    "b10 = 0", "b11 = 0", "b12 = 0", "b13 = 0", "b14 = 0", "b15 = 0", 
    "b16 = 0", "b17 = 0", "b18 = 0"), weights = "Weight")

Cases: 45
Model:   multivariate_probit (TDA 6)
logLik (starting values): -935.1167
logLik:  -828.232
Converged in 5 iterations

 Idx    Cat Term   Variable   Coeff  Error C/Error Signif
   1 2.0000    I  Intercept  2.8003 0.5528  5.0655 1.0000
   2 2.0000    X          X -0.1915 0.0431 -4.4477 1.0000
   3 3.0000    I  Intercept  3.3703 0.5454  6.1790 1.0000
   4 3.0000    X          X -0.2320 0.0425 -5.4523 1.0000
   5 4.0000    I  Intercept  4.8489 0.5609  8.6451 1.0000
   6 4.0000    X          X -0.3740 0.0445 -8.3994 1.0000
   7 5.0000    I  Intercept  4.5686 0.6054  7.5467 1.0000
   8 5.0000    X          X -0.3867 0.0493 -7.8424 1.0000
   9     NA    S Sigma 2, 1  0.0000 0.0000      NA     NA
  10     NA    S Sigma 3, 1  0.0000 0.0000      NA     NA
  11     NA    S Sigma 3, 2  0.0000 0.0000      NA     NA
  12     NA    S Sigma 4, 1  0.0000 0.0000      NA     NA
  13     NA    S Sigma 4, 2  0.0000 0.0000      NA     NA
  14     NA    S Sigma 4, 3  0.0000 0.0000      NA     NA
  15     NA    S Sigma 5, 1  0.0000 0.0000      NA     NA
  16     NA    S Sigma 5, 2  0.0000 0.0000      NA     NA
  17     NA    S Sigma 5, 3  0.0000 0.0000      NA     NA
  18     NA    S Sigma 5, 4  0.0000 0.0000      NA     NA
unlist(mvp$stats)
ncase 
   45 

Box 126Multivariate probit over lvl() columns (qr7.cf)

mv <- tda_qreg(Y ~ 0 + lvl(Z1, Z2, Z3), data = qr4, model = 6, nq = 3,
               start = c(-0.1716, 0, 0, 0), constraints = c("b3 = 0", "b4 = 0"))
mv
Call: tda_qreg(formula = Y ~ 0 + lvl(Z1, Z2, Z3), data = qr4, model = 6, 
    nq = 3, constraints = c("b3 = 0", "b4 = 0"), start = c(-0.1716, 
        0, 0, 0))

Cases: 50
Model:   multivariate_probit (TDA 6)
logLik (starting values): -34.5825
logLik:  -34.41663
Converged in 4 iterations

 Idx   Cat Term   Variable   Coeff  Error C/Error Signif
   1    NA   Z1     Lvl1_1 -0.1787 0.0349 -5.1178 1.0000
   2    NA    S Sigma 2, 1  0.4134 0.6320  0.6542 0.4870
   3    NA    S Sigma 3, 1  0.0000 0.0000      NA     NA
   4    NA    S Sigma 3, 2  0.0000 0.0000      NA     NA
# the correlation among the latent equations, which TDA writes into its
# ppar= file with the rows commented
mv$correlation
         [,1]     [,2] [,3]
[1,] 1.000000 0.413439    0
[2,] 0.413439 1.000000    0
[3,] 0.000000 0.000000    1

6.14.1 Poisson Regression

poisson(mu, y) is TDA’s log Poisson density, so a count model is a likelihood like any other. LOGS becomes an offset once its coefficient is constrained to one.

Box 127Poisson regression, free and with an offset (cd1.cf)

cd1 <- read.table(ex("cd1.dat"),
                  col.names = c("NDI", "Service", "B", "C", "D", "E",
                                "C60", "C65", "C70", "P75"))
cd1$LOGS <- log(cd1$Service)
cd1 <- cd1[cd1$NDI >= 0, ]

pois <- function(...) tda_fml({
    xb = b0 + B * bb + C * bc + D * bd + E * be +
         C60 * bc60 + C65 * bc65 + C70 * bc70 + P75 * bp75 + LOGS * blogs
    exb = exp(xb)
    fn = poisson(exb, NDI)
}, data = cd1, ...)

free <- pois()
free
Call: tda_fml(definitions = {
    xb = b0 + B * bb + C * bc + D * bd + E * be + C60 * bc60 + 
        C65 * bc65 + C70 * bc70 + P75 * bp75 + LOGS * blogs
    exb = exp(xb)
    fn = poisson(exb, NDI)
}, data = cd1)

Cases: 34
logLik (starting values): -870.2939
logLik:  -67.83538
Converged in 8 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1        b0 -5.5940 0.8724 -6.4121 1.0000
   2        bb -0.3499 0.2702 -1.2948 0.8046
   3        bc -0.7631 0.3382 -2.2565 0.9760
   4      bc60  0.6625 0.1536  4.3124 1.0000
   5      bc65  0.7597 0.1777  4.2763 1.0000
   6      bc70  0.3697 0.2458  1.5040 0.8674
   7        bd -0.1355 0.2971 -0.4560 0.3516
   8        be  0.2739 0.2418  1.1331 0.7428
   9     blogs  0.9027 0.1018  8.8672 1.0000
  10      bp75  0.3703 0.1181  3.1339 0.9983
# what the manual's box reports around the estimates
unlist(free$convergence)
             converged             iterations                problem           final_change 
                "TRUE"                    "8"                     NA                     NA 
              gradient               change.f             change.par            evaluations 
"3.04826456175064e-11" "5.57244259068293e-14"  "2.0557657693741e-06"                   "12" 
pois(constraints = "b9 = 1")
Call: tda_fml(definitions = {
    xb = b0 + B * bb + C * bc + D * bd + E * be + C60 * bc60 + 
        C65 * bc65 + C70 * bc70 + P75 * bp75 + LOGS * blogs
    exb = exp(xb)
    fn = poisson(exb, NDI)
}, data = cd1, constraints = "b9 = 1")

Cases: 34
logLik (starting values): -161157.8
logLik:  -68.28077
Converged in 12 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        b0 -6.4059 0.2174 -29.4600 1.0000
   2        bb -0.5433 0.1776  -3.0595 0.9978
   3        bc -0.6874 0.3290  -2.0891 0.9633
   4      bc60  0.6971 0.1496   4.6587 1.0000
   5      bc65  0.8184 0.1698   4.8207 1.0000
   6      bc70  0.4534 0.2332   1.9446 0.9482
   7        bd -0.0760 0.2906  -0.2614 0.2062
   8        be  0.3256 0.2359   1.3803 0.8325
   9     blogs  1.0000     NA       NA     NA
  10      bp75  0.3845 0.1183   3.2507 0.9988

6.14.2 Compound Poisson Models

cd2.cf is the same count model with a gamma-distributed rate – the negative binomial, negbin(alpha, gamma, y) in TDA’s expression language – and mina = 8 for a more robust algorithm. The overdispersion parameter sig is not identified on these data: its error is two orders of magnitude larger than its value, and its last digits depend on where the algorithm stops (the manual’s box reads -14.77, this run -15.06); the regression coefficients are the manual’s.

Box 128cd2.cf: a negative binomial count model

tda_fml({
    xb = b0 + B * bb + C * bc + D * bd + E * be +
         C60 * bc60 + C65 * bc65 + C70 * bc70 + P75 * bp75 + LOGS * blogs
    gamma = exp(xb)
    sigma = exp(sig)
    alpha = 1 / sigma
    fn = negbin(alpha, gamma, NDI)
}, data = cd1, options = list(mina = 8))
Call: tda_fml(definitions = {
    xb = b0 + B * bb + C * bc + D * bd + E * be + C60 * bc60 + 
        C65 * bc65 + C70 * bc70 + P75 * bp75 + LOGS * blogs
    gamma = exp(xb)
    sigma = exp(sig)
    alpha = 1/sigma
    fn = negbin(alpha, gamma, NDI)
}, data = cd1, options = list(mina = 8))

Cases: 34
logLik (starting values): -270.3274
logLik:  -67.8354
Converged in 17 iterations

 Idx Parameter    Coeff    Error C/Error Signif
   1        b0  -5.5940   0.8724 -6.4121 1.0000
   2        bb  -0.3499   0.2702 -1.2948 0.8046
   3        bc  -0.7631   0.3382 -2.2565 0.9760
   4      bc60   0.6625   0.1536  4.3124 1.0000
   5      bc65   0.7597   0.1777  4.2763 1.0000
   6      bc70   0.3697   0.2458  1.5040 0.8674
   7        bd  -0.1355   0.2971 -0.4560 0.3516
   8        be   0.2739   0.2418  1.1331 0.7428
   9     blogs   0.9027   0.1018  8.8672 1.0000
  10      bp75   0.3703   0.1181  3.1339 0.9983
  11       sig -15.0638 212.9850 -0.0707 0.0564

6.15.2.1 Generalized Linear Models

tda_glm() is TDA’s glm. Without a family it is the Gaussian model with the identity link, which is least squares; link = 2 is the log link, which needs starting values here.

Box 129glm, Gaussian, identity and log links

plain <- tda_glm(Weight ~ Height, data = lsreg1)
plain
Call: tda_glm(formula = Weight ~ Height, data = lsreg1)

Cases: 19
Family:  gaussian
Link:    identity
Converged in 2 iterations

 Idx   Wave  Variable     Coeff   Error C/Error Signif
   1     NA Intercept -143.0269 32.2746 -4.4316 0.9996
   2 1.0000    Height    3.8990  0.5161  7.5549 1.0000
# the manual prints the deviance as 2142.4880. Reading its data file TDA
# stores each variable as a 4-byte float; the wrapper hands R's doubles
# over directly, and stats::glm on the same data agrees with it exactly.
unlist(plain$stats)
            df       deviance        pearson       scale.ml scale.deviance           rank 
       17.0000      2142.4877      2142.4877       112.7625       126.0287         2.0000 
gen <- tda_rng()
n <- 100
X <- Y <- numeric(n)
for (i in seq_len(n)) {
    X[i] <- gen$rd(a = -3, b = 3)
    Y[i] <- exp(3 + X[i]) + gen$rd()
}
bad <- tda_glm(Y ~ X, data = data.frame(X, Y), start = c(5, 5), link = 2)
bad
Call: tda_glm(formula = Y ~ X, data = data.frame(X, Y), link = 2, start = c(5, 
    5))

Cases: 100
Family:  gaussian
Link:    log
Converged in 19 iterations

 Idx   Wave  Variable  Coeff  Error   C/Error Signif
   1     NA Intercept 3.0128 0.0020 1524.0437 1.0000
   2 1.0000         X 0.9958 0.0008 1299.1796 1.0000
# what the manual's Box 4 reports around the estimates: the degrees of
# freedom, the deviance and Pearson statistics, the scale estimates and
# the rank, and how far the last step moved
unlist(bad$stats)
            df       deviance        pearson       scale.ml scale.deviance           rank 
    98.0000000     19.9937532     19.9937532      0.1999375      0.2040179      2.0000000 
unlist(bad$convergence)[c("converged", "iterations", "final_change")]
    converged    iterations  final_change 
       "TRUE"          "19" "4.13312e-10" 

6.15.2.2 Binomial Models

The number of trials is given separately, and a link can be written out rather than selected: custom_link= takes a TDA expression in mue.

Box 130Binomial models, selected and hand-written links

glmbin1 <- tda_glm(Response ~ Log10Dose, data = qr1, weights = "Weight", family = 2)
glmbin1
Call: tda_glm(formula = Response ~ Log10Dose, data = qr1, family = 2, 
    weights = "Weight")

Cases: 13
Family:  binomial
Link:    logit
Converged in 6 iterations

 Idx   Wave  Variable   Coeff  Error C/Error Signif
   1     NA Intercept -3.2246 0.8861 -3.6393 0.9961
   2 1.0000 Log10Dose  5.9702 1.4492  4.1197 0.9983
unlist(glmbin1$stats)
      df deviance     rank 
11.00000 74.22131  2.00000 
glm3 <- read.table(ex("glm3.dat"), col.names = c("D", "N", "Y"))
glm3$D <- log(glm3$D)

glmbin2 <- tda_glm(Y ~ D, data = glm3, family = 2, link = 3, trials = glm3$N)
glmbin2
Call: tda_glm(formula = Y ~ D, data = glm3, family = 2, link = 3, trials = glm3$N)

Cases: 6
Family:  binomial
Link:    logit
Converged in 8 iterations

 Idx   Wave  Variable   Coeff  Error C/Error Signif
   1     NA Intercept -4.8869 0.6429 -7.6010 0.9984
   2 1.0000         D  3.1035 0.3877  8.0047 0.9987
unlist(glmbin2$stats)
      df deviance     rank 
4.000000 1.424057 2.000000 
glmbin1 <- tda_glm(Y ~ D, data = glm3, family = 2, trials = glm3$N,
        custom_link = "log(mue / (1 - mue))")
glmbin1
Call: tda_glm(formula = Y ~ D, data = glm3, family = 2, custom_link = "log(mue / (1 - mue))", 
    trials = glm3$N)

Cases: 6
Family:  binomial
Converged in 8 iterations

 Idx   Wave  Variable   Coeff  Error C/Error Signif
   1     NA Intercept -4.8869 0.6429 -7.6010 0.9984
   2 1.0000         D  3.1035 0.3877  8.0047 0.9987
unlist(glmbin1$stats)
      df deviance     rank 
4.000000 1.424057 2.000000 

6.15.2.3 Poisson Models

The offset is the same constrained coefficient as in 6.14.1, given here as an equality constraint on the GLM.

Box 131The Poisson family

glm5 <- read.table(ex("glm5.dat"), col.names = c("X", "Y"))
glmpois1 <- tda_glm(Y ~ X, data = glm5, family = "poisson")
glmpois1
Call: tda_glm(formula = Y ~ X, data = glm5, family = "poisson")

Cases: 10
Family:  poisson
Link:    log
Converged in 9 iterations

 Idx   Wave  Variable  Coeff  Error C/Error Signif
   1     NA Intercept 0.8673 0.2384  3.6382 0.9934
   2 1.0000         X 1.1754 0.3565  3.2974 0.9891
unlist(glmpois1$stats)
      df deviance     rank 
8.000000 6.190151 2.000000 
glmpois1 <- tda_glm(NDI ~ B + C + D + E + C60 + C65 + C70 + P75 + LOGS, data = cd1,
        family = "poisson", equality = c("b9 = 1"),
        control = tda_control(maxit = 100))
glmpois1
Call: tda_glm(formula = NDI ~ B + C + D + E + C60 + C65 + C70 + P75 + 
    LOGS, data = cd1, family = "poisson", equality = c("b9 = 1"), 
    control = tda_control(maxit = 100))

Cases: 34
Family:  poisson
Link:    log
Converged in 41 iterations

 Idx   Wave  Variable   Coeff  Error  C/Error Signif
   1     NA Intercept -6.4059 0.2174 -29.4600 1.0000
   2 1.0000         B -0.5433 0.1776  -3.0595 0.9948
   3 1.0000         C -0.6874 0.3290  -2.0891 0.9530
   4 1.0000         D -0.0760 0.2906  -0.2614 0.2041
   5 1.0000         E  0.3256 0.2359   1.3803 0.8203
   6 1.0000       C60  0.6971 0.1496   4.6587 0.9999
   7 1.0000       C65  0.8184 0.1698   4.8207 0.9999
   8 1.0000       C70  0.4534 0.2332   1.9446 0.9368
   9 1.0000       P75  0.3845 0.1183   3.2507 0.9967
  10 1.0000      LOGS  1.0000     NA       NA     NA
unlist(glmpois1$stats)
      df deviance     rank 
25.00000 38.69505  9.00000 

6.15.2.4 Gamma Models

Box 132The Gamma family, log and inverse links

gen <- tda_rng()
n <- 1000
X <- Y <- numeric(n)
for (i in seq_len(n)) {
    X[i] <- gen$rd()
    Y[i] <- -exp(1 + X[i]) * (log(gen$rd()) + log(gen$rd())) / 2
}
glmgamma1 <- tda_glm(Y ~ X, data = data.frame(X, Y), family = "Gamma", link = 2,
        start = c(1, 1), control = tda_control(maxit = 50))
glmgamma1
Call: tda_glm(formula = Y ~ X, data = data.frame(X, Y), family = "Gamma", 
    link = 2, start = c(1, 1), control = tda_control(maxit = 50))

Cases: 1000
Family:  Gamma
Link:    log
Converged in 4 iterations

 Idx   Wave  Variable  Coeff  Error C/Error Signif
   1     NA Intercept 0.9620 0.0624 15.4239 1.0000
   2 1.0000         X 1.0947 0.1070 10.2332 1.0000
unlist(glmgamma1$stats)
      df deviance     rank 
998.0000 519.1471   2.0000 
# glm7.cf's second model: the same generator continued, with expectation
# 1/(1+X), and the gamma family's canonical (reciprocal) link
X2 <- Y2 <- numeric(n)
for (i in seq_len(n)) {
    X2[i] <- gen$rd()
    Y2[i] <- -(log(gen$rd()) + log(gen$rd())) / ((1 + X2[i]) * 2)
}
glmgamma2 <- tda_glm(Y2 ~ X2, data = data.frame(X2, Y2), family = "Gamma",
        start = c(1, 1), control = tda_control(maxit = 50))
glmgamma2
Call: tda_glm(formula = Y2 ~ X2, data = data.frame(X2, Y2), family = "Gamma", 
    start = c(1, 1), control = tda_control(maxit = 50))

Cases: 1000
Family:  Gamma
Link:    inverse
Converged in 3 iterations

 Idx   Wave  Variable  Coeff  Error C/Error Signif
   1     NA Intercept 1.0082 0.0748 13.4819 1.0000
   2 1.0000        X2 1.0223 0.1604  6.3736 1.0000
unlist(glmgamma2$stats)
      df deviance     rank 
998.0000 550.6416   2.0000 
glm8a <- read.table(ex("glm8a.dat"), col.names = c("U", "Y", "L"))
glm8a$X <- log(glm8a$U)
glmgamma3 <- tda_glm(Y ~ X, data = glm8a[glm8a$L == 0, ], family = "Gamma",
        start = c(-0.02, 0.02))
glmgamma3
Call: tda_glm(formula = Y ~ X, data = glm8a[glm8a$L == 0, ], family = "Gamma", 
    start = c(-0.02, 0.02))

Cases: 9
Family:  Gamma
Link:    inverse
Converged in 5 iterations

 Idx   Wave  Variable   Coeff  Error C/Error Signif
   1     NA Intercept -0.0166 0.0188 -0.8827 0.5933
   2 1.0000         X  0.0153 0.0084  1.8287 0.8898
unlist(glmgamma3$stats)
        df   deviance       rank 
7.00000000 0.01672972 2.00000000 
glmgamma4 <- tda_glm(Y ~ X, data = glm8a[glm8a$L == 1, ], family = "Gamma",
        start = c(-0.02, 0.02))
glmgamma4
Call: tda_glm(formula = Y ~ X, data = glm8a[glm8a$L == 1, ], family = "Gamma", 
    start = c(-0.02, 0.02))

Cases: 9
Family:  Gamma
Link:    inverse
Converged in 4 iterations

 Idx   Wave  Variable   Coeff  Error C/Error Signif
   1     NA Intercept -0.0239 0.0311 -0.7675 0.5321
   2 1.0000         X  0.0236 0.0135  1.7423 0.8750
unlist(glmgamma4$stats)
        df   deviance       rank 
7.00000000 0.01267176 2.00000000 

6.15.2.5 Inverse Gaussian Models

glm9.cf draws from an inverse Gaussian with mean 1 + X through TDA’s own generator, and fits it with the identity link from the starting values 0.5, 0.5.

Box 133The inverse Gaussian family (glm9.cf)

gen <- tda_rng()
n <- 1000
X <- Mu <- Z <- X1 <- Y <- numeric(n)
for (i in seq_len(n)) {
    X[i] <- gen$rd()
    Mu[i] <- 1 + X[i]
    Z[i] <- gen$rdn()^2
    X1[i] <- Mu[i] + Mu[i]^2 * Z[i] -
        Mu[i] * sqrt(2 * Mu[i] * Z[i] + Mu[i]^2 * Z[i]^2)
    Y[i] <- ifelse(gen$rd() <= Mu[i] / (Mu[i] + X1[i]), X1[i], Mu[i]^2 / X1[i])
}
ig <- tda_glm(Y ~ X, data = data.frame(X, Y), family = "inverse.gaussian",
              link = 1, start = c(0.5, 0.5))
ig
Call: tda_glm(formula = Y ~ X, data = data.frame(X, Y), family = "inverse.gaussian", 
    link = 1, start = c(0.5, 0.5))

Cases: 1000
Family:  inverse.gaussian
Link:    identity
Converged in 6 iterations

 Idx   Wave  Variable  Coeff  Error C/Error Signif
   1     NA Intercept 1.0702 0.0867 12.3497 1.0000
   2 1.0000         X 0.8148 0.1909  4.2690 1.0000
unlist(ig$stats)
      df deviance     rank 
 998.000 1933.998    2.000 
unlist(ig$convergence)[c("converged", "iterations", "final_change")]
    converged    iterations  final_change 
       "TRUE"           "6" "7.00739e-09" 

6.16.1 The freg Command

tda_freg() is nonlinear least squares: the function block gives the model and fn the squared residual, summed over the data. The manual’s example is Gallant’s (1987, p. 4) data, 30 observations, which the manual’s freg1.dat holds; they are typed in here.

Box 134freg: Gallant’s example (freg1.cf)

freg1dat <- read.csv(ex("gallant.dat"))
tda_freg({
    ax = a1 * X1 + a2 * X2 + a4 * exp(a3 * X3)
    fn = (Y - ax)^2
}, data = freg1dat, start = c(-0.1, 1, -1, -1),
   control = tda_control(algorithm = 5))
Call: tda_freg(definitions = {
    ax = a1 * X1 + a2 * X2 + a4 * exp(a3 * X3)
    fn = (Y - ax)^2
}, data = freg1dat, start = c(-0.1, 1, -1, -1), control = tda_control(algorithm = 5))

Cases: 30
Converged in 9 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        a1 -0.0259 0.0126  -2.0511 0.9597
   2        a2  1.0157 0.0099 102.4480 1.0000
   3        a3 -1.1157 0.1607  -6.9423 1.0000
   4        a4 -0.5049 0.0255 -19.7862 1.0000

6.17.2.1 The Basic Exponential Model

The simplest transition rate model lets the rate vary with the covariates but not with the duration of the episode. The duration is exponentially distributed, with density f(t) = a exp(-a t), survivor function G(t) = exp(-a t) and rate r(t) = a, and the covariates enter through an exponential link,

r(jk) = exp(A(jk) alpha(jk)),

where A holds a constant one in its first component for the intercept. This is TDA’s model number 2. The xa(0,1) = ... parameter of the rate command, which links covariates to the transition from 0 to 1, is the right hand side of the formula.

Example 1 The manual’s rt1.cf links three covariates to the only transition in the data.

Box 135The command file rt1.cf, in R

fit <- tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 2,
                residuals = TRUE,
                # rt1.cf's constellation: COHO3 = 1 and W = 1
                prate = list(tp = seq(0, 100, 5), COHO3 = 1, W = 1))
fit
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 2, prate = list(tp = seq(0, 100, 5), COHO3 = 1, W = 1), 
    residuals = TRUE)

Episodes: 600
Model:   2 (TDA 2)
logLik (starting values): -2514.02
logLik:  -2475.438
Converged in 5 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -5.0114 0.0843 -59.4446 1.0000
   2  1   0   1  A    COHO2  0.5341 0.1120   4.7686 1.0000
   3  1   0   1  A    COHO3  0.6738 0.1152   5.8472 1.0000
   4  1   0   1  A        W  0.5065 0.0942   5.3746 1.0000

rt1.cf asks for two things besides the estimates: the generalized residuals for every episode, and the fitted rate over a time axis at a chosen covariate constellation.

Box 136rt1.cf: generalized residuals and the rate table

# the manual prints part of res.d: its first rows and its last
head(fit$residuals, 6)
  Case Org Des TS  TF        Rate   Function  Residual Weight
1    1   0   0  0 428 0.006661875 0.05777019 2.8512823      1
2    2   0   1  0  46 0.011055526 0.60136441 0.5085542      1
3    3   0   1  0  34 0.011055526 0.68667932 0.3758879      1
4    4   0   1  0 220 0.011055526 0.08784199 2.4322157      1
5    5   0   1  0  12 0.018859457 0.79746807 0.2263135      1
6    6   0   1  0  30 0.018859457 0.56791490 0.5657837      1
tail(fit$residuals, 6)
    Case Org Des TS  TF        Rate  Function  Residual Weight
595  595   0   1  0 119 0.006661875 0.4525925 0.7927631      1
596  596   0   1  0  66 0.006661875 0.6442401 0.4396837      1
597  597   0   0  0 112 0.006661875 0.4741982 0.7461300      1
598  598   0   0  0 103 0.013068795 0.2602569 1.3460859      1
599  599   0   1  0  22 0.018859457 0.6604010 0.4149080      1
600  600   0   1  0  13 0.018859457 0.7825692 0.2451729      1
# the rate table, and the constellation it was computed at -- which
# covariate values, and the coefficients they multiply
attr(fit$rates, "constellation")
  idx sn org des mt variable      coeff covariate
1   1  1   0   1  A Constant -5.0113544         1
2   2  1   0   1  A    COHO2  0.5340841         0
3   3  1   0   1  A    COHO3  0.6738264         1
4   4  1   0   1  A        W  0.5065294         1
fit$rates
   ID Time    Surv.F     Density       Rate
1   0    0 1.0000000 0.021687950 0.02168795
2   0    5 0.8972330 0.019459143 0.02168795
3   0   10 0.8050270 0.017459385 0.02168795
4   0   15 0.7222967 0.015665135 0.02168795
5   0   20 0.6480684 0.014055276 0.02168795
6   0   25 0.5814683 0.012610856 0.02168795
7   0   30 0.5217126 0.011314876 0.02168795
8   0   35 0.4680977 0.010152079 0.02168795
9   0   40 0.4199927 0.009108780 0.02168795
10  0   45 0.3768313 0.008172698 0.02168795
11  0   50 0.3381054 0.007332814 0.02168795
12  0   55 0.3033593 0.006579242 0.02168795
13  0   60 0.2721840 0.005903113 0.02168795
14  0   65 0.2442124 0.005296467 0.02168795
15  0   70 0.2191155 0.004752165 0.02168795
16  0   75 0.1965976 0.004263799 0.02168795
17  0   80 0.1763939 0.003825621 0.02168795
18  0   85 0.1582664 0.003432473 0.02168795
19  0   90 0.1420018 0.003079728 0.02168795
20  0   95 0.1274087 0.002763234 0.02168795
21  0  100 0.1143153 0.002479264 0.02168795

Convergence is reached in 5 iterations with TDA’s default algorithm, and the coefficients are the ones printed in the manual. They are not read back from that printout: coef() holds the doubles TDA computed.

Box 137The estimates, at full precision

coef(fit)
  Constant      COHO2      COHO3          W 
-5.0113544  0.5340841  0.6738264  0.5065294 
logLik(fit)
'log Lik.' -2475.438 (df=4)
fit$logLik_null   # the exponential null model TDA starts from
[1] -2514.02

Example 2 rt1m.cf estimates the same three covariates over the three destination states. Its DES differs from ed2.cf’s in one comparison – ge(PRESN/PRES - 1, 0.2) against gt – and one episode lies on 0.2 to within TDA’s comparison tolerance, so the manual’s episode table here reads 85/218 where 3.3.3’s read 84/219; tda_rrdat(upward = "ge") is that coding. relative_risk= adds the manual’s table of rate ratios.

Box 138rt1m.cf: three transitions, and the relative risks (frml1m.cf)

d4m <- tda_rrdat(states = 4, upward = "ge")
d4m$W <- as.integer(d4m$SEX == 2)
fitm <- tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d4m, model = 2,
                 relative_risk = TRUE)
fitm$episodes
  sn org des episodes weighted mean.duration ts.min tf.max excluded
1  1   0   0      142      142     128.17606      0    428    FALSE
2  1   0   1       85       85      44.81176      0    326    FALSE
3  1   0   2      218      218      46.00917      0    350    FALSE
4  1   0   3      155      155      56.40000      0    332    FALSE
fitm$estimates
   Idx SN Org Des MT Variable       Coeff     Error     C/Error    Signif
1    1  1   0   1  A Constant -6.34115531 0.1736214 -36.5228896 1.0000000
2    2  1   0   1  A    COHO2  0.30116538 0.2617898   1.1504092 0.7500246
3    3  1   0   1  A    COHO3  0.49987105 0.2650832   1.8857137 0.9406665
4    4  1   0   1  A        W -0.09566548 0.2342343  -0.4084179 0.3170331
5    5  1   0   2  A Constant -5.75478094 0.1236419 -46.5439221 1.0000000
6    6  1   0   2  A    COHO2  0.69241033 0.1629726   4.2486308 0.9999785
7    7  1   0   2  A    COHO3  0.80879929 0.1683635   4.8038877 0.9999984
8    8  1   0   2  A        W  0.29771709 0.1381110   2.1556362 0.9688879
9    9  1   0   3  A Constant -6.36856915 0.1577556 -40.3698331 1.0000000
10  10  1   0   3  A    COHO2  0.44239916 0.1923777   2.2996383 0.9785313
11  11  1   0   3  A    COHO3  0.58747938 0.1979632   2.9676193 0.9969988
12  12  1   0   3  A        W  1.11561053 0.1658799   6.7254092 1.0000000
fitm
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d4m, 
    model = 2, relative_risk = TRUE)

Episodes: 600
Model:   2 (TDA 2)
logLik (starting values): -2986.95
logLik:  -2935.583
Converged in 5 iterations
Transitions: 0->1, 0->2, 0->3 

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -6.3412 0.1736 -36.5229 1.0000
   2  1   0   1  A    COHO2  0.3012 0.2618   1.1504 0.7500
   3  1   0   1  A    COHO3  0.4999 0.2651   1.8857 0.9407
   4  1   0   1  A        W -0.0957 0.2342  -0.4084 0.3170
   5  1   0   2  A Constant -5.7548 0.1236 -46.5439 1.0000
   6  1   0   2  A    COHO2  0.6924 0.1630   4.2486 1.0000
   7  1   0   2  A    COHO3  0.8088 0.1684   4.8039 1.0000
   8  1   0   2  A        W  0.2977 0.1381   2.1556 0.9689
   9  1   0   3  A Constant -6.3686 0.1578 -40.3698 1.0000
  10  1   0   3  A    COHO2  0.4424 0.1924   2.2996 0.9785
  11  1   0   3  A    COHO3  0.5875 0.1980   2.9676 0.9970
  12  1   0   3  A        W  1.1156 0.1659   6.7254 1.0000
fitm$relative_risk
   Idx SN Org Des MT Variable      R.Risk
1    1  1   0   1  A Constant 0.001762265
2    2  1   0   1  A    COHO2 1.351432826
3    3  1   0   1  A    COHO3 1.648508683
4    4  1   0   1  A        W 0.908767967
5    5  1   0   2  A Constant 0.003167600
6    6  1   0   2  A    COHO2 1.998526843
7    7  1   0   2  A    COHO3 2.245210517
8    8  1   0   2  A        W 1.346780721
9    9  1   0   3  A Constant 0.001714611
10  10  1   0   3  A    COHO2 1.556436885
11  11  1   0   3  A    COHO3 1.799446975
12  12  1   0   3  A        W 3.051430595

6.17.2.2 Models with Time Periods

Model 3 is the piecewise constant exponential: a separate rate in each period given by tp=, the covariate effects held common.

Box 139A piecewise constant exponential (rt2.cf)

rt2 <- tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 3,
                tp = seq(0, 96, 12))
rt2$periods
  start end starting ending events
1     0  12      600     76     63
2    12  24        0    104     96
3    24  36        0    108     95
4    36  48        0     55     44
5    48  60        0     44     38
6    60  72        0     38     29
7    72  84        0     22     17
8    84  96        0      9      6
9    96 Inf        0    144     70
rt2
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 3, tp = seq(0, 96, 12))

Episodes: 600
Model:   3 (TDA 3)
logLik (starting values): -2514.02
logLik:  -2433.376
Converged in 5 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Period-1 -5.2070 0.1520 -34.2464 1.0000
   2  1   0   1  A Period-2 -4.5473 0.1312 -34.6574 1.0000
   3  1   0   1  A Period-3 -4.2675 0.1296 -32.9389 1.0000
   4  1   0   1  A Period-4 -4.7784 0.1690 -28.2758 1.0000
   5  1   0   1  A Period-5 -4.7300 0.1779 -26.5924 1.0000
   6  1   0   1  A Period-6 -4.7862 0.1987 -24.0857 1.0000
   7  1   0   1  A Period-7 -5.1366 0.2523 -20.3568 1.0000
   8  1   0   1  A Period-8 -6.0783 0.4137 -14.6918 1.0000
   9  1   0   1  A Period-9 -5.4198 0.1281 -42.3123 1.0000
  10  1   0   1  A    COHO2  0.4367 0.1133   3.8534 0.9999
  11  1   0   1  A    COHO3  0.5162 0.1182   4.3688 1.0000
  12  1   0   1  A        W  0.4336 0.0949   4.5688 1.0000

rt2c.cf constrains all nine period coefficients to be equal, which gives the exponential model of rt1.cf back – the manual’s way of showing what a constraint does.

Box 140rt2c.cf: all periods constrained equal

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 3,
         tp = seq(0, 96, 12),
         constraints = sprintf("b%d - b%d = 0", 1:8, 2:9))
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 3, constraints = sprintf("b%d - b%d = 0", 1:8, 2:9), 
    tp = seq(0, 96, 12))

Episodes: 600
Model:   3 (TDA 3)
logLik (starting values): -2514.02
logLik:  -2475.438
Converged in 5 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Period-1 -5.0114 0.0843 -59.4446 1.0000
   2  1   0   1  A Period-2 -5.0114 0.0843 -59.4446 1.0000
   3  1   0   1  A Period-3 -5.0114 0.0843 -59.4446 1.0000
   4  1   0   1  A Period-4 -5.0114 0.0843 -59.4446 1.0000
   5  1   0   1  A Period-5 -5.0114 0.0843 -59.4446 1.0000
   6  1   0   1  A Period-6 -5.0114 0.0843 -59.4446 1.0000
   7  1   0   1  A Period-7 -5.0114 0.0843 -59.4446 1.0000
   8  1   0   1  A Period-8 -5.0114 0.0843 -59.4446 1.0000
   9  1   0   1  A Period-9 -5.0114 0.0843 -59.4446 1.0000
  10  1   0   1  A    COHO2  0.5341 0.1120   4.7686 1.0000
  11  1   0   1  A    COHO3  0.6738 0.1152   5.8472 1.0000
  12  1   0   1  A        W  0.5065 0.0942   5.3746 1.0000

6.17.2.3 Period-specific Effects

Model 16 gives each period its covariate effects as well as its own baseline.

Box 141Period-specific effects (rt3.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 16,
         tp = seq(0, 96, 12))
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 16, tp = seq(0, 96, 12))

Episodes: 600
Model:   16 (TDA 16)
logLik (starting values): -2514.02
logLik:  -2411.991
Converged in 10 iterations

 Idx SN Org Des MT      P Variable   Coeff   Error  C/Error Signif
   1  1   0   1  A 1.0000 Period-1 -5.0470  0.2696 -18.7220 1.0000
   2  1   0   1  A 2.0000 Period-2 -4.5147  0.2121 -21.2865 1.0000
   3  1   0   1  A 3.0000 Period-3 -4.1413  0.1949 -21.2431 1.0000
   4  1   0   1  A 4.0000 Period-4 -5.1107  0.3160 -16.1711 1.0000
   5  1   0   1  A 5.0000 Period-5 -4.9954  0.3226 -15.4869 1.0000
   6  1   0   1  A 6.0000 Period-6 -4.5366  0.2934 -15.4603 1.0000
   7  1   0   1  A 7.0000 Period-7 -5.5069  0.5127 -10.7403 1.0000
   8  1   0   1  A 8.0000 Period-8 -6.0808  0.7231  -8.4090 1.0000
   9  1   0   1  A 9.0000 Period-9 -5.5356  0.1732 -31.9616 1.0000
  10  1   0   1  A 1.0000    COHO2  0.4474  0.3362   1.3308 0.8168
  11  1   0   1  A 2.0000    COHO2  0.5783  0.2509   2.3050 0.9788
  12  1   0   1  A 3.0000    COHO2  0.4240  0.2411   1.7581 0.9213
  13  1   0   1  A 4.0000    COHO2  0.2850  0.3616   0.7881 0.5694
  14  1   0   1  A 5.0000    COHO2  0.6357  0.4089   1.5546 0.8800
  15  1   0   1  A 6.0000    COHO2 -0.0546  0.4292  -0.1273 0.1013
  16  1   0   1  A 7.0000    COHO2  1.5141  0.6137   2.4671 0.9864
  17  1   0   1  A 8.0000    COHO2 -0.8695  1.0971  -0.7925 0.5719
  18  1   0   1  A 9.0000    COHO2  0.2966  0.3020   0.9822 0.6740
  19  1   0   1  A 1.0000    COHO3  0.7676  0.3161   2.4284 0.9848
  20  1   0   1  A 2.0000    COHO3  0.5335  0.2609   2.0446 0.9591
  21  1   0   1  A 3.0000    COHO3  0.2488  0.2590   0.9606 0.6633
  22  1   0   1  A 4.0000    COHO3  0.3212  0.3684   0.8717 0.6166
  23  1   0   1  A 5.0000    COHO3  0.8420  0.3946   2.1337 0.9671
  24  1   0   1  A 6.0000    COHO3 -0.7163  0.5605  -1.2779 0.7987
  25  1   0   1  A 7.0000    COHO3  1.1975  0.6762   1.7708 0.9234
  26  1   0   1  A 8.0000    COHO3 -8.4412 42.1447  -0.2003 0.1587
  27  1   0   1  A 9.0000    COHO3  1.2489  0.3227   3.8701 0.9999
  28  1   0   1  A 1.0000        W -0.1421  0.2620  -0.5422 0.4123
  29  1   0   1  A 2.0000        W  0.2518  0.2054   1.2259 0.7798
  30  1   0   1  A 3.0000        W  0.3492  0.2060   1.6958 0.9101
  31  1   0   1  A 4.0000        W  1.2122  0.3188   3.8026 0.9999
  32  1   0   1  A 5.0000        W  0.6285  0.3255   1.9310 0.9465
  33  1   0   1  A 6.0000        W  0.7757  0.3749   2.0691 0.9615
  34  1   0   1  A 7.0000        W -0.2436  0.5366  -0.4539 0.3501
  35  1   0   1  A 8.0000        W  1.7909  0.8673   2.0650 0.9611
  36  1   0   1  A 9.0000        W  0.5607  0.2580   2.1729 0.9702

6.17.3 Parametric Duration Dependence

The parametric family is a matter of the model number, and TDA_MODELS names them, so model = "weibull" and model = 7 are the same request. Each family has its section in the manual, with the same three covariates as rt1.cf; the boxes below are those sections’ models, one each.

Box 142The model numbers

TDA_MODELS
                 cox          exponential  exponential_periods           polynomial 
                   1                    2                    3                    4 
         polynomial2     gompertz_makeham              weibull               sickle 
                   5                    6                    7                    8 
         loglogistic         loglogistic2        loglogistic2a            lognormal 
                   9                   10                   11                   12 
   generalized_gamma          invgaussian exponential_periods2    discrete_logistic 
                  13                   14                   16                   20 
    discrete_cloglog 
                  21 

6.17.3.1 Polynomial Rates

Models 4 and 5, with degree=: the rate, or its logarithm, a polynomial in time.

Box 143Polynomial rates

poly1 <- tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d,
                  model = 4, degree = 2)
poly1
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 4, degree = 2)

Episodes: 600
Model:   4 (TDA 4)
logLik (starting values): -2514.02
logLik:  -2454.039
Converged in 7 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -4.4325 0.1031 -42.9986 1.0000
   2  1   0   1  A    COHO2  0.2452 0.0893   2.7468 0.9940
   3  1   0   1  A    COHO3  0.3411 0.0991   3.4429 0.9994
   4  1   0   1  A        W  0.3175 0.0758   4.1878 1.0000
   5  1   0   1  B   Beta-1 -0.0001 0.0000  -4.8750 1.0000
   6  1   0   1  B   Beta-2  0.0000 0.0000   3.2809 0.9990
unlist(poly1$convergence)   # what the manual prints above the estimates
             converged             iterations                problem           final_change 
                "TRUE"                    "7"                     NA                     NA 
              gradient               change.f             change.par            evaluations 
 "0.00705957041140394" "9.30147686918993e-14" "2.72933217258955e-06"                   "10" 
poly2 <- tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d,
                  model = 5, degree = 2)
poly2
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 5, degree = 2)

Episodes: 600
Model:   5 (TDA 5)
logLik (starting values): -2514.02
logLik:  -2454.54
Converged in 7 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -4.5911 0.1166 -39.3890 1.0000
   2  1   0   1  A    COHO2  0.3831 0.1132   3.3834 0.9993
   3  1   0   1  A    COHO3  0.4583 0.1178   3.8908 0.9999
   4  1   0   1  A        W  0.4173 0.0948   4.3996 1.0000
   5  1   0   1  B   Beta-1 -0.0037 0.0022  -1.7010 0.9111
   6  1   0   1  B   Beta-2 -0.0000 0.0000  -0.7764 0.5625
unlist(poly2$convergence)
             converged             iterations                problem           final_change 
                "TRUE"                    "7"                     NA                     NA 
              gradient               change.f             change.par            evaluations 
"5.24057885883376e-07" "3.43758764652829e-15" "3.81351790694824e-06"                    "8" 

6.17.3.2 Gompertz-Makeham Models

Model 6. on= says which terms of the model the covariates are linked to; the manual’s example puts them on the manual’s xb and xc.

Box 144Gompertz-Makeham (rt6.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 6,
         on = c("xb", "xc"))
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 6, on = c("xb", "xc"))

Episodes: 600
Model:   6 (TDA 6)
logLik (starting values): -2557.365
logLik:  -2453.383
Converged in 6 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  B Constant -4.5114 0.1198 -37.6503 1.0000
   2  1   0   1  B    COHO2  0.4595 0.1546   2.9716 0.9970
   3  1   0   1  B    COHO3  0.4147 0.1657   2.5025 0.9877
   4  1   0   1  B        W  0.2860 0.1280   2.2351 0.9746
   5  1   0   1  C Constant -0.0060 0.0013  -4.6239 1.0000
   6  1   0   1  C    COHO2 -0.0016 0.0022  -0.7275 0.5331
   7  1   0   1  C    COHO3  0.0011 0.0028   0.3986 0.3098
   8  1   0   1  C        W  0.0026 0.0018   1.4299 0.8472
Gompertz transition rates at three shape parameters.

Box 145plot-gm.cf

p <- tda_ps(xlim = c(0, 5), ylim = c(0, 3), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, "1+0*x1", range = c(0, 5), step = 1)
p <- tda_pl_function(p, "exp(0.2*x1)", range = c(0, 5), step = 0.1, lty = 5)
p <- tda_pl_function(p, "exp(-0.5*x1)", range = c(0, 5), step = 0.1, lty = 6)

p <- tda_pl_text(p, "c = 0.0", at = c(4.3, 1.1))
p <- tda_pl_text(p, "c = 0.2", at = c(4.3, 2.1))
p <- tda_pl_text(p, "c = -0.5", at = c(4.3, 0.2))

plot(p)

6.17.3.3 Weibull Models

Box 146Weibull (rt7.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 7)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 7)

Episodes: 600
Model:   7 (TDA 7)
logLik (starting values): -2514.02
logLik:  -2473.543
Converged in 6 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -5.0055 0.0906 -55.2452 1.0000
   2  1   0   1  A    COHO2  0.5339 0.1201   4.4448 1.0000
   3  1   0   1  A    COHO3  0.6671 0.1235   5.4003 1.0000
   4  1   0   1  A        W  0.5191 0.1012   5.1274 1.0000
   5  1   0   1  B Constant -0.0700 0.0366  -1.9119 0.9441
Weibull transition rates at five shape parameters.

Box 147plot-wei.cf

wei <- function(b) sprintf("%s*1^%s*x1^(%s-1)", b, b, b)

p <- tda_ps(xlim = c(0, 3), ylim = c(0, 3), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, "1+0*x1", range = c(0.01, 3), step = 1)
p <- tda_pl_function(p, wei(1.2), range = c(0.01, 3), step = 0.01, lty = 5)
p <- tda_pl_function(p, wei(1.5), range = c(0.01, 3), step = 0.01, lty = 6)
p <- tda_pl_function(p, wei(0.7), range = c(0.01, 3), step = 0.01, lty = 5)
p <- tda_pl_function(p, wei(0.5), range = c(0.01, 3), step = 0.01, lty = 6)

p <- tda_pl_text(p, "b = 0.0", at = c(2.6, 1.1))
p <- tda_pl_text(p, "b = 1.2", at = c(2.6, 1.6))
p <- tda_pl_text(p, "b = 1.5", at = c(2.6, 2.2))
p <- tda_pl_text(p, "b = 0.7", at = c(2.6, 0.6))
p <- tda_pl_text(p, "b = 0.5", at = c(2.6, 0.1))

plot(p)

6.17.3.4 Sickle Models

Box 148Sickle (rt8.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 8)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 8)

Episodes: 600
Model:   8 (TDA 8)
logLik (starting values): -2897.762
logLik:  -2466.1
Converged in 7 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -7.0645 0.1140 -61.9618 1.0000
   2  1   0   1  A    COHO2  0.3926 0.1123   3.4947 0.9995
   3  1   0   1  A    COHO3  0.4363 0.1162   3.7556 0.9998
   4  1   0   1  A        W  0.4232 0.0944   4.4808 1.0000
   5  1   0   1  B Constant  3.7046 0.0517  71.6511 1.0000
Sickle transition rates at three scale parameters.

Box 149plot-sic.cf

sic <- function(b) sprintf("1*x1*exp(-x1/%s)", b)

p <- tda_ps(xlim = c(0, 12), ylim = c(0, 0.8), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 0.4), ic = c(2, 4))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, sic(1.0), range = c(0, 12), step = 0.1)
p <- tda_pl_function(p, sic(0.5), range = c(0, 12), step = 0.1, lty = 5)
p <- tda_pl_function(p, sic(2.0), range = c(0, 12), step = 0.1, lty = 6)

p <- tda_pl_text(p, "b = 1.0", at = c(4, 0.11))
p <- tda_pl_text(p, "b = 0.5", at = c(2, 0.06))
p <- tda_pl_text(p, "b = 2.0", at = c(10, 0.14))

plot(p)

6.17.3.5 Log-logistic Models

Box 150Log-logistic (rt9.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 9)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 9)

Episodes: 600
Model:   9 (TDA 9)
logLik (starting values): -2521.785
logLik:  -2442.302
Converged in 5 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -4.3546 0.0998 -43.6183 1.0000
   2  1   0   1  A    COHO2  0.4792 0.1257   3.8138 0.9999
   3  1   0   1  A    COHO3  0.5927 0.1295   4.5752 1.0000
   4  1   0   1  A        W  0.3989 0.1065   3.7463 0.9998
   5  1   0   1  B Constant  0.3307 0.0385   8.5823 1.0000
Log-logistic transition rates at five shape parameters.

Box 151plot-ll.cf

ll <- function(b) sprintf("%s*x1^(%s-1)/(1+x1^%s)", b, b, b)

p <- tda_ps(xlim = c(0, 3), ylim = c(0, 2), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, ll(1.0), range = c(0.01, 3), step = 0.02)
p <- tda_pl_function(p, ll(1.5), range = c(0.01, 3), step = 0.02, lty = 5)
p <- tda_pl_function(p, ll(2.0), range = c(0.01, 3), step = 0.02, lty = 6)
p <- tda_pl_function(p, ll(0.7), range = c(0.01, 3), step = 0.02, lty = 5)
p <- tda_pl_function(p, ll(0.4), range = c(0.01, 3), step = 0.02, lty = 6)

p <- tda_pl_text(p, "0.4", at = c(2.8, 0.016), fs = 1.5)
p <- tda_pl_text(p, "0.7", at = c(2.8, 0.1), fs = 1.5)
p <- tda_pl_text(p, "1.0", at = c(2.8, 0.18), fs = 1.5)
p <- tda_pl_text(p, "1.5", at = c(2.8, 0.33), fs = 1.5)
p <- tda_pl_text(p, "2.0", at = c(2.8, 0.52), fs = 1.5)

plot(p)

6.17.3.6 Log-normal Models

Box 152Log-normal (rt12.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 12)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 12)

Episodes: 600
Model:   12 (TDA 12)
logLik (starting values): -2603.168
logLik:  -2437.524
Converged in 6 iterations

 Idx SN Org Des MT Variable   Coeff  Error C/Error Signif
   1  1   0   1  A Constant  4.3688 0.0960 45.5055 1.0000
   2  1   0   1  A    COHO2 -0.4608 0.1255 -3.6707 0.9998
   3  1   0   1  A    COHO3 -0.6110 0.1272 -4.8036 1.0000
   4  1   0   1  A        W -0.3821 0.1062 -3.5992 0.9997
   5  1   0   1  B Constant  0.2036 0.0342  5.9508 1.0000
Log-normal transition rates at three scale parameters.

Box 153plot-ln.cf

ln <- function(b) sprintf("ndf(log(x1)/%s)/(%s*x1*(1-nd(log(x1)/%s)))",
                          b, b, b)

p <- tda_ps(xlim = c(0, 7), ylim = c(0, 1.5), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 0.5))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, ln(1.0), range = c(0.02, 7), step = 0.02)
p <- tda_pl_function(p, ln(0.7), range = c(0.02, 7), step = 0.02, lty = 5)
p <- tda_pl_function(p, ln(1.3), range = c(0.02, 7), step = 0.02, lty = 6)

p <- tda_pl_text(p, "b = 1.3", at = c(6, 0.125))
p <- tda_pl_text(p, "b = 1.0", at = c(6, 0.4))
p <- tda_pl_text(p, "b = 0.7", at = c(6, 0.725))

plot(p)

6.17.3.7 Generalized Gamma Models

Box 154Generalized gamma (rt13.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 13)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 13)

Episodes: 600
Model:   13 (TDA 13)
logLik (starting values): -2514.02
logLik:  -2473.543
Converged in 6 iterations

 Idx SN Org Des MT Variable   Coeff  Error C/Error Signif
   1  1   0   1  A Constant  5.0055 0.0906 55.2452 1.0000
   2  1   0   1  A    COHO2 -0.5339 0.1201 -4.4448 1.0000
   3  1   0   1  A    COHO3 -0.6671 0.1235 -5.4003 1.0000
   4  1   0   1  A        W -0.5191 0.1012 -5.1274 1.0000
   5  1   0   1  B Constant  0.0700 0.0366  1.9119 0.9441

The generalised gamma rate at three values of the shape parameter, written out with TDA’s lgam() and icg(), the log gamma and the incomplete gamma function.

Box 155plot-g1.cf

gg <- function(k) sprintf(
    "%s^(%s-0.5)*exp(sqrt(%s)*log(x1)-%s*exp(log(x1)/sqrt(%s)))/(x1*exp(lgam(%s))*(1-icg(%s*exp(log(x1)/sqrt(%s)),%s)))",
    k, k, k, k, k, k, k, k, k)

p <- tda_ps(xlim = c(0, 5), ylim = c(0, 2), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, gg(1), range = c(0.01, 5), step = 0.02)
p <- tda_pl_function(p, gg(0.5), range = c(0.01, 5), step = 0.02, lty = 5)
p <- tda_pl_function(p, gg(10), range = c(0.01, 5), step = 0.02, lty = 6)

p <- tda_pl_text(p, "a = 0.0", at = c(0.5, 1.73))
p <- tda_pl_text(p, "b = 1.0", at = c(0.5, 1.57))
p <- tda_pl_text(p, "k = 10.0", at = c(4, 0.63))
p <- tda_pl_text(p, "k = 1.0", at = c(4, 1.07))
p <- tda_pl_text(p, "k = 0.5", at = c(4, 1.5))

plot(p)

Generalised gamma transition rates at three scale parameters.

Box 156plot-g2.cf

gg2 <- function(b) sprintf(
    "1^(1-0.5)*exp(sqrt(1)*log(x1)/%s-1*exp((log(x1)/%s)/sqrt(1)))/(x1*exp(lgam(1))*(1-icg(1*exp((log(x1)/%s)/sqrt(1)),1)))",
    b, b, b)

p <- tda_ps(xlim = c(0, 5), ylim = c(0, 3), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 1))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, gg2(1), range = c(0.01, 5), step = 0.02)
p <- tda_pl_function(p, gg2(0.7), range = c(0.01, 5), step = 0.02, lty = 5)
p <- tda_pl_function(p, gg2(2), range = c(0.01, 5), step = 0.02, lty = 6)

p <- tda_pl_text(p, "a = 0.0", at = c(0.5, 2.6))
p <- tda_pl_text(p, "k = 1.0", at = c(0.5, 2.35))
p <- tda_pl_text(p, "b = 2.0", at = c(4, 0.6))
p <- tda_pl_text(p, "b = 1.0", at = c(4, 1.1))
p <- tda_pl_text(p, "b = 0.7", at = c(4, 2))

plot(p)

6.17.3.8 Inverse Gaussian Models

Box 157Inverse Gaussian (rt14.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 14)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 14)

Episodes: 600
Model:   14 (TDA 14)
logLik (starting values): -2699.618
logLik:  -2434.345
Converged in 6 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant  0.0007 0.0015   0.4225 0.3273
   2  1   0   1  A    COHO2  0.0067 0.0024   2.8243 0.9953
   3  1   0   1  A    COHO3  0.0088 0.0026   3.4284 0.9994
   4  1   0   1  A        W  0.0093 0.0021   4.4894 1.0000
   5  1   0   1  B Constant -1.7092 0.0311 -55.0104 1.0000

The inverse Gaussian rate, written out with TDA’s ndf() and nd().

Box 158plot-ig.cf

ig <- function(a) sprintf(
    "(ndf((%s*x1-1)/sqrt(x1))/x1^(3/2))/(nd((1-%s*x1)/sqrt(x1))-exp(2*%s)*nd((-1-%s*x1)/sqrt(x1)))",
    a, a, a, a)

p <- tda_ps(xlim = c(0, 6), ylim = c(0, 2.5), width = 90, height = 50)
p <- tda_pl_axes(p, sc = c(1, 0.5))
p <- tda_pl_frame(p)
p <- tda_pl_function(p, ig(1), range = c(0.02, 6), step = 0.02)
p <- tda_pl_function(p, ig(0.1), range = c(0.02, 6), step = 0.02, lty = 5)
p <- tda_pl_function(p, ig(1.5), range = c(0.02, 6), step = 0.02, lty = 6)

p <- tda_pl_text(p, "a = 0.1", at = c(5, 0.21))
p <- tda_pl_text(p, "a = 1.0", at = c(5, 0.83))
p <- tda_pl_text(p, "a = 1.5", at = c(5, 1.46))
p <- tda_pl_text(p, "b = 1.0", at = c(5, 2.08))

plot(p)

6.17.5.1 The frml Command

tda_frml() is the rate-model counterpart of tda_fml(): the episode is named by the formula, and the likelihood is written out. tf, ts and des are available inside the block, which is what makes a model with its duration dependence writable.

Example 1 The exponential model again, now by hand.

Box 159An exponential model as frml (fml1.cf)

tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    rate = exp(a0 + COHO2 * a1 + COHO3 * a2 + W * a3)
    l1 = ifelse(DES, log(rate), 0)
    fn = l1 - rate * TFP
}, data = d, start = c(-4, 0, 0, 0))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        rate = exp(a0 + COHO2 * a1 + COHO3 * a2 + W * a3)
        l1 = ifelse(DES, log(rate), 0)
        fn = l1 - rate * TFP
    }, start = c(-4, 0, 0, 0))

Episodes: 600
logLik (starting values): -2578.948
logLik:  -2475.438
Converged in 6 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        a0 -5.0114 0.0843 -59.4446 1.0000
   2        a1  0.5341 0.1120   4.7686 1.0000
   3        a2  0.6738 0.1152   5.8472 1.0000
   4        a3  0.5065 0.0942   5.3746 1.0000

Example 2 frml1m.cf: three competing destinations, each with its own rate, the episode contributing the log of whichever rate it realised and the sum of all three to the survivor term. Its DES is rt1m.cf’s (the ge coding).

Box 160Three competing rates (frml1m.cf)

tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    rate1 = exp(a10 + COHO2 * a11 + COHO3 * a12 + W * a13)
    rate2 = exp(a20 + COHO2 * a21 + COHO3 * a22 + W * a23)
    rate3 = exp(a30 + COHO2 * a31 + COHO3 * a32 + W * a33)
    rate = rate1 + rate2 + rate3
    l1 = ifelse(DES == 1, log(rate1),
         ifelse(DES == 2, log(rate2),
         ifelse(DES == 3, log(rate3), 0)))
    fn = l1 - rate * TFP
}, data = d4m, start = rep(c(-4, 0, 0, 0), 3),
   control = tda_control(maxit = 100))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d4m, 
    definitions = {
        rate1 = exp(a10 + COHO2 * a11 + COHO3 * a12 + W * a13)
        rate2 = exp(a20 + COHO2 * a21 + COHO3 * a22 + W * a23)
        rate3 = exp(a30 + COHO2 * a31 + COHO3 * a32 + W * a33)
        rate = rate1 + rate2 + rate3
        l1 = ifelse(DES == 1, log(rate1), ifelse(DES == 2, log(rate2), 
            ifelse(DES == 3, log(rate3), 0)))
        fn = l1 - rate * TFP
    }, start = rep(c(-4, 0, 0, 0), 3), control = tda_control(maxit = 100))

Episodes: 600
logLik (starting values): -4072.845
logLik:  -2935.583
Converged in 7 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1       a10 -6.3412 0.1736 -36.5229 1.0000
   2       a11  0.3012 0.2618   1.1504 0.7500
   3       a12  0.4999 0.2651   1.8857 0.9407
   4       a13 -0.0957 0.2342  -0.4084 0.3170
   5       a20 -5.7548 0.1236 -46.5439 1.0000
   6       a21  0.6924 0.1630   4.2486 1.0000
   7       a22  0.8088 0.1684   4.8039 1.0000
   8       a23  0.2977 0.1381   2.1556 0.9689
   9       a30 -6.3686 0.1578 -40.3698 1.0000
  10       a31  0.4424 0.1924   2.2996 0.9785
  11       a32  0.5875 0.1980   2.9676 0.9970
  12       a33  1.1156 0.1659   6.7254 1.0000

Example 2a frml2.cf writes the piecewise constant model of rt2.cf out by hand: one indicator per period, and the log rate as the sum of the period constants and the covariate effects; frml2c.cf adds the constraints that equate the periods. Both reach rt2 and rt2c.

Box 161frml2.cf, frml2c.cf: the piecewise constant model, by hand

pw <- function(...) tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    bb = COHO2 * b1 + COHO3 * b2 + W * b3
    p1 = ge(tf, 0) & lt(tf, 12);  p2 = ge(tf, 12) & lt(tf, 24)
    p3 = ge(tf, 24) & lt(tf, 36); p4 = ge(tf, 36) & lt(tf, 48)
    p5 = ge(tf, 48) & lt(tf, 60); p6 = ge(tf, 60) & lt(tf, 72)
    p7 = ge(tf, 72) & lt(tf, 84); p8 = ge(tf, 84) & lt(tf, 96)
    p9 = ge(tf, 96)
    rr = ifelse(p1, a1, ifelse(p2, a2, ifelse(p3, a3, ifelse(p4, a4,
         ifelse(p5, a5, ifelse(p6, a6, ifelse(p7, a7, ifelse(p8, a8,
         ifelse(p9, a9, 0)))))))))
    s1 = 12 * exp(a1 + bb); s2 = s1 + 12 * exp(a2 + bb)
    s3 = s2 + 12 * exp(a3 + bb); s4 = s3 + 12 * exp(a4 + bb)
    s5 = s4 + 12 * exp(a5 + bb); s6 = s5 + 12 * exp(a6 + bb)
    s7 = s6 + 12 * exp(a7 + bb); s8 = s7 + 12 * exp(a8 + bb)
    lsurv = ifelse(p1, tf * exp(a1 + bb),
            ifelse(p2, s1 + (tf - 12) * exp(a2 + bb),
            ifelse(p3, s2 + (tf - 24) * exp(a3 + bb),
            ifelse(p4, s3 + (tf - 36) * exp(a4 + bb),
            ifelse(p5, s4 + (tf - 48) * exp(a5 + bb),
            ifelse(p6, s5 + (tf - 60) * exp(a6 + bb),
            ifelse(p7, s6 + (tf - 72) * exp(a7 + bb),
            ifelse(p8, s7 + (tf - 84) * exp(a8 + bb),
            ifelse(p9, s8 + (tf - 96) * exp(a9 + bb), 0)))))))))
    l1 = ifelse(DES, rr + bb, 0)
    fn = l1 - lsurv
}, data = d, start = c(rep(-4, 9), 0, 0, 0), ...)
pw()                                              # frml2.cf
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        bb = COHO2 * b1 + COHO3 * b2 + W * b3
        p1 = ge(tf, 0) & lt(tf, 12)
        p2 = ge(tf, 12) & lt(tf, 24)
        p3 = ge(tf, 24) & lt(tf, 36)
        p4 = ge(tf, 36) & lt(tf, 48)
        p5 = ge(tf, 48) & lt(tf, 60)
        p6 = ge(tf, 60) & lt(tf, 72)
        p7 = ge(tf, 72) & lt(tf, 84)
        p8 = ge(tf, 84) & lt(tf, 96)
        p9 = ge(tf, 96)
        rr = ifelse(p1, a1, ifelse(p2, a2, ifelse(p3, a3, ifelse(p4, 
            a4, ifelse(p5, a5, ifelse(p6, a6, ifelse(p7, a7, 
                ifelse(p8, a8, ifelse(p9, a9, 0)))))))))
        s1 = 12 * exp(a1 + bb)
        s2 = s1 + 12 * exp(a2 + bb)
        s3 = s2 + 12 * exp(a3 + bb)
        s4 = s3 + 12 * exp(a4 + bb)
        s5 = s4 + 12 * exp(a5 + bb)
        s6 = s5 + 12 * exp(a6 + bb)
        s7 = s6 + 12 * exp(a7 + bb)
        s8 = s7 + 12 * exp(a8 + bb)
        lsurv = ifelse(p1, tf * exp(a1 + bb), ifelse(p2, s1 + 
            (tf - 12) * exp(a2 + bb), ifelse(p3, s2 + (tf - 24) * 
            exp(a3 + bb), ifelse(p4, s3 + (tf - 36) * exp(a4 + 
            bb), ifelse(p5, s4 + (tf - 48) * exp(a5 + bb), ifelse(p6, 
            s5 + (tf - 60) * exp(a6 + bb), ifelse(p7, s6 + (tf - 
                72) * exp(a7 + bb), ifelse(p8, s7 + (tf - 84) * 
                exp(a8 + bb), ifelse(p9, s8 + (tf - 96) * exp(a9 + 
                bb), 0)))))))))
        l1 = ifelse(DES, rr + bb, 0)
        fn = l1 - lsurv
    }, start = c(rep(-4, 9), 0, 0, 0))

Episodes: 600
logLik (starting values): -2578.948
logLik:  -2433.376
Converged in 7 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        a1 -5.2070 0.1520 -34.2464 1.0000
   2        a2 -4.5473 0.1312 -34.6574 1.0000
   3        a3 -4.2675 0.1296 -32.9389 1.0000
   4        a4 -4.7784 0.1690 -28.2758 1.0000
   5        a5 -4.7300 0.1779 -26.5924 1.0000
   6        a6 -4.7862 0.1987 -24.0857 1.0000
   7        a7 -5.1366 0.2523 -20.3568 1.0000
   8        a8 -6.0783 0.4137 -14.6917 1.0000
   9        a9 -5.4198 0.1281 -42.3123 1.0000
  10        b1  0.4367 0.1133   3.8534 0.9999
  11        b2  0.5162 0.1182   4.3688 1.0000
  12        b3  0.4336 0.0949   4.5687 1.0000
pw(constraints = sprintf("b%d - b%d = 0", 2:9, 1:8))   # frml2c.cf
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        bb = COHO2 * b1 + COHO3 * b2 + W * b3
        p1 = ge(tf, 0) & lt(tf, 12)
        p2 = ge(tf, 12) & lt(tf, 24)
        p3 = ge(tf, 24) & lt(tf, 36)
        p4 = ge(tf, 36) & lt(tf, 48)
        p5 = ge(tf, 48) & lt(tf, 60)
        p6 = ge(tf, 60) & lt(tf, 72)
        p7 = ge(tf, 72) & lt(tf, 84)
        p8 = ge(tf, 84) & lt(tf, 96)
        p9 = ge(tf, 96)
        rr = ifelse(p1, a1, ifelse(p2, a2, ifelse(p3, a3, ifelse(p4, 
            a4, ifelse(p5, a5, ifelse(p6, a6, ifelse(p7, a7, 
                ifelse(p8, a8, ifelse(p9, a9, 0)))))))))
        s1 = 12 * exp(a1 + bb)
        s2 = s1 + 12 * exp(a2 + bb)
        s3 = s2 + 12 * exp(a3 + bb)
        s4 = s3 + 12 * exp(a4 + bb)
        s5 = s4 + 12 * exp(a5 + bb)
        s6 = s5 + 12 * exp(a6 + bb)
        s7 = s6 + 12 * exp(a7 + bb)
        s8 = s7 + 12 * exp(a8 + bb)
        lsurv = ifelse(p1, tf * exp(a1 + bb), ifelse(p2, s1 + 
            (tf - 12) * exp(a2 + bb), ifelse(p3, s2 + (tf - 24) * 
            exp(a3 + bb), ifelse(p4, s3 + (tf - 36) * exp(a4 + 
            bb), ifelse(p5, s4 + (tf - 48) * exp(a5 + bb), ifelse(p6, 
            s5 + (tf - 60) * exp(a6 + bb), ifelse(p7, s6 + (tf - 
                72) * exp(a7 + bb), ifelse(p8, s7 + (tf - 84) * 
                exp(a8 + bb), ifelse(p9, s8 + (tf - 96) * exp(a9 + 
                bb), 0)))))))))
        l1 = ifelse(DES, rr + bb, 0)
        fn = l1 - lsurv
    }, start = c(rep(-4, 9), 0, 0, 0), constraints = ..1)

Episodes: 600
logLik (starting values): -2578.948
logLik:  -2475.438
Converged in 6 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        a1 -5.0114 0.0843 -59.4446 1.0000
   2        a2 -5.0114 0.0843 -59.4446 1.0000
   3        a3 -5.0114 0.0843 -59.4446 1.0000
   4        a4 -5.0114 0.0843 -59.4446 1.0000
   5        a5 -5.0114 0.0843 -59.4446 1.0000
   6        a6 -5.0114 0.0843 -59.4446 1.0000
   7        a7 -5.0114 0.0843 -59.4446 1.0000
   8        a8 -5.0114 0.0843 -59.4446 1.0000
   9        a9 -5.0114 0.0843 -59.4446 1.0000
  10        b1  0.5341 0.1120   4.7686 1.0000
  11        b2  0.6738 0.1152   5.8472 1.0000
  12        b3  0.5065 0.0942   5.3746 1.0000

Example 3 A Weibull and a log-normal, written out. The second uses nd and ndf, TDA’s normal distribution and density operators.

Box 162Weibull and log-normal, written out

tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    aa = exp(a0 + COHO2 * a1 + COHO3 * a2 + W * a3)
    bb = exp(b0)
    l1 = ifelse(DES, log(bb * aa^bb * tf^(bb - 1)), 0)
    fn = l1 - (aa * tf)^bb
}, data = d, start = c(-4, 0, 0, 0, 0), control = tda_control(maxit = 100))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        aa = exp(a0 + COHO2 * a1 + COHO3 * a2 + W * a3)
        bb = exp(b0)
        l1 = ifelse(DES, log(bb * aa^bb * tf^(bb - 1)), 0)
        fn = l1 - (aa * tf)^bb
    }, start = c(-4, 0, 0, 0, 0), control = tda_control(maxit = 100))

Episodes: 600
logLik (starting values): -2578.948
logLik:  -2473.543
Converged in 6 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1        a0 -5.0055 0.0906 -55.2452 1.0000
   2        a1  0.5339 0.1201   4.4448 1.0000
   3        a2  0.6671 0.1235   5.4003 1.0000
   4        a3  0.5191 0.1012   5.1274 1.0000
   5        b0 -0.0700 0.0366  -1.9119 0.9441
tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    aa = a0 + COHO2 * a1 + COHO3 * a2 + W * a3
    bb = exp(b0)
    zz = (log(tf) - aa) / bb
    qz = 1 - nd(zz)
    vz = ndf(zz) / qz
    l1 = ifelse(DES, log(vz / (bb * tf)), 0)
    fn = l1 + log(qz)
}, data = d, start = c(4, 0, 0, 0, 0), control = tda_control(maxit = 100))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        aa = a0 + COHO2 * a1 + COHO3 * a2 + W * a3
        bb = exp(b0)
        zz = (log(tf) - aa)/bb
        qz = 1 - nd(zz)
        vz = ndf(zz)/qz
        l1 = ifelse(DES, log(vz/(bb * tf)), 0)
        fn = l1 + log(qz)
    }, start = c(4, 0, 0, 0, 0), control = tda_control(maxit = 100))

Episodes: 600
logLik (starting values): -2493.477
logLik:  -2437.524
Converged in 6 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1        a0  4.3688 0.0960 45.5055 1.0000
   2        a1 -0.4608 0.1255 -3.6707 0.9998
   3        a2 -0.6110 0.1272 -4.8035 1.0000
   4        a3 -0.3821 0.1062 -3.5992 0.9997
   5        b0  0.2036 0.0342  5.9508 1.0000

Example 4 frml5.cf is the generalized gamma model written out, with icg, the incomplete gamma integral, and lgam; with the shape kk = 1 it is the Weibull of Example 3, which is what the box shows.

Box 163frml5.cf: the generalized gamma, written out

tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    kk = 1
    aa = a0 + COHO2 * a1 + COHO3 * a2 + W * a3
    bb = exp(b0)
    zz = (log(tf) - aa) / bb
    qt = kk * exp(zz / sqrt(kk))
    rr = (kk - 0.5) * log(kk) + (sqrt(kk) * zz - qt) - (b0 + log(tf) + lgam(kk))
    icc = icg(qt, kk)
    fn = ifelse(DES, rr, log(1 - icc))
}, data = d, start = c(5, 0, 0, 0, 0), control = tda_control(algorithm = 4))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        kk = 1
        aa = a0 + COHO2 * a1 + COHO3 * a2 + W * a3
        bb = exp(b0)
        zz = (log(tf) - aa)/bb
        qt = kk * exp(zz/sqrt(kk))
        rr = (kk - 0.5) * log(kk) + (sqrt(kk) * zz - qt) - (b0 + 
            log(tf) + lgam(kk))
        icc = icg(qt, kk)
        fn = ifelse(DES, rr, log(1 - icc))
    }, start = c(5, 0, 0, 0, 0), control = tda_control(algorithm = 4))

Episodes: 600
logLik (starting values): -2564.787
logLik:  -2473.543
Converged in 20 iterations

 Idx Parameter   Coeff  Error C/Error Signif
   1        a0  5.0055 0.0906 55.2452 1.0000
   2        a1 -0.5339 0.1201 -4.4448 1.0000
   3        a2 -0.6671 0.1235 -5.4003 1.0000
   4        a3 -0.5191 0.1012 -5.1274 1.0000
   5        b0  0.0700 0.0366  1.9119 0.9441

6.17.5.2 The Hernes Model

Box 164The Hernes model (frml8.cf)

tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    sigma = exp(s)
    lambda = exp(l) / (1 + exp(l))
    beta = exp(b)
    ff = sigma * exp(beta * lambda^tf)
    ff1 = ff + 1
    dens = -(ff * beta * lambda^tf * log(lambda)) / (ff1 * ff1)
    surv = 1 - 1 / ff1
    fn = ifelse(des, log(dens), log(surv))
}, data = d, control = tda_control(maxit = 100, algorithm = 4))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        sigma = exp(s)
        lambda = exp(l)/(1 + exp(l))
        beta = exp(b)
        ff = sigma * exp(beta * lambda^tf)
        ff1 = ff + 1
        dens = -(ff * beta * lambda^tf * log(lambda))/(ff1 * 
            ff1)
        surv = 1 - 1/ff1
        fn = ifelse(des, log(dens), log(surv))
    }, control = tda_control(maxit = 100, algorithm = 4))

Episodes: 600
logLik (starting values): -16552.96
logLik:  -2526.374
Converged in 22 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1         b  1.5856 0.0378  41.9814 1.0000
   2         l  3.8236 0.0599  63.8607 1.0000
   3         s -1.8379 0.1305 -14.0821 1.0000

6.17.5.3 The Coale-McNeil Model

The manual’s frml9.cf gives two starting values for three parameters and lets TDA pad the third with zero, which TDA warns about; the same values, spelled out, reach the same estimates without the warning.

Box 165The Coale-McNeil model (frml9.cf)

tda_frml(Surv(TFP, DES) ~ COHO2 + COHO3 + W, {
    bg = 0.604
    sigma = exp(s) / (1 + exp(s))
    lambda = exp(l)
    mue = exp(m)
    beta = bg * lambda
    tl = exp(-lambda * (tf - mue))
    ldens = log(sigma) + l - beta * (tf - mue) - tl - lgam(bg)
    surv = 1 - sigma * (1 - icg(tl, bg))
    fn = ifelse(DES, ldens, log(surv))
}, data = d, start = c(l = -3, m = 2, s = 0),
   control = tda_control(maxit = 100, algorithm = 4))
Call: tda_frml(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    definitions = {
        bg = 0.604
        sigma = exp(s)/(1 + exp(s))
        lambda = exp(l)
        mue = exp(m)
        beta = bg * lambda
        tl = exp(-lambda * (tf - mue))
        ldens = log(sigma) + l - beta * (tf - mue) - tl - lgam(bg)
        surv = 1 - sigma * (1 - icg(tl, bg))
        fn = ifelse(DES, ldens, log(surv))
    }, start = c(l = -3, m = 2, s = 0), control = tda_control(maxit = 100, 
        algorithm = 4))

Episodes: 600
logLik (starting values): -2699.607
logLik:  -2529.593
Converged in 13 iterations

 Idx Parameter   Coeff  Error  C/Error Signif
   1         l -3.0653 0.0454 -67.4629 1.0000
   2         m  2.8902 0.0685  42.1764 1.0000
   3         s  1.8505 0.1421  13.0217 1.0000

6.17.6.2 Logistic Regression Models

Model 20: the discrete-time logistic model, the rate at each time point a logit in the covariates.

Box 166Discrete time logistic

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 20)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 20)

Episodes: 600
Model:   20 (TDA 20)
logLik (starting values): -2511.467
logLik:  -2472.389
Converged in 5 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -5.0056 0.0847 -59.1117 1.0000
   2  1   0   1  A    COHO2  0.5402 0.1127   4.7944 1.0000
   3  1   0   1  A    COHO3  0.6822 0.1160   5.8808 1.0000
   4  1   0   1  A        W  0.5133 0.0949   5.4097 1.0000

6.17.6.3 Complementary Log-Log Models

Model 21.

Box 167Complementary log-log

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 21)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 21)

Episodes: 600
Model:   21 (TDA 21)
logLik (starting values): -2511.446
logLik:  -2472.421
Converged in 5 iterations

 Idx SN Org Des MT Variable   Coeff  Error  C/Error Signif
   1  1   0   1  A Constant -5.0081 0.0843 -59.4116 1.0000
   2  1   0   1  A    COHO2  0.5371 0.1120   4.7959 1.0000
   3  1   0   1  A    COHO3  0.6773 0.1152   5.8768 1.0000
   4  1   0   1  A        W  0.5094 0.0942   5.4044 1.0000

6.17.7.1 Partial Likelihood Estimation

Model 1 is the Cox model, estimated by partial likelihood, here with one transition and then with three.

Box 168pl1.cf and pl1m.cf: Cox models with one and three transitions

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 1)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, 
    model = 1)

Episodes: 600
Model:   1 (TDA 1)
logLik (starting values): -2584.57
logLik:  -2565.426
Converged in 4 iterations

 Idx SN Org Des MT Variable  Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2 0.3797 0.1134  3.3501 0.9992
   2  1   0   1  A    COHO3 0.4585 0.1177  3.8937 0.9999
   3  1   0   1  A        W 0.4078 0.0949  4.2988 1.0000
tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d4m, model = 1)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d4m, 
    model = 1)

Episodes: 600
Model:   1 (TDA 1)
logLik (starting values): -2584.57
logLik:  -2550.89
Converged in 4 iterations
Transitions: 0->1, 0->2, 0->3 

 Idx SN Org Des MT Variable   Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2  0.0893 0.2640  0.3381 0.2647
   2  1   0   1  A    COHO3  0.1624 0.2696  0.6024 0.4531
   3  1   0   1  A        W -0.2411 0.2352 -1.0254 0.6948
   4  1   0   2  A    COHO2  0.5017 0.1641  3.0564 0.9978
   5  1   0   2  A    COHO3  0.5487 0.1714  3.2010 0.9986
   6  1   0   2  A        W  0.1879 0.1387  1.3547 0.8245
   7  1   0   3  A    COHO2  0.3726 0.1964  1.8966 0.9421
   8  1   0   3  A    COHO3  0.4909 0.2039  2.4069 0.9839
   9  1   0   3  A        W  1.0651 0.1678  6.3482 1.0000

6.17.7.2 Time-dependent Covariates

define= writes a covariate as a TDA expression in time, evaluated at every risk set rather than once per episode, and helpers= names the columns that expression needs. MDATE does not depend on time and so is computed in R; MARR does, and so is defined for TDA.

Box 169A time-dependent covariate

d$MDATE <- ifelse(d$TMAR <= 0, 10000, d$TMAR - d$TStart)
tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W + MARR, data = d, model = 1,
         define = list(MARR = "gt(time,MDATE)"), helpers = "MDATE")
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W + MARR, 
    data = d, model = 1, define = list(MARR = "gt(time,MDATE)"), 
    helpers = "MDATE")

Episodes: 600
Model:   1 (TDA 1)
logLik (starting values): -2584.57
logLik:  -2562.509
Converged in 4 iterations

 Idx SN Org Des MT Variable   Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2  0.3920 0.1135  3.4548 0.9994
   2  1   0   1  A    COHO3  0.4668 0.1179  3.9604 0.9999
   3  1   0   1  A        W  0.3947 0.0950  4.1543 1.0000
   4  1   0   1  A     MARR -0.2408 0.1000 -2.4083 0.9840

6.17.7.3 Episode Splitting

pl4.cf splits every episode at the date of marriage (split = MDATE in edef) and writes the pieces out; pl5.cf reads them back and fits the Cox model twice, without and with MARR, the piece’s marriage status. tda_split(at=) is the split, and the pieces are left truncated, so the model is told Surv(ts, tf, des).

Box 170pl4.cf, pl5.cf: episode splitting

d$MDATE <- ifelse(d$TMAR <= 0, 10000, d$TMAR - d$TStart)
sp <- tda_split(Surv(TFP, DES) ~ COHO2 + COHO3 + W, d, at = "MDATE")
sp$MARR <- as.integer(sp$MDATE <= sp$ts)
nrow(sp)
[1] 761
tda_rate(Surv(ts, tf, des) ~ COHO2 + COHO3 + W, sp, model = 1)
Call: tda_rate(formula = Surv(ts, tf, des) ~ COHO2 + COHO3 + W, data = sp, 
    model = 1)

Episodes: 761
Model:   1 (TDA 1)
logLik (starting values): -2584.57
logLik:  -2565.426
Converged in 4 iterations

 Idx SN Org Des MT Variable  Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2 0.3797 0.1134  3.3501 0.9992
   2  1   0   1  A    COHO3 0.4585 0.1177  3.8937 0.9999
   3  1   0   1  A        W 0.4078 0.0949  4.2988 1.0000
tda_rate(Surv(ts, tf, des) ~ COHO2 + COHO3 + W + MARR, sp, model = 1)
Call: tda_rate(formula = Surv(ts, tf, des) ~ COHO2 + COHO3 + W + MARR, 
    data = sp, model = 1)

Episodes: 761
Model:   1 (TDA 1)
logLik (starting values): -2584.57
logLik:  -2562.509
Converged in 4 iterations

 Idx SN Org Des MT Variable   Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2  0.3920 0.1135  3.4548 0.9994
   2  1   0   1  A    COHO3  0.4668 0.1179  3.9604 0.9999
   3  1   0   1  A        W  0.3947 0.0950  4.1543 1.0000
   4  1   0   1  A     MARR -0.2408 0.1000 -2.4083 0.9840

6.17.7.4 The Proportionality Assumption

The usual test is an interaction with log time: if the effect is proportional, its coefficient is zero.

Box 171A test of proportionality (pl6.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W + WTEST, data = d, model = 1,
         define = list(WTEST = "W * (log(time) - 4.22)"))
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + W + WTEST, 
    data = d, model = 1, define = list(WTEST = "W * (log(time) - 4.22)"))

Episodes: 600
Model:   1 (TDA 1)
logLik (starting values): -2584.57
logLik:  -2562.903
Converged in 4 iterations

 Idx SN Org Des MT Variable  Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2 0.3816 0.1133  3.3682 0.9992
   2  1   0   1  A    COHO3 0.4381 0.1180  3.7136 0.9998
   3  1   0   1  A        W 0.5840 0.1215  4.8074 1.0000
   4  1   0   1  A    WTEST 0.2196 0.0974  2.2541 0.9758

The same question asked by splitting the time axis: tp= estimates within periods.

Box 172The effect within periods (pl7.cf)

rrdat5 <- read.table(ex("rrdat.5"), col.names = c("TF", "DES", "G"))
rrdat5$G1 <- as.integer(rrdat5$G == 1)
pl7 <- tda_rate(Surv(TF, DES) ~ G1, data = rrdat5, model = 1,
                tp = c(0, 170.1, 354.1, 535.1))
pl7
Call: tda_rate(formula = Surv(TF, DES) ~ G1, data = rrdat5, model = 1, 
    tp = c(0, 170.1, 354.1, 535.1))

Episodes: 90
Model:   1 (TDA 1)
logLik (starting values): -283.3952
logLik:  -282.7441
Converged in 3 iterations

 Idx SN Org Des MT Variable   Coeff  Error C/Error Signif
   1  1   0   1  A       G1 -0.2666 0.2332 -1.1428 0.7469
pl7$gof
  sn org des statistic df significance
1  1   0   1  10.23511  3    0.9833302

$gof holds the global goodness-of-fit statistic that comes with a period split – the manual’s Box 5, from pl8.cf for the single transition and pl8m.cf for the three.

Box 173pl8.cf, pl8m.cf: goodness of fit over periods

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 1,
         tp = seq(0, 96, 12))$gof
  sn org des statistic df significance
1  1   0   1  37.10844 24    0.9573181
tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d4m, model = 1,
         tp = seq(0, 96, 12))$gof
  sn org des statistic df significance
1  1   0   1  15.90665 24    0.1085840
2  1   0   2  25.54543 24    0.6233751
3  1   0   3  38.57908 24    0.9697826

6.17.7.5 Stratification

strata() on the right hand side is survival’s spelling and TDA’s grp=: a separate baseline rate per stratum, one set of coefficients.

Box 174A stratified Cox model (pl9.cf)

tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + strata(SEX), data = d, model = 1)
Call: tda_rate(formula = Surv(TFP, DES) ~ COHO2 + COHO3 + strata(SEX), 
    data = d, model = 1)

Episodes: 600
Model:   1 (TDA 1)
logLik (starting values): -2259.567
logLik:  -2250.583
Converged in 4 iterations

 Idx SN Org Des MT Variable  Coeff  Error C/Error Signif
   1  1   0   1  A    COHO2 0.3840 0.1133  3.3878 0.9993
   2  1   0   1  A    COHO3 0.4428 0.1183  3.7433 0.9998

6.17.7.6 The Baseline Rate

prate= evaluates the baseline rate at a covariate vector.

Box 175The baseline rate at given covariate values (pl1r.cf)

fit <- tda_rate(Surv(TFP, DES) ~ COHO2 + COHO3 + W, data = d, model = 1,
                prate = list(COHO3 = 1, W = 1))
head(fit$rates)
  ID Time Events Censored RiskSet    Surv.F     CumRate BaselineRate
1  0    0      0        0     600 1.0000000 0.000000000  0.000000000
2  0    2      2        0     600 0.9950713 0.004940857  0.001038902
3  0    3      5        1     597 0.9827884 0.017361412  0.005223277
4  0    4      9        2     590 0.9607712 0.040018982  0.009528300
5  0    5      3        0     581 0.9534437 0.047674879  0.003219573
6  0    6     10        1     577 0.9292075 0.073423260  0.010828094

6.18.2 Investigating Events

Event sequence data are given as a list of column groups, one per sequence data structure – the manual’s seq8.cf defines two from seq.d4, Y0..Y5 and S0..S5. tda_seqev() counts the events (Box 3), and tda_seqevd() their distribution over time for the first structure (Box 4).

Box 176seq_info, seqev and seqevd

seqd4 <- read.table(ex("seq.d4"))
names(seqd4) <- c("ID", paste0("Y", 0:5), paste0("S", 0:5), "V1", "V2")
multi <- list(paste0("Y", 0:5), paste0("S", 0:5))

tda_seq_info(multi, data = seqd4)
  sn type variables tmin tmax nstates states
1  1    1         6    0    5       2   1, 2
2  2    1         6    0    5       2   1, 3
tda_seqev(multi, data = seqd4)
  from to count
1    1  2     4
2    2  1     3
tda_seqevd(multi, data = seqd4)
  time cases ev1_2 ev2_1 total
1    1     3     1     0     1
2    2     3     1     2     3
3    3     3     0     0     0
4    4     3     1     1     2
5    5     3     1     0     1

6.18.3 Data Generation

tda_seqmd() turns sequences into episode data around chosen events, which is how a sequence becomes something a rate model can take.

Box 177Episodes derived from sequences

tda_seqmd(paste0("Y", 0:5), event = c(1, 2), data = seqd4, summary = TRUE)
  time riskset events
1    1       2      1
2    2       1      1
3    3       2      0
4    4       2      1
5    5       2      1
tda_seqmd(paste0("Y", 0:5), event = c(1, 2), data = seqd4)
  id time event period1 period2 period3 period4 period5
1  1    1     0       1       0       0       0       0
2  1    2     1       0       1       0       0       0
3  1    5     0       0       0       0       0       1
4  2    1     1       1       0       0       0       0
5  2    3     0       0       0       1       0       0
6  2    4     0       0       0       0       1       0
7  2    5     1       0       0       0       0       1
8  3    3     0       0       0       1       0       0
9  3    4     1       0       0       0       1       0
tda_seqmd(paste0("Y", 0:5), event = c(1, 2), data = seqd4,
           covariates = c("V1", "V2"))
  id time event period1 period2 period3 period4 period5 V1  V2
1  1    1     0       1       0       0       0       0  1 -10
2  1    2     1       0       1       0       0       0  1 -10
3  1    5     0       0       0       0       0       1  1 -10
4  2    1     1       1       0       0       0       0  2 -15
5  2    3     0       0       0       1       0       0  2 -15
6  2    4     0       0       0       0       1       0  2 -15
7  2    5     1       0       0       0       0       1  2 -15
8  3    3     0       0       0       1       0       0  2 -20
9  3    4     1       0       0       0       1       0  2 -20

xe= adds time-varying covariates derived from a date: xe=[V1],[V2] (the manual’s seq8c.cf, Box 4) gives, for each episode, the process time since the date each variable holds – event_covariates=, named after the columns TDA writes.

Box 178seqmd with xe= (seq8c.cf)

tda_seqmd(paste0("Y", 0:5), event = c(1, 2), data = seqd4,
           covariates = c("V1", "V2"),
           event_covariates = list(V1_D = "V1", V2_D = "V2"))
  id time event period1 period2 period3 period4 period5 V1  V2 V1_D V2_D
1  1    1     0       1       0       0       0       0  1 -10    0   11
2  1    2     1       0       1       0       0       0  1 -10    1   12
3  1    5     0       0       0       0       0       1  1 -10    4   15
4  2    1     1       1       0       0       0       0  2 -15   -1   16
5  2    3     0       0       0       1       0       0  2 -15    1   18
6  2    4     0       0       0       0       1       0  2 -15    2   19
7  2    5     1       0       0       0       0       1  2 -15    3   20
8  3    3     0       0       0       1       0       0  2 -20    1   23
9  3    4     1       0       0       0       1       0  2 -20    2   24

A third form of xe= counts the events of a given type in another sequence structure before the present time and the time since the last one, with effect-shape dummies (seq8d.cf, the manual’s Box 5): [1,2,1,4] is the event 1 -> 2 in structure 1, with 4 dummies.

Box 179seqmd with event counting (seq8d.cf)

tda_seqmd(paste0("Y", 0:5), event = c(1, 2), data = seqd4,
           covariates = c("V1", "V2"),
           event_covariates = list(V1_D = "V1", V2_D = "V2"),
           options = list(xe = "[V1],[V2],[1,2,1,4]"))
  id time event period1 period2 period3 period4 period5 V1  V2 V1_D V2_D E_2_1 ED_2_1 E0_2_1 E1_2_1
1  1    1     0       1       0       0       0       0  1 -10    0   11     0     -1      0      0
2  1    2     1       0       1       0       0       0  1 -10    1   12     0     -1      0      0
3  1    5     0       0       0       0       0       1  1 -10    4   15     1      4      0      1
4  2    1     1       1       0       0       0       0  2 -15   -1   16     0     -1      0      0
5  2    3     0       0       0       1       0       0  2 -15    1   18     1      2      0      1
6  2    4     0       0       0       0       1       0  2 -15    2   19     1      2      0      0
7  2    5     1       0       0       0       0       1  2 -15    3   20     1      2      0      0
8  3    3     0       0       0       1       0       0  2 -20    1   23     1      2      0      1
9  3    4     1       0       0       0       1       0  2 -20    2   24     1      2      0      0
  E2_2_1 E3_2_1 E4_2_1
1      0      0      0
2      0      0      0
3      0      0      0
4      0      0      0
5      0      0      0
6      1      0      0
7      0      1      0
8      0      0      0
9      1      0      0

6.19.1 Contingency Tables

Box 180loglin on a contingency table

ll1 <- read.table(ex("ll1.dat"), col.names = c("X1", "X2", "H"))
tda_loglin(~ X1 + X2, data = ll1, weights = "H")$table
  Idx X1 X2 H
1   1  1  2 3
2   2  1  3 0
3   3  1  4 0
4   4  2  2 0
5   5  2  3 0
6   6  2  4 3
7   7  5  2 0
8   8  5  3 2
9   9  5  4 0

6.19.2 Loglinear Models

ll2.dat carries a fourth column, S, the scale factors of 6.19.1’s structural zeros; ll2.cf defines it and does not use it, and the residual table’s Scale column is all ones accordingly. scale= would pass it.

Box 181A loglinear model with residuals

ll2 <- read.table(ex("ll2.dat"), col.names = c("X1", "X2", "F", "S"))
fit <- tda_loglin(~ X1 + X2, data = ll2, weights = "F", residuals = TRUE)
fit
Call: tda_loglin(formula = ~X1 + X2, data = ll2, weights = "F", residuals = TRUE)

Cases: 6 

 Idx X1 X2 F
   1  1  1 3
   2  1  2 4
   3  2  1 0
   4  2  2 2
   5  3  1 3
   6  3  2 5

Model: ~X1 + X2
Likelihood Ratio Statistic: 1.928719  Prob: 0.9643594
Degrees of Freedom: 2

 idx parameter      coeff     error    t_stat    signif
   0  Constant  0.8344470        NA        NA        NA
   1     X1[1]  0.3730772 0.3646046  1.023238 0.6938046
   2     X1[2] -0.8796858 0.5019802 -1.752431 0.9203003
   3     X2[1] -0.3030679 0.2537596 -1.194311 0.7676437
coef(fit)
  Constant      X1[1]      X1[2]      X2[1] 
 0.8344470  0.3730772 -0.8796858 -0.3030679 
head(residuals(fit))
  Idx X1 X2 Observed    Fitted   Residual Scale
1   1  1  1        3 2.4705882  0.5459413     1
2   2  1  2        4 4.5294118 -0.5459413     1
3   3  2  1        0 0.7058824 -1.1119189     1
4   4  2  2        2 1.2941176  1.1119189     1
5   5  3  1        3 2.8235294  0.1794351     1
6   6  3  2        5 5.1764706 -0.1794351     1

7 Relational Data

The manual’s example graph is gd1.dat: an edge list I, J with two value variables, V1 and V2, so every node count comes once per value variable. tda_graph() is the gdd command.

7.2.1.1 Degree of Nodes

gni reports each node of the current graph: its number, its label, and per value variable the in-degree, out-degree and number of loops; tda_g_degrees() is gni, and gd4.cf runs it on gd1.dat. A loop (node 8 to itself) is counted in its own column and not in the degrees: the manual’s Box 1 prints node 8’s first-graph degrees as 1 and 1, while gd4.cf run through TDA gives 0 and 0, as does a build of Rohwer’s unmodified sources, so the box is out of date.

Box 182gni: in-degree, out-degree and loops per value variable (gd4.cf)

gd1 <- read.table(ex("gd1.dat"), col.names = c("I", "J", "V1", "V2"))
g1 <- tda_graph(gd1, directed = TRUE)
tda_g_degrees(g1)
  index node in_degree1 out_degree1 loops1 in_degree2 out_degree2 loops2 degree
1     1    1          1           2      0          2           0      0      3
2     2    5          1           0      0          0           1      0      1
3     3    7          1           1      0          1           1      0      2
4     4    8          0           0      1          0           1      0      0
5     5    9          0           0      0          0           0      0      0
6     6   11          0           1      0          1           0      0      1
7     7   12          1           0      0          0           1      0      1

tda_g_links() is gdln: for each node the nodes it links to, one ragged record per node with any forward links.

Box 183gdln: forward links

tda_g_links(g1)$table                     # forward links
  index node n_links links
1     1    1       2  5, 7
2     3    7       1     1
3     4    8       1     8
4     6   11       1    12
tda_g_links(g1, backward = TRUE)$table    # and backward
  V1 V2 V3 V4
1  1  1  1  7
2  2  5  1  1
3  3  7  1  1
4  4  8  1  8
5  7 12  1 11

7.2.2.1 Topological Sorting

tda_g_toposort() is gsort, on the manual’s gd6.dat: a directed acyclic graph, its nodes in an order that respects every edge.

Box 184gsort (gd22.cf)

gd6 <- read.table(ex("gd6.dat"), col.names = c("I", "J", "V"))
gsrt <- tda_graph(gd6, directed = TRUE)
tda_g_toposort(gsrt)$table
  i node_i label_i
1 1      1       4
2 2      2       5
3 3      4       1
4 4      5       2
5 5      7       3
# option 2 relabels the graph's edges, keeping their values
tda_g_toposort(gsrt, relabel_graph = TRUE)$table
  label_i label_j node_i node_j value
1       1       2      4      5     6
2       1       3      4      7     5
3       1       5      4      2     7
4       2       3      5      7     1
5       2       4      5      1     3
6       3       4      7      1     2
7       3       5      7      2     4
8       4       5      1      2     8

7.2.3.1 Connected Components

tda_g_components() is gcon. gd7.cf declares gd1.dat undirected and valued (gt = 2) and asks for the components of its second graph, the one V2 defines (gn = 2): node 8 joins component 1 through the edge 8-7 that V2 carries and V1 does not.

Box 185gcon: components of graph 2 (gd7.cf)

gcomp <- tda_graph(gd1, directed = FALSE)
tda_g_components(gcomp, gn = 2)$table
  component n i node_i
1         1 4 1      1
2         1 4 2      5
3         1 4 3      7
4         1 4 4      8
5         2 1 5      9
6         3 2 6     11
7         3 2 7     12
# option 2 lists each component's edges instead, with their values
tda_g_components(gcomp, gn = 2, edges = TRUE)$table
  component n i j node_i node_j value
1         1 4 1 2      1      5     4
2         1 4 1 3      1      7    15
3         1 4 3 4      7      8     0
4         2 1 5 5      9      9    -1
5         3 2 6 7     11     12    14

7.2.3.2 Reachable Nodes in Directed Graphs

gdcon (tda_g(g, "gdcon")) is the directed counterpart of gcon: strongly connected components (opt = 1) or, with opt = 2, the nodes each node reaches. gd8.cf runs it on graph 2 of gd1.dat.

Box 186gdcon: reachable nodes (gd8.cf)

g1d <- tda_graph(gd1, directed = TRUE)
tda_g(g1d, "gdcon", list(gn = 2, opt = 2))$table
4 records, lengths 4-5
[1]  2 5 1 1 
[2]  3 7 1 1 
[3]  4 8 2 1 7 
[4]   7 12  1 11 

7.2.3.3 Cut Nodes and Blocks

tda_g_cutpoints() is gcut, on gd8.dat: the nodes whose removal disconnects the graph, and with opt = 2 the blocks.

Box 187gcut (gd26.cf)

gd8 <- read.table(ex("gd8.dat"), col.names = c("I", "J", "V"))
g8 <- tda_graph(gd8, directed = FALSE)
tda_g_cutpoints(g8)$table
2 records, lengths 3-5
[1]  1 5 2 2 4 
[2]  2 3 0 
tda_g_cutpoints(g8, opt = 2)$table
  V1 V2 V3 V4 V5
1  1  1  1  2  0
2  1  2  2  2  1
3  1  3  3  2  0
4  1  4  4  2  1
5  1  5  5  2  0
6  2  6  6  0  0
7  2  7  7  0  0
8  2  8  8  0  0

7.2.4.1 Enumeration of Paths

tda_g_paths() is gep, on gd5.dat, a small directed graph; gd20.cf runs its six output options: the paths between all pairs of nodes, their number per pair, the shortest and longest path per pair, and the two full listings with each path’s length and value.

Box 188gep (gd20.cf)

gd5 <- read.table(ex("gd5.dat"), col.names = c("I", "J", "V"))
g5 <- tda_graph(gd5, directed = TRUE)
for (o in 1:6)
    print(tda_g_paths(g5, opt = o)$table)
   V1 V2 V3 V4 V5 V6 V7   V8 V9  V10
1   1  2  1  2  1  1  1  1.0  1  1.0
2   1  3  1  3  3  1  2  3.0  4 13.0
3   1  4  1  4  3  1  2  4.0  4 13.5
4   1  5  1  5  3  1  2  1.5  4 14.0
5   2  1  2  1  3  1  2  8.0  4 18.0
6   2  3  2  3  3  1  1  2.0  3 12.0
7   2  4  2  4  3  1  1  3.0  3 12.5
8   2  5  2  5  3  1  1  0.5  3 13.0
9   3  1  3  1  1  1  1  6.0  1  6.0
10  3  2  3  2  2  1  1  0.5  2  7.0
11  3  4  3  4  3  1  2  3.5  3 10.0
12  3  5  3  5  5  1  2  1.0  4 14.0
13  4  1  4  1  1  1  3 15.0  3 15.0
14  4  2  4  2  2  1  3  9.5  4 16.0
15  4  3  4  3  1  1  2  9.0  2  9.0
16  4  5  4  5  1  1  1  4.0  1  4.0
17  5  1  5  1  1  1  2 11.0  2 11.0
18  5  2  5  2  2  1  2  5.5  3 12.0
19  5  3  5  3  1  1  1  5.0  1  5.0
20  5  4  5  4  3  1  3  8.5  4 15.0
45 records, lengths 9-12
[1]  1 2 1 2 1 1 1 1 2 
[2]  1 3 1 3 1 2 3 1 2 3 
[3]   1  3  1  3  2  4 13  1  2  4  5  3 
[4]  1.0 3.0 1.0 3.0 3.0 3.0 6.5 1.0 2.0 5.0 3.0 
[5]   1  4  1  4  1  3 10  1  2  3  4 
[6]  1 4 1 4 2 2 4 1 2 4 
[7]   1.0  4.0  1.0  4.0  3.0  4.0 13.5  1.0  2.0  5.0  3.0  4.0 
[8]   1  5  1  5  1  4 14  1  2  3  4  5 
[9]  1 5 1 5 2 3 8 1 2 4 5 
[10]  1.0 5.0 1.0 5.0 3.0 2.0 1.5 1.0 2.0 5.0 
... and 35 more
  V1 V2 V3  V4 V5  V6  V7
1  1  1  0 1.0  3 4.0 1.5
2  2  2  8 0.0  2 3.0 0.5
3  3  3  6 0.5  0 3.5 1.0
4  4  4 15 9.5  9 0.0 4.0
5  5  5 11 5.5  5 8.5 0.0
  V1 V2 V3 V4 V5   V6 V7
1  1  1  0  1 13 13.5 14
2  2  2 18  0 12 12.5 13
3  3  3  6  7  0 10.0 14
4  4  4 15 16  9  0.0  4
5  5  5 11 12  5 15.0  0
   V1 V2 V3 V4 V5 V6 V7   V8 V9 V10 V11 V12 V13
1   1  2  1  2  1  1  1  1.0  0   0   0   0   0
2   1  3  1  3  3  1  2  3.0  0   1   0   0   0
3   1  4  1  4  3  1  2  4.0  0   1   0   0   0
4   1  5  1  5  3  1  2  1.5  0   1   0   0   0
5   2  1  2  1  3  1  2  8.0  0   0   1   0   0
6   2  3  2  3  3  1  1  2.0  0   0   0   0   0
7   2  4  2  4  3  1  1  3.0  0   0   0   0   0
8   2  5  2  5  3  1  1  0.5  0   0   0   0   0
9   3  1  3  1  1  1  1  6.0  0   0   0   0   0
10  3  2  3  2  2  1  1  0.5  0   0   0   0   0
11  3  4  3  4  3  1  2  3.5  0   1   0   0   0
12  3  5  3  5  5  1  2  1.0  0   1   0   0   0
13  4  1  4  1  1  1  3 15.0  0   0   1   0   1
14  4  2  4  2  2  1  3  9.5  0   0   1   0   1
15  4  3  4  3  1  1  2  9.0  0   0   0   0   1
16  4  5  4  5  1  1  1  4.0  0   0   0   0   0
17  5  1  5  1  1  1  2 11.0  0   0   1   0   0
18  5  2  5  2  2  1  2  5.5  0   0   1   0   0
19  5  3  5  3  1  1  1  5.0  0   0   0   0   0
20  5  4  5  4  3  1  3  8.5  0   1   1   0   0
   V1 V2 V3 V4 V5 V6 V7   V8 V9 V10 V11 V12 V13
1   1  2  1  2  1  1  1  1.0  0   0   0   0   0
2   1  3  1  3  3  1  2  3.0  0   1   0   0   0
3   1  3  1  3  3  2  3  6.5  0   1   0   0   1
4   1  3  1  3  3  3  4 13.0  0   1   0   1   1
5   1  4  1  4  3  1  2  4.0  0   1   0   0   0
6   1  4  1  4  3  2  3 10.0  0   1   1   0   0
7   1  4  1  4  3  3  4 13.5  0   1   1   0   1
8   1  5  1  5  3  1  2  1.5  0   1   0   0   0
9   1  5  1  5  3  2  3  8.0  0   1   0   1   0
10  1  5  1  5  3  3  4 14.0  0   1   1   1   0
11  2  1  2  1  3  1  2  8.0  0   0   1   0   0
12  2  1  2  1  3  2  3 11.5  0   0   1   0   1
13  2  1  2  1  3  3  4 18.0  0   0   1   1   1
14  2  3  2  3  3  1  1  2.0  0   0   0   0   0
15  2  3  2  3  3  2  2  5.5  0   0   0   0   1
16  2  3  2  3  3  3  3 12.0  0   0   0   1   1
17  2  4  2  4  3  1  1  3.0  0   0   0   0   0
18  2  4  2  4  3  2  2  9.0  0   0   1   0   0
19  2  4  2  4  3  3  3 12.5  0   0   1   0   1
20  2  5  2  5  3  1  1  0.5  0   0   0   0   0
21  2  5  2  5  3  2  2  7.0  0   0   0   1   0
22  2  5  2  5  3  3  3 13.0  0   0   1   1   0
23  3  1  3  1  1  1  1  6.0  0   0   0   0   0
24  3  2  3  2  2  1  1  0.5  0   0   0   0   0
25  3  2  3  2  2  2  2  7.0  1   0   0   0   0
26  3  4  3  4  3  1  2  3.5  0   1   0   0   0
27  3  4  3  4  3  2  1  7.0  0   0   0   0   0
28  3  4  3  4  3  3  3 10.0  1   1   0   0   0
29  3  5  3  5  5  1  2  1.0  0   1   0   0   0
30  3  5  3  5  5  2  3  7.5  1   1   0   0   0
31  3  5  3  5  5  3  3  7.5  0   1   0   1   0
32  3  5  3  5  5  4  2 11.0  0   0   0   1   0
33  3  5  3  5  5  5  4 14.0  1   1   0   1   0
34  4  1  4  1  1  1  3 15.0  0   0   1   0   1
35  4  2  4  2  2  1  3  9.5  0   0   1   0   1
36  4  2  4  2  2  2  4 16.0  1   0   1   0   1
37  4  3  4  3  1  1  2  9.0  0   0   0   0   1
38  4  5  4  5  1  1  1  4.0  0   0   0   0   0
39  5  1  5  1  1  1  2 11.0  0   0   1   0   0
40  5  2  5  2  2  1  2  5.5  0   0   1   0   0
41  5  2  5  2  2  2  3 12.0  1   0   1   0   0
42  5  3  5  3  1  1  1  5.0  0   0   0   0   0
43  5  4  5  4  3  1  3  8.5  0   1   1   0   0
44  5  4  5  4  3  2  2 12.0  0   0   1   0   0
45  5  4  5  4  3  3  4 15.0  1   1   1   0   0

The graph itself, as the manual draws it (gd18.cf): five nodes, curved arrows for the edges that run both ways, the edge values as labels.

Box 189The directed graph of gd5.dat, drawn (gd18.cf)

p <- tda_ps(xlim = c(0, 8), ylim = c(-1, 5), width = 80, height = 40)
nodes <- data.frame(id = 1:5, x = c(3, 5, 1, 7, 7), y = c(3, 1, 1, 4, 1),
                    grey = 0.8)
edges <- data.frame(from  = c(1, 2, 3, 3, 2, 4, 5, 2, 3),
                    to    = c(2, 3, 2, 1, 4, 5, 3, 5, 4),
                    curve = c(-3, 0, -3, -3, 0, 0, -3, 0, -3),
                    label = c(1, 2, 0.5, 6, 3, 4, 5, 0.5, 7),
                    arrow = "1.5,1.0")
plot(tda_pl_graph(p, nodes, edges))

7.2.4.2 All Shortest Paths

tda_g_shortest() is gsp, the same graph, gd27.cf.

Box 190gsp (gd27.cf)

tda_g_shortest(g5, opt = 1, fmt = "4.1")$table
  index node to1 dist1 to2 dist2 to3 dist3 to4 dist4
1     1    1   2     1   3   3.0   4   4.0   5   1.5
2     2    2   1     8   3   2.0   4   3.0   5   0.5
3     3    3   1     6   2   0.5   4   3.5   5   1.0
4     4    4   1    15   2   9.5   3   9.0   5   4.0
5     5    5   1    11   2   5.5   3   5.0   4   8.5

7.2.4.3 Transitive Closure

tda_g_transitive() is gtcl; gd16.cf runs it on gd5.dat read as an undirected, an undirected valued and a directed graph, opt = 2 for the closure as a matrix.

Box 191gtcl (gd16.cf)

# gdd's gt= is the graph type: 1 undirected unvalued, 2 undirected
# valued, 3 directed unvalued, 4 directed valued. The manual's box runs
# gtcl over all four.
for (gt in 1:4)
    print(tda_g_transitive(tda_graph(gd5, options = list(gt = gt)),
                           opt = 2, fmt = "4.1")$table)
  index node n1 n2 n3 n4 n5
1     1    1  0  1  1  1  1
2     2    2  1  0  1  1  1
3     3    3  1  1  0  1  1
4     4    4  1  1  1  0  1
5     5    5  1  1  1  1  0
  index node  n1  n2  n3  n4  n5
1     1    1 0.0 1.0 1.5 4.0 1.5
2     2    2 1.0 0.0 0.5 3.0 0.5
3     3    3 1.5 0.5 0.0 3.5 1.0
4     4    4 4.0 3.0 3.5 0.0 3.5
5     5    5 1.5 0.5 1.0 3.5 0.0
  index node n1 n2 n3 n4 n5
1     1    1  0  1  1  1  1
2     2    2  1  0  1  1  1
3     3    3  1  1  0  1  1
4     4    4  1  1  1  0  1
5     5    5  1  1  1  1  0
  index node n1  n2 n3  n4  n5
1     1    1  0 1.0  3 4.0 1.5
2     2    2  8 0.0  2 3.0 0.5
3     3    3  6 0.5  0 3.5 1.0
4     4    4 15 9.5  9 0.0 4.0
5     5    5 11 5.5  5 8.5 0.0

7.2.5.1 Depth-first Spanning Trees

tda_g_spanning() is gst, on gd7.dat: one spanning tree per component, the edges listed with their values.

Box 192gst (gd24.cf)

gd7 <- read.table(ex("gd7.dat"), col.names = c("I", "J", "V"))
tda_g_spanning(tda_graph(gd7, directed = FALSE))$table
  tree node_i node_j value
1    1      1      2     1
2    1      2      3     7
3    1      3      4     5
4    1      4      5     3
5    2      6      7     9
6    2      7      8    10

The manual’s Figure 7.2.5.1-1 is gd7.dat’s first component drawn by gd21.cf, with its spanning tree’s edges.

Box 193gd7.dat drawn (gd21.cf)

p <- tda_ps(xlim = c(0, 6), ylim = c(0, 5), width = 60, height = 30)
nodes <- data.frame(id = c(5, 7, 4, 1, 2), x = c(1, 3, 5, 2, 4),
                    y = c(3, 3, 3, 1, 1), grey = 0.8)
edges <- data.frame(from  = c(5, 4, 5, 7, 7, 4, 1, 4),
                    to    = c(7, 7, 1, 1, 2, 2, 2, 5),
                    curve = c(0, 0, 0, 0, 0, 0, 0, 3),
                    label = c(1, 5, 3, 2, 4, 7, 8, 6),
                    arrow = "1.5,1.0")
plot(tda_pl_graph(p, nodes, edges))

Box 194gd23.cf

p <- tda_ps(xlim = c(0, 7), ylim = c(0, 3.7), width = 60, height = 30)
nodes <- data.frame(id = 1:8, x = c(0.5, 2, 2, 3.5, 4.5, 5, 6, 6),
                    y = c(1.5, 2.5, 0.5, 1.5, 0.5, 1.5, 0.5, 2.5), grey = 0.8)
edges <- data.frame(
    from  = c(1, 2, 4, 5, 3, 1, 2, 6, 6, 8),
    to    = c(2, 4, 5, 3, 4, 3, 3, 8, 7, 7),
    label = 1:10,
    arrow = "1.5,1.0")
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.2.5.2 Minimum Spanning Trees

tda_g_mst() is gmst, gd25.cf, on the same gd7.dat.

Box 195gmst (gd25.cf)

tda_g_mst(tda_graph(gd7, directed = FALSE))$table
  tree node_i node_j value
1    1      1      2     1
2    1      2      4     2
3    1      4      5     3
4    1      3      5     4
5    2      6      8     8
6    2      6      7     9

7.2.5.3 Enumeration of Spanning Trees

tda_g_spantrees() is gnst, on gd10.dat (gd32.cf): the number of spanning trees, and with opt = 2 the trees themselves.

Box 196gnst (gd32.cf)

gd10 <- read.table(ex("gd10.dat"), col.names = c("I", "J", "V"))
g10 <- tda_graph(gd10, directed = FALSE)
tda_g_spantrees(g10, opt = 1)$table
  V1 V2 V3 V4 V5 V6
1  1  1  0  1  1  1
2  1  2  0  1  1  3
3  1  3  0  1  2  1
4  1  4  0  1  2  3
5  1  5  0  1  4  1
6  1  6  0  3  1  1
7  1  7  0  3  1  3
8  1  8  0  3  4  1
tda_g_spantrees(g10, opt = 2)$table
   V1 V2 V3 V4 V5 V6
1   1  1  3  1  2  1
2   1  1  3  2  3  1
3   1  1  3  3  4  1
4   1  2  3  1  2  1
5   1  2  3  2  3  1
6   1  2  3  3  4  3
7   1  3  3  1  2  1
8   1  3  3  2  3  2
9   1  3  3  3  4  1
10  1  4  3  1  2  1
11  1  4  3  2  3  2
12  1  4  3  3  4  3
13  1  5  3  1  2  1
14  1  5  3  2  3  4
15  1  5  3  3  4  1
16  1  6  3  1  2  3
17  1  6  3  2  3  1
18  1  6  3  3  4  1
19  1  7  3  1  2  3
20  1  7  3  2  3  1
21  1  7  3  3  4  3
22  1  8  3  1  2  3
23  1  8  3  2  3  4
24  1  8  3  3  4  1

Box 197gd31.cf

p <- tda_ps(xlim = c(0, 8), ylim = c(-1, 5), width = 80, height = 40)
nodes <- data.frame(id = 1:4, x = c(3, 5, 3, 5), y = c(3, 3, 1, 1), grey = 0.8)
edges <- data.frame(from = c(1, 1, 1, 2, 3), to = c(2, 3, 4, 3, 4))
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.2.6.1 Fundamental Set of Cycles

tda_g_cycles() is gcyc; gd30.cf runs it on gd9.dat, an undirected graph with 6 nodes and 11 edges. opt = 1 lists a fundamental set of cycles, one ragged record each: index, cycle number, length, and the nodes.

gcyc’s four options are four views of the same cycle space. Option 1 lists the nodes of each fundamental cycle, so its rows are ragged; option 2 gives the edge and one indicator per fundamental cycle; option 3 the same over every cycle found; option 4 writes the edges first and then one indicator row per cycle, two row shapes in the one file, which is why it arrives ragged as well.

Box 198gcyc, options 1 to 4

gd9 <- read.table(ex("gd9.dat"), col.names = c("I", "J", "V"))
g9 <- tda_graph(gd9, directed = FALSE)
tda_g_cycles(g9, "fundamental_nodes")$table
6 records, lengths 5-6
[1]  1 1 6 1 3 
[2]  1 2 5 6 1 2 
[3]  1 3 5 6 4 
[4]  1 4 4 6 1 2 
[5]  1 5 4 6 1 3 
[6]  1 6 3 1 2 
tda_g_cycles(g9, "fundamental_edges")$table
   cycle index from to in1 in2 in3 in4 in5 in6
1      1     1    1  2   0   1   0   1   0   1
2      1     2    1  3   1   0   0   0   1   1
3      1     3    1  6   1   1   0   1   1   0
4      1     4    2  3   0   0   0   0   0   1
5      1     5    2  4   0   0   0   1   0   0
6      1     6    2  5   0   1   0   0   0   0
7      1     7    3  4   0   0   0   0   1   0
8      1     8    3  6   1   0   0   0   0   0
9      1     9    4  5   0   0   1   0   0   0
10     1    10    4  6   0   0   1   1   1   0
11     1    11    5  6   0   1   1   0   0   0
tda_g_cycles(g9, "all_v1")$table
   cycle index from to in1 in2 in3 in4 in5 in6 in7 in8 in9 in10 in11 in12 in13 in14 in15 in16 in17
1      1     1    1  2   0   1   1   1   1   0   1   0   1    0    1    1    0    1    0    1    1
2      1     2    1  3   1   1   0   1   0   0   1   0   1    0    0    0    0    1    0    0    1
3      1     3    1  6   1   0   1   0   1   0   0   0   0    0    1    1    0    0    0    1    0
4      1     4    2  3   0   0   0   0   0   0   0   0   0    0    0    0    0    0    0    0    0
5      1     5    2  4   0   0   0   0   0   0   1   1   1    1    1    1    0    0    0    0    0
6      1     6    2  5   0   1   1   1   1   0   0   1   0    1    0    0    0    1    0    1    1
7      1     7    3  4   0   0   0   0   0   0   0   0   0    0    0    0    1    1    1    1    1
8      1     8    3  6   1   1   0   1   0   0   1   0   1    0    0    0    1    0    1    1    0
9      1     9    4  5   0   0   0   1   1   1   0   0   1    1    1    0    0    0    1    1    1
10     1    10    4  6   0   0   0   1   1   1   1   1   0    0    0    1    1    1    0    0    0
11     1    11    5  6   0   1   1   0   0   1   0   1   1    0    1    0    0    1    1    0    0
   in18 in19 in20 in21 in22 in23 in24 in25 in26 in27 in28 in29 in30 in31 in32 in33 in34 in35 in36
1     0    1    0    0    1    0    1    0    0    0    0    0    0    0    0    0    0    1    0
2     1    0    0    1    1    1    0    0    1    0    1    0    0    1    1    0    0    0    0
3     1    1    0    1    0    1    1    0    1    0    1    0    0    1    1    0    0    1    0
4     0    0    0    0    0    0    1    1    1    1    1    1    1    1    1    1    1    1    1
5     0    1    1    1    1    0    0    0    0    0    0    1    1    1    1    0    0    0    1
6     0    0    1    1    0    0    0    1    1    1    1    0    0    0    0    1    1    0    0
7     1    1    1    1    1    1    0    0    0    0    0    0    0    0    0    1    1    1    1
8     0    1    1    0    0    0    1    1    0    1    0    1    1    0    0    0    0    0    0
9     1    0    0    0    0    0    0    0    0    1    1    0    1    1    0    0    1    1    0
10    0    0    0    0    0    1    0    0    0    1    1    1    0    0    1    1    0    0    0
11    1    0    1    1    0    0    0    1    1    0    0    0    1    1    0    1    0    1    0
   in37 in38
1     1    1
2     0    1
3     1    0
4     1    1
5     0    0
6     0    0
7     1    0
8     0    0
9     0    0
10    1    0
11    0    0
# option 4's records are of two shapes -- a cycle's row and its edge
# list -- so they stay a ragged list; the manual prints all of them
print(tda_g_cycles(g9, "all_v2")$table, n = Inf)
49 records, lengths 4-13
[1]  1 1 1 2 
[2]  1 2 1 3 
[3]  1 3 1 6 
[4]  1 4 2 3 
[5]  1 5 2 4 
[6]  1 6 2 5 
[7]  1 7 3 4 
[8]  1 8 3 6 
[9]  1 9 4 5 
[10]   1 10  4  6 
[11]   1 11  5  6 
[12]  1 1 0 1 1 0 0 0 0 1 0 0 0 
[13]  1 2 1 1 0 0 0 1 0 1 0 0 1 
[14]  1 3 1 0 1 0 0 1 0 0 0 0 1 
[15]  1 4 1 1 0 0 0 1 0 1 1 1 0 
[16]  1 5 1 0 1 0 0 1 0 0 1 1 0 
[17]  1 6 0 0 0 0 0 0 0 0 1 1 1 
[18]  1 7 1 1 0 0 1 0 0 1 0 1 0 
[19]  1 8 0 0 0 0 1 1 0 0 0 1 1 
[20]  1 9 1 1 0 0 1 0 0 1 1 0 1 
[21]   1 10  0  0  0  0  1  1  0  0  1  0  0 
[22]   1 11  1  0  1  0  1  0  0  0  1  0  1 
[23]   1 12  1  0  1  0  1  0  0  0  0  1  0 
[24]   1 13  0  0  0  0  0  0  1  1  0  1  0 
[25]   1 14  1  1  0  0  0  1  1  0  0  1  1 
[26]   1 15  0  0  0  0  0  0  1  1  1  0  1 
[27]   1 16  1  0  1  0  0  1  1  1  1  0  0 
[28]   1 17  1  1  0  0  0  1  1  0  1  0  0 
[29]   1 18  0  1  1  0  0  0  1  0  1  0  1 
[30]   1 19  1  0  1  0  1  0  1  1  0  0  0 
[31]   1 20  0  0  0  0  1  1  1  1  0  0  1 
[32]   1 21  0  1  1  0  1  1  1  0  0  0  1 
[33]   1 22  1  1  0  0  1  0  1  0  0  0  0 
[34]   1 23  0  1  1  0  0  0  1  0  0  1  0 
[35]   1 24  1  0  1  1  0  0  0  1  0  0  0 
[36]   1 25  0  0  0  1  0  1  0  1  0  0  1 
[37]   1 26  0  1  1  1  0  1  0  0  0  0  1 
[38]   1 27  0  0  0  1  0  1  0  1  1  1  0 
[39]   1 28  0  1  1  1  0  1  0  0  1  1  0 
[40]   1 29  0  0  0  1  1  0  0  1  0  1  0 
[41]   1 30  0  0  0  1  1  0  0  1  1  0  1 
[42]   1 31  0  1  1  1  1  0  0  0  1  0  1 
[43]   1 32  0  1  1  1  1  0  0  0  0  1  0 
[44]   1 33  0  0  0  1  0  1  1  0  0  1  1 
[45]   1 34  0  0  0  1  0  1  1  0  1  0  0 
[46]   1 35  1  0  1  1  0  0  1  0  1  0  1 
[47]   1 36  0  0  0  1  1  0  1  0  0  0  0 
[48]   1 37  1  0  1  1  0  0  1  0  0  1  0 
[49]   1 38  1  1  0  1  0  0  0  0  0  0  0 

Box 199gd29.cf

p <- tda_ps(xlim = c(0, 8), ylim = c(-1, 5), width = 80, height = 40)
nodes <- data.frame(id = c(1, 3, 4, 6, 2, 5), x = c(3, 3, 5, 1, 5, 7),
                    y = c(3, 1, 1, 1, 3, 1), grey = 0.8)
edges <- data.frame(
    from  = c(1, 3, 1, 1, 3, 3, 2, 2, 4, 6, 6),
    to    = c(6, 6, 3, 2, 4, 2, 4, 5, 5, 4, 5),
    curve = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5))
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.2.6.2 Enumeration of Cycles in Directed Graphs

tda_g_dcycles() is gdcyc, on gd5.dat (gd33.cf).

Box 200gdcyc (gd33.cf)

tda_g_dcycles(g5, opt = 1)$table
7 records, lengths 4-7
[1]  1 3 1 2 3 
[2]  2 5 1 2 4 5 3 
[3]  3 4 1 2 5 3 
[4]  4 2 2 3 
[5]  5 4 2 4 5 3 
[6]  6 3 2 5 3 
[7]  7 3 3 4 5 
tda_g_dcycles(g5, opt = 2)$table
   V1 V2 V3
1   1  3  1
2   1  3  2
3   1  3  3
4   2  5  1
5   2  5  2
6   2  5  4
7   2  5  5
8   2  5  3
9   3  4  1
10  3  4  2
11  3  4  5
12  3  4  3
13  4  2  2
14  4  2  3
15  5  4  2
16  5  4  4
17  5  4  5
18  5  4  3
19  6  3  2
20  6  3  5
21  6  3  3
22  7  3  3
23  7  3  4
24  7  3  5

7.2.8.1 Maximal Flows

tda_g_flow() is gflow, on gd11.dat: for every ordered pair of nodes the maximal flow, one record per pair – index, the two nodes’ indices, the two nodes, the flow.

Box 201gflow (gd35.cf)

gd11 <- read.table(ex("gd11.dat"), col.names = c("I", "J", "V"))
g11 <- tda_graph(gd11, directed = TRUE)
tda_g_flow(g11)$table
   index i j node_i node_j flow
1      1 1 2      1      2   30
2      2 1 3      1      3   27
3      3 1 4      1      4   17
4      4 1 5      1      5   17
5      5 1 6      1      6   30
6      6 1 7      1      7   30
7      7 2 3      2      3   27
8      8 2 4      2      4   17
9      9 2 5      2      5   17
10    10 2 6      2      6   37
11    11 2 7      2      7   47
12    12 3 7      3      7   22
13    13 4 3      4      3   50
14    14 4 5      4      5   54
15    15 4 7      4      7   22
16    16 5 3      5      3   50
17    17 5 7      5      7   22
18    18 6 3      6      3   17
19    19 6 4      6      4   17
20    20 6 5      6      5   17
21    21 6 7      6      7   49
22    22 8 1      8      1   40
23    23 8 2      8      2   30
24    24 8 3      8      3   47
25    25 8 4      8      4   37
26    26 8 5      8      5   37
27    27 8 6      8      6   30
28    28 8 7      8      7   50
# option 2 gives the same flows as a matrix
tda_g_flow(g11, matrix = TRUE)$table
  V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1  1  1  0 30 27 17 17 30 30  -1
2  2  2 -1  0 27 17 17 37 47  -1
3  3  3 -1 -1  0 -1 -1 -1 22  -1
4  4  4 -1 -1 50  0 54 -1 22  -1
5  5  5 -1 -1 50 -1  0 -1 22  -1
6  6  6 -1 -1 17 17 17  0 49  -1
7  7  7 -1 -1 -1 -1 -1 -1  0  -1
8  8  8 40 30 47 37 37 30 50   0

Box 202gd34.cf

p <- tda_ps(xlim = c(0, 11), ylim = c(-1, 7), width = 80, height = 40)
nodes <- data.frame(id = c(4, 1, 8, 6, 3, 7, 2, 5),
                    x = c(3, 3, 1, 8, 8, 10, 6, 4.5),
                    y = c(1, 5, 3, 1, 5, 3, 5, 3), grey = 0.8)
edges <- data.frame(
    from  = c(8, 8, 1, 6, 2, 6, 3, 2, 4, 5),
    to    = c(1, 4, 2, 4, 3, 7, 7, 6, 5, 3),
    label = c(40, 20, 30, 17, 10, 32, 22, 37, 54, 50),
    arrow = "1.5,1.0")
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.2.9.1 Cliques

tda_g_cliques() is gcliq, on gd16.dat; the manual’s Box 1 runs both of its algorithms, alg = 1 (Bron-Kerbosch) and alg = 2 (Harary-Ross), whose listings differ in order, not in content.

Box 203gcliq (gd42.cf)

gd16 <- read.table(ex("gd16.dat"), col.names = c("I", "J", "V"))
g16 <- tda_graph(gd16, directed = FALSE)
tda_g_cliques(g16, algorithm = "bron_kerbosch")$table
4 records, lengths 6-7
[1]  1 1 4 3 2 1 4 
[2]  1 2 3 3 2 5 
[3]  1 3 3 3 6 4 
[4]  1 4 3 3 6 5 
tda_g_cliques(g16, algorithm = "harary_ross")$table
5 records, lengths 6-7
[1]  1 1 4 1 2 3 4 
[2]  1 2 3 4 2 3 
[3]  1 3 3 3 2 5 
[4]  1 4 3 4 3 6 
[5]  1 5 3 3 5 6 

Box 204gd43.cf

p <- tda_ps(xlim = c(0, 8), ylim = c(-1.5, 7), width = 80, height = 40)
nodes <- data.frame(id = 1:6, x = c(1, 3, 5, 3, 7, 7), y = c(3, 6, 3, 0, 6, 0),
                    grey = 0.8)
edges <- data.frame(
    from = c(1, 1, 1, 2, 2, 2, 4, 4, 3, 3, 5),
    to   = c(3, 2, 4, 4, 3, 5, 3, 6, 5, 6, 6))
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.2.9.2 Compact Sets

tda_g_independent() is giset, independent sets on gd12.dat (gd37.cf), and tda_g_compact() is gcset, compact sets on gd13.dat (gd39.cf).

Box 205giset and gcset

gd12 <- read.table(ex("gd12.dat"), col.names = c("I", "J", "V"))
tda_g_independent(tda_graph(gd12, directed = FALSE))$table
  V1 V2 V3
1  1  3  3
2  2  6  6
gd13 <- read.table(ex("gd13.dat"), col.names = c("I", "J", "V"))
g13 <- tda_graph(gd13, directed = FALSE)
print(tda_g_compact(g13, opt = 1)$table, n = Inf)   # the sets themselves
3 records, lengths 7-9
[1]  1 1 2 1 3 1 3 
[2]  1 2 2 2 8 4 6 
[3]  1 3 4 7 8 1 2 3 5 
tda_g_compact(g13, opt = 2)$table                   # one row per node
  V1 V2 V3 V4 V5 V6
1  1  1  2  1  3  1
2  1  1  2  1  3  3
3  1  2  2  2  8  4
4  1  2  2  2  8  6
5  1  3  4  7  8  1
6  1  3  4  7  8  2
7  1  3  4  7  8  3
8  1  3  4  7  8  5

Box 206gd38.cf

p <- tda_ps(xlim = c(0, 8), ylim = c(-1.5, 7), width = 80, height = 40)
nodes <- data.frame(id = 1:6, x = c(1, 3, 5, 3, 7, 7), y = c(3, 6, 3, 0, 6, 0),
                    grey = 0.8)
edges <- data.frame(
    from  = c(1, 1, 1, 2, 2, 4, 4, 3, 3, 5),
    to    = c(3, 2, 4, 3, 5, 3, 6, 5, 6, 6),
    label = c(1, 3, 12, 7, 5, 11, 2, 4, 15, 8))
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.2.9.3 Regular Equivalence

Box 207gd36.cf

p <- tda_ps(xlim = c(0, 8), ylim = c(-1.5, 7), width = 80, height = 40)
nodes <- data.frame(id = 1:6, x = c(1, 2.5, 4, 4, 5.5, 7),
                    y = c(1, 3, 1, 5, 3, 1), grey = 0.8)
edges <- data.frame(
    from  = c(1, 1, 2, 2, 2, 3, 3, 4, 5, 1),
    to    = c(2, 3, 3, 4, 5, 4, 5, 5, 6, 6),
    curve = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 3))
p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.3.1.1 The Linear Assignment Problem

tda_gap() is gap: given a cost matrix, the assignment of rows to columns with minimal total cost. The manual’s co1.dat is a 6 x 6 cost matrix, row by row; the result is the permutation of its Box 2 and the matrix with its rows permuted accordingly.

Box 208gap (co1.cf)

cost <- matrix(scan(ex("co1.dat"), quiet = TRUE), 6, 6, byrow = TRUE)
ap <- tda_gap(cost)
ap$assignment
     [,1] [,2]
[1,]    1    6
[2,]    2    4
[3,]    3    3
[4,]    4    1
[5,]    5    5
[6,]    6    2
ap$permuted
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    0   76    0   60    0    0
[2,]   24    0   18    0   60   40
[3,]   40    4    2   60    0   16
[4,]   75    3   18    0   55   27
[5,]   53   16   62    0   11   40
[6,]   16   84   10   28    0    4

7.3.1.2 The Quadratic Assignment Problem

tda_gqap() is gqap, on two symmetric matrices, a flow and a distance matrix. co2.dat gives their lower triangles in one file, one pair per line in the order (2,1), (3,1), (3,2); the manual’s Box 2 is the permutation 2, 1, 3.

Box 209gqap (co2.cf)

co2 <- read.table(ex("co2.dat"), col.names = c("F", "D"))
lower <- function(v) { m <- matrix(0, 3, 3); m[lower.tri(m)] <- v; m + t(m) }
qap <- tda_gqap(lower(co2$F), lower(co2$D))
qap$assignment
[1] 2 1 3
qap$cost
[1] 36
# gqap's second output file: the multigraph under that permutation, with
# the flow and distance between each pair
qap$permuted
  i j node_i node_j flow distance
1 1 1      1      1    0        0
2 1 2      1      2    1        5
3 1 3      1      3    2        3
4 2 1      2      1    1        5
5 2 2      2      2    0        0
6 2 3      2      3    7        1
7 3 1      3      1    2        3
8 3 2      3      2    7        1
9 3 3      3      3    0        0

7.5.1.1 SAHN Algorithms

tda_cluster() covers TDA’s clustering commands. The manual’s example dissimilarity matrix is cl1.dat, the upper triangle of an 11 x 11 matrix, row by row; cl1.cf runs hcls with opt = 2, complete linkage. The output is the manual’s Box 3: at each level the two clusters merged, named by their representative nodes.

Box 210hcls: complete-link clustering (cl1.cf)

D <- scan(ex("cl1.dat"), quiet = TRUE)
dm <- matrix(0, 11, 11)
dm[lower.tri(dm)] <- D
dm <- dm + t(dm)
tda_cluster(dm, method = "hierarchical_single", options = list(opt = 2))$table
   V1 V2 V3 V4
1   1  1  3  6
2   2  2  1  8
3   3  4  2  9
4   4  7  5 11
5   5  8  1  2
6   6 13  3  4
7   7 24  3 10
8   8 33  1  5
9   9 34  3  7
10 10 55  1  3

cl1a.cf reads the dendrogram back and cuts it into four levels with hclsp; tda_cutree() is both steps.

Box 211hclsp: the dendrogram cut at four levels (cl1a.cf)

tda_cutree(dm, nlev = 4)$table
   node leaves parent
1    21     11     NA
2    19      6     21
3    15      2     21
4     5      1     21
5    11      1     21
6    16      4     21
7    13      2     21
8    14      2     21
9    20      5     21
10    7      1     21
11   18      4     21
12   10      1     21
13   17      3     21

7.5.1.2 Nearest-Neighbor Clustering

cl2.cf runs nncl twice: with a threshold (sc = 10) and with a fixed number of neighbours (ns = 2).

Box 212nncl (cl2.cf)

tda_cluster(dm, method = "nearest_neighbour", options = list(alg = 1, sc = 10))$table
   V1 V2 V3
1   1  1  1
2   2  2  1
3   3  3  2
4   4  4  1
5   5  5  3
6   6  6  2
7   7  7  4
8   8  8  1
9   9  9  1
10 10 10  5
11 11 11  3
tda_cluster(dm, method = "nearest_neighbour", options = list(alg = 2, ns = 2))$table
  V1 V2 V3 V4 V5 V6
1  1  2  1  8  1  8
2  2  2  3  6  3  6
3  3  2  5 11  5 11
4  4  3  2  9  2  9
5  5  3  8  9  8  9
6  6  4  3  4  3  4

7.5.2.1 Maximally Different Cluster Centers

cl3.cf is hcld with its first algorithm: divisive clustering around maximally different centres. The manual’s Box 2 lists each cluster’s nodes.

Box 213hcld (cl3.cf)

hc <- tda_cluster(dm, method = "hierarchical", algorithm = "centers")
hc$clusters
$`1`
 [1]  1  2  3  4  5  6  7  8  9 10 11

$`2`
[1]  3  4  6  7 10

$`3`
[1]  1  2  5  8  9 11

$`4`
[1] 4 7

$`5`
[1]  3  6 10

$`6`
[1] 1 2 8 9

$`7`
[1]  5 11

$`8`
[1] 3 6

$`9`
[1] 10

$`10`
[1] 1 8

$`11`
[1] 2 9
hc$merges     # one row per split: the two clusters, their sizes, their diameters
   V1 V2 V3 V4   V5
1   2  1  5 11 27.5
2   3  1  6 11 27.5
3   4  2  2  5 17.0
4   5  2  3  5 17.0
5   6  3  4  6 16.5
6   7  3  2  6 16.5
7   8  5  2  3 12.0
8   9  5  1  3 12.0
9  10  6  2  4  4.0
10 11  6  2  4  4.0

7.5.2.2 Maximally Homogeneous Clusters

The second algorithm splits on the diameter instead – at each step the cluster with the largest diameter is divided.

Box 214hcld, second algorithm

# the default stops after two splits; the manual's run goes on until
# the size bound is reached
hd <- tda_cluster(dm, method = "hierarchical", algorithm = "diameter",
                  max_splits = 5)
hd$clusters
$`2`
[1] 33

$`3`
[1] 34

$`4`
[1] 24  3  3

$`5`
[1] 16  2  4

$`6`
[1] 8 4

$`7`
[1] 7 2

$`8`
[1] 1 2 3 6

$`9`
[1]  0  1 10

$`10`
[1] 0 1 4

$`11`
[1] 0 1 7
hd$merges
   ci cj ni nj diameter_i diameter_j
1   2  1  6 11         33         55
2   3  1  5 11         34         55
3   4  3  3  5         24         34
4   5  3  2  5         16         34
5   6  2  4  6          8         33
6   7  2  2  6          7         33
7   8  4  2  3          1         24
8   9  4  1  3          0         24
9  10  5  1  2          0         16
10 11  5  1  2          0         16

cl3p.cf draws the merge table as a dendrogram: columns 1 and 2 of cl3.df are the pair joined, column 5 the distance.

Box 215cl3p.cf

edges <- data.frame(
    from = c(2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
    to   = c(1, 1, 2, 2, 3, 3, 5, 5, 6, 6),
    v    = c(27.5, 27.5, 17.0, 17.0, 16.5, 16.5, 12.0, 12.0, 4.0, 4.0))
g <- tda_graph(edges, directed = FALSE)

plot(g, width = 90, height = 50, layout = "tree", rt = 1, pl = 2, nc = 11)

7.5.3.1 The Bond Energy Algorithm

becl is the bond energy algorithm on a graph; cl5.cf runs it on gd15.dat, five nodes, with the four output options. Option 1 is the node permutation, option 2 the edge list permuted (Box 3).

Box 216becl (cl5.cf)

gd15 <- read.table(ex("gd15.dat"), col.names = c("I", "J", "V"))
g15 <- tda_graph(gd15, directed = FALSE)
tda_g(g15, "becl", list(opt = 1, fmt = "3.0"))$table
  index node position node_at
1     1    3        1       3
2     2    2        2       2
3     3    5        3       5
4     4    4        4       4
5     5    1        5       1
tda_g(g15, "becl", list(opt = 2, fmt = "3.0"))$table
  V1 V2 V3 V4 V5 V6 V7
1  1  2  3  2  3  2  1
2  3  4  5  4  5  4  1
3  3  5  5  1  5  1  1
4  4  5  4  1  4  1  1
# options 3 and 4 take sc=, the value to fill a non-edge with; the
# default is -1 and cl5.cf asks for 0
tda_g(g15, "becl", list(opt = 3, fmt = "3.0", sc = 0))$table
   V1 V2 V3 V4 V5 V6 V7
1   2  1  2  3  2  3  1
2   3  1  5  3  5  3  0
3   3  2  5  2  5  2  0
4   4  1  4  3  4  3  0
5   4  2  4  2  4  2  0
6   4  3  4  5  4  5  1
7   5  1  1  3  1  3  0
8   5  2  1  2  1  2  0
9   5  3  1  5  1  5  1
10  5  4  1  4  1  4  1
tda_g(g15, "becl", list(opt = 4, fmt = "3.0", sc = 0))$table
  V1 V2 V3 V4 V5
1  0  1  0  0  0
2  1  0  0  0  0
3  0  0  0  1  1
4  0  0  1  0  1
5  0  0  1  1  0

7.6.1.1 Direct and Indirect Control

tda_gfcf() is gfcf, control in a directed valued graph – gd3.dat is a small ownership network. With sc = 49 (control at more than 49 per cent) node 1 controls nodes 2 to 9, node 9 controls 1 to 8, node 3 controls 5; the pair table of the manual’s Box 2 is the result’s "pairs" attribute.

Box 217gfcf (gd13.cf)

gd3 <- read.table(ex("gd3.dat"), col.names = c("I", "J", "V"))
ctl <- tda_gfcf(gd3, threshold = 49)
ctl
   node n_controlled      controlled
1     1            8 2,3,4,5,6,7,8,9
2     2            0                
3     3            1               5
4     4            0                
5     5            0                
6     6            0                
7     7            0                
8     8            0                
9     9            8 1,2,3,4,5,6,7,8
10   10            0                
11   11            0                
head(attr(ctl, "pairs"), 8)
  i node controlled L  S M R
1 1    1          2 1 50 8 8
2 1    1          3 1 50 8 8
3 1    1          4 4 60 8 8
4 1    1          5 2 50 8 8
5 1    1          6 3 60 8 8
6 1    1          7 2 60 8 8
7 1    1          8 3 60 8 8
8 1    1          9 5 50 8 8

gbcf is the same question backwards – who controls a given node – on gd3a.dat. It writes two files: the pair table, and a summary with one row per node (the manual’s Box 4 shows both).

Box 218gbcf (gd14.cf)

gd3a <- read.table(ex("gd3a.dat"), col.names = c("I", "J", "V"))
bwd <- tda_g_backward(tda_graph(gd3a, directed = TRUE), sc = 49)
bwd$table
   i node_i n_a node_j a_ji n_y node_k y_ki
1  2      2   1      1   50   1      1   50
2  3      3   1      1   50   1      1   50
3  4      4   4      2   30   2      1   60
4  4      4   4      3   10   2     10   55
5  4      4   4      8   20   2     -1    0
6  4      4   4     10   55   2     -1    0
7  5      5   1      3   50   1      1   50
8  6      6   3      3   30   2      1   60
9  6      6   3      7   30   2     11   22
10 6      6   3     11   22   2     -1    0
11 7      7   2      1   20   1      1   60
12 7      7   2      3   40   1     -1    0
13 8      8   2      5   30   1      1   60
14 8      8   2      7   30   1     -1    0
15 9      9   2      1   10   1      1   50
16 9      9   2      4   40   1     -1    0
bwd$summary
   i node_i m
1  1      1 8
2 10     10 1
3 11     11 0
# gd14a.cf asks for the second layout, opt = 2
tda_g_backward(tda_graph(gd3a, directed = TRUE), sc = 49,
               opt = 2, fmt = "3.0")$table
  V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13
1  2  2  4  1  1 50  0  0  0  50   0   0   0
2  3  3  4  1  1 50  0  0  0  50   0   0   0
3  4  4  4  4  2 55 30 20 10  60  55   0   0
4  5  5  4  1  1 50  0  0  0  50   0   0   0
5  6  6  4  3  2 30 30 22  0  60  22   0   0
6  7  7  4  2  1 40 20  0  0  60   0   0   0
7  8  8  4  2  1 30 30  0  0  60   0   0   0
8  9  9  4  2  1 40 10  0  0  50   0   0   0

In gd12.cf node 1 is drawn as a square, shape = for plg’s gt=2, and several edges are curved or run back to an earlier node.

Box 219gd12.cf

p <- tda_ps(xlim = c(0, 11), ylim = c(2.0, 7.5), width = 100, height = 50)

nodes <- data.frame(
    id = c(9, 1, 3, 2, 4, 10, 7, 6, 5, 8, 11),
    x  = c(3, 3, 5, 5, 7, 9, 3, 5, 7, 9, 7),
    y  = c(7, 5, 5, 6, 5, 5, 3, 3, 4, 3, 3),
    grey = 0.8,
    shape = c(1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1))

edges <- data.frame(
    from  = c(9, 1, 1, 1, 2, 10, 3, 1, 3, 3, 11, 7, 5, 8, 7, 3, 4),
    to    = c(1, 9, 2, 3, 4, 4, 5, 7, 7, 6, 6, 8, 8, 4, 6, 4, 9),
    label = c(50, 10, 50, 50, 30, 15, 50, 20, 40, 30, 22, 30, 30, 20, 30, 10, 40),
    curve = c(5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 10),
    arrow = "1.2,1.0")

p <- tda_pl_graph(p, nodes, edges)

plot(p)

7.6.1.2 Control

gio computes ownership shares in a directed valued graph: for each pair, the direct share A(i,j) and the share including holdings held through other nodes, Y(i,j). gd11.cf runs it on gd2b.dat.

Box 220gio (gd11.cf)

gd2b <- read.table(ex("gd2b.dat"), col.names = c("I", "J", "V"))
tda_g_ownership(tda_graph(gd2b, directed = TRUE))$table
   i node_i node_j a_ij      y_ij c iterations
1  1      1      1  0.0 0.0000000 4          4
2  1      1      2  0.5 0.5081285 4          4
3  1      1      3  0.0 0.0812940 4          4
4  1      1      4  0.0 0.1524330 4          4
5  2      2      2  0.0 0.0160000 3          2
6  2      2      3  0.1 0.1600000 3          2
7  2      2      4  0.3 0.3000000 3          2
8  3      3      2  0.1 0.1000000 3          1
9  3      3      3  0.0 0.0160000 3          1
10 3      3      4  0.0 0.0300000 3          1
11 4      4      2  0.0 0.0202000 3          2
12 4      4      3  0.2 0.2020200 3          2
13 4      4      4  0.0 0.0060600 3          2

Two graphs on one page; in the second a reciprocal pair of edges is drawn as two curves, and all edges carry arrowheads.

Box 221gd9.cf

p <- tda_ps(xlim = c(0, 11.5), ylim = c(0, 4), width = 80, height = 40)

nodesA <- data.frame(id = 1:4, x = c(0.5, 3, 5, 5), y = c(2, 2, 1, 3),
                     grey = 0.8)
edgesA <- data.frame(from = c(1, 2, 2, 4), to = c(2, 4, 3, 3),
                     label = c(0.5, 0.3, 0.1, 0.2), arrow = "1.5,1.0")
p <- tda_pl_graph(p, nodesA, edgesA)

nodesB <- data.frame(id = 1:4, x = c(6.5, 9, 11, 11), y = c(2, 2, 1, 3),
                     grey = 0.8)
edgesB <- data.frame(from = c(1, 2, 2, 4, 3), to = c(2, 4, 3, 3, 2),
                     label = c(0.5, 0.3, 0.1, 0.2, 0.1),
                     curve = c(0, 0, 0, 0, -3), arrow = "1.5,1.0")
p <- tda_pl_graph(p, nodesB, edgesB)

p <- tda_pl_text(p, "Graph A", at = c(0.5, 3))
p <- tda_pl_text(p, "Graph B", at = c(6.5, 3))

plot(p)

7.6.1.3 Measures of Flow Centrality

gfc (tda_g(g, "gfc")) is flow centrality on gd14.dat (gd41.cf), one record per pair of nodes.

Box 222gfc (gd41.cf)

gd14 <- read.table(ex("gd14.dat"), col.names = c("I", "J", "V"))
tda_g(tda_graph(gd14, directed = TRUE), "gfc", list(fmt = "4.0"))$table
  i j node_i node_j f1 f2 f3 f4 f5 f6
1 1 2      1      2  3 -1 -1 -1 -1 -1
2 1 3      1      3  6 -1  3 -1  4 -1
3 1 4      1      4  2 -1 -1 -1 -1 -1
4 1 5      1      5  2 -1  2  0  2 -1
5 2 3      2      3  3 -1 -1 -1 -1 -1
6 2 5      2      5  2 -1 -1  0 -1 -1
7 3 5      3      5  2 -1 -1 -1 -1 -1
8 4 3      4      3  2 -1 -1 -1 -1 -1
9 4 5      4      5  2 -1 -1  0 -1 -1

The section’s Figure 1 is the directed graph the examples use, drawn by gd40.cf.

Box 223gd40.cf

p <- tda_ps(xlim = c(0, 8), ylim = c(-1.5, 7), width = 80, height = 40)
nodes <- data.frame(id = 1:5, x = c(1, 3, 5, 3, 7), y = c(3, 6, 3, 0, 3),
                    grey = 0.8)
edges <- data.frame(
    from  = c(1, 1, 1, 2, 4, 3),
    to    = c(2, 3, 4, 3, 3, 5),
    label = c(3, 1, 2, 3, 2, 2),
    arrow = "1.5,1.0")
p <- tda_pl_graph(p, nodes, edges)

plot(p)

8 Set-valued Data

8.3.2 Evaluation of Inclusion Functions

Chapter 8 is about set-valued data: a case is not a number but an interval, and the answer is an interval too. tda_evalfi() evaluates a function over interval arguments.

The manual’s box is evalfi1: the function’s inclusion over the interval and its derivative’s, derivative = TRUE.

Box 224An inclusion function and its derivative (evalfi1)

tda_evalfi("sin(x)", x = c(0, 1))
   lower    upper 
0.000000 0.841471 
tda_evalfi("sin(x)", x = c(0, 1), derivative = TRUE)
    lower     upper 
0.5403023 1.0000000 

8.4.1 The gmin Command

tda_gmin() finds the global minimum by subdivision. start= gives the search interval, as c(step, lower, upper); tol_fe= is the tolerance on the function value within which a box is finally accepted. TDA 6.4p’s default is 1e-10, and with it the box containing the minimum of sin(x) on [0, 10] is only temporarily accepted (its lower bound is -1 to sixteen digits, but not to ten); 1e-6 accepts it, giving one box with x in [4.71237, 4.71245].

The manual’s box reports 39 iterations, 41 function evaluations and 77 inclusion evaluations. The program does not: running the manual’s command file unchanged through TDA gives 19, 20 and 35, and so does the call here. The manual is out of date on this box.

Box 225gmin

g <- tda_gmin("sin(x)", start = list(c(1, 0, 10)), use_derivatives = TRUE,
              tol_fe = 1e-6)
g$value
[1] -1
g$boxes
  box accepted        width lower_f upper_f
1   1        2 7.629395e-05      -1      -1
g$estimates
  box parameter    lower    upper
1   1         x 4.712372 4.712448
# the counters TDA prints above the boxes
g$counts
           iterations           evaluations inclusion_evaluations            boxes_used 
                   19                    20                    35                     3 
      boxes_temporary        boxes_accepted 
                    1                     1 

8.4.2 The range Command

Box 226range

tda_range("sin(x)", start = list(c(1, 0, 10)), use_derivatives = TRUE)
Call: tda_range("sin(x)", start = list(c(1, 0, 10)), use_derivatives = TRUE)

Range: [ -1 , 1 ]

8.5.1 Set-valued Discrete Variables

self_consistent = TRUE asks for the self-consistent estimate rather than the bounds alone.

Box 227Distribution of a set-valued discrete variable

# the manual's box shows both data files as well as the results
id1 <- read.table(ex("id1.dat"))
id1
  V1 V2 V3
1  1  0  0
2  0  1  0
3  0  0  1
4  1  1  0
5  0  1  1
tda_sddf(id1, self_consistent = TRUE)
Call: tda_sddf(sets = id1, self_consistent = TRUE)

Cases: 

 category lower upper mean_df self_consistent_df
       V1   0.2   0.4     0.3          0.2765957
       V2   0.2   0.6     0.4          0.4468085
       V3   0.2   0.4     0.3          0.2765957
id1a <- read.table(ex("id1a.dat"))
id1a
  V1 V2
1  1  1
2  2  2
3  3  3
4  1  2
5  2  3
tda_iddf(~iv(V1, V2), data = id1a, self_consistent = TRUE)
Call: tda_iddf(~iv(V1, V2), data = id1a, self_consistent = TRUE)

Cases: 5 

 index lower upper mean_df self_consistent_df
     1   0.2   0.4     0.3          0.2765957
     2   0.2   0.6     0.4          0.4468085
     3   0.2   0.4     0.3          0.2765957

8.5.2 Interval-valued Variables

iv(lower, upper) is the interval-valued variable, the counterpart of Surv() for this chapter.

Box 228Distribution of an interval-valued variable

id2 <- read.table(ex("id2.dat"))
id2$XL <- id2$V1 - 30L
id2$XH <- id2$V1 + 30L
tda_idf(~iv(XL, XH), data = id2)
Call: tda_idf(~iv(XL, XH), data = id2)

Cases: 11 

 index partition      lower      upper    mean_df
     1      1870 0.00000000 0.09090909 0.00000000
     2      1920 0.00000000 0.18181818 0.07575758
     3      1930 0.09090909 0.18181818 0.10606061
     4      1970 0.09090909 0.27272727 0.16666667
     5      1980 0.18181818 0.36363636 0.19696970
     6      2000 0.18181818 0.45454545 0.25757576
     7      2020 0.18181818 0.54545455 0.34848485
     8      2030 0.27272727 0.54545455 0.40909091
     9      2040 0.36363636 0.54545455 0.45454545
    10      2060 0.45454545 0.54545455 0.51515152
    11      2070 0.45454545 0.63636364 0.53030303
    12      2080 0.54545455 0.72727273 0.56060606
    13      2110 0.54545455 0.90909091 0.65151515
    14      2120 0.54545455 1.00000000 0.71212121
    15      2130 0.63636364 1.00000000 0.78787879
    16      2140 0.72727273 1.00000000 0.84848485
    17      2170 0.90909091 1.00000000 0.98484848
    18      2180 1.00000000 1.00000000 1.00000000

8.6.1 Mean Values

The mean of an interval-valued variable is itself an interval: every value in it is the mean of some admissible data set.

Box 229imean

tda_imean(~iv(XL, XH), data = id2)
Call: tda_imean(~iv(XL, XH), data = id2)

Cases: 11 
Bounds: [2022.727, 2082.727]

8.6.2 Variances

Box 230ivariance

tda_ivariance(~iv(V1, V2), data = id1a)
Call: tda_ivariance(~iv(V1, V2), data = id1a)

Cases: 5 
Bounds: [0.4, 0.8]