a b/partyMod/R/Plot.R
1
2
# $Id$
3
4
## utility functions for querying the number of
5
## terminal nodes and the maximal depth of (sub-)trees
6
nterminal <- function(node) {
7
    if (node$terminal) return(1)
8
    nl <- nterminal(node$left)
9
    nr <- nterminal(node$right)
10
    return(nl + nr)
11
}
12
13
maxdepth <- function(node) {
14
    if (node$terminal) return(1)
15
    nl <- maxdepth(node$left)
16
    nr <- maxdepth(node$right)
17
    return(max(c(nl, nr)) + 1)
18
}
19
20
21
## panel functions for labeling trees:
22
## inner and terminal nodes and edges.
23
24
node_inner <- function(ctreeobj,
25
                       digits = 3,
26
               abbreviate = FALSE,
27
               fill = "white",
28
               pval = TRUE,
29
               id = TRUE)
30
{
31
    getLabel1 <- function(x) {
32
        if (x$terminal) return(rep.int("", 2))
33
        varlab <- ifelse(abbreviate > 0,
34
            abbreviate(x$psplit$variableName, as.numeric(abbreviate)),
35
        x$psplit$variableName)
36
    if(pval) {
37
            pvalue <- 1 - x$criterion$maxcriterion
38
            plab <- ifelse(pvalue < 10^(-digits),
39
                           paste("p <", 10^(-digits)),
40
                           paste("p =", round(pvalue, digits = digits)))
41
    } else {
42
        plab <- ""
43
    }
44
        return(c(varlab, plab))
45
    }
46
47
    maxstr <- function(node) {
48
        lab <- getLabel1(node)
49
        msl <- ifelse(node$terminal, "", maxstr(node$left))
50
        msr <- ifelse(node$terminal, "", maxstr(node$right))
51
        lab <- c(lab, msl, msr)
52
        return(lab[which.max(nchar(lab))])
53
    }
54
55
    nstr <- maxstr(ctreeobj@tree) 
56
57
    ### panel function for the inner nodes
58
    rval <- function(node) {
59
    
60
        node_vp <- viewport(x = unit(0.5, "npc"),
61
                        y = unit(0.5, "npc"),
62
                        width = unit(1, "strwidth", nstr) * 1.3, 
63
                        height = unit(3, "lines"),
64
                name = paste("node_inner", node$nodeID, sep = ""))
65
        pushViewport(node_vp)
66
67
        xell <- c(seq(0, 0.2, by = 0.01),
68
              seq(0.2, 0.8, by = 0.05),
69
          seq(0.8, 1, by = 0.01))
70
    yell <- sqrt(xell * (1-xell))
71
    
72
    lab <- getLabel1(node)
73
    fill <- rep(fill, length.out = 2)
74
    
75
        grid.polygon(x = unit(c(xell, rev(xell)), "npc"),
76
                     y = unit(c(yell, -yell)+0.5, "npc"),
77
                     gp = gpar(fill = fill[1]))
78
        grid.text(lab[1], y = unit(1.5 + 0.5 * pval, "lines"))
79
        if(pval) grid.text(lab[2], y = unit(1, "lines"))
80
81
        if (id) {
82
            nodeIDvp <- viewport(x = unit(0.5, "npc"), y = unit(1, "npc"),
83
            width = max(unit(1, "lines"), unit(1.3, "strwidth", as.character(node$nodeID))),
84
            height = max(unit(1, "lines"), unit(1.3, "strheight", as.character(node$nodeID))))
85
            pushViewport(nodeIDvp)
86
            grid.rect(gp = gpar(fill = fill[2]))
87
            grid.text(node$nodeID)
88
            popViewport()
89
        }
90
        upViewport()
91
    }
92
    
93
    return(rval)
94
}
95
class(node_inner) <- "grapcon_generator"
96
97
node_surv <- function(ctreeobj,
98
              ylines = 2,
99
              id = TRUE, ...)
100
{
101
    survobj <- response(ctreeobj)[[1]]
102
    if (!("Surv" %in% class(survobj))) 
103
        stop(sQuote("ctreeobj"), " is not a survival tree")
104
105
    ### panel function for Kaplan-Meier curves in nodes
106
    rval <- function(node) {
107
        km <- mysurvfit(survobj, weights = node$weights, ...)
108
109
        a <- dostep(km$time, km$surv)
110
111
        yscale <- c(0,1)
112
        xscale <- c(0, max(survobj[,1]))
113
114
        top_vp <- viewport(layout = grid.layout(nrow = 2, ncol = 3,
115
                           widths = unit(c(ylines, 1, 1), 
116
                                         c("lines", "null", "lines")),  
117
                           heights = unit(c(1, 1), c("lines", "null"))),
118
                           width = unit(1, "npc"), 
119
                           height = unit(1, "npc") - unit(2, "lines"),
120
               name = paste("node_surv", node$nodeID, sep = ""))
121
122
        pushViewport(top_vp)
123
        grid.rect(gp = gpar(fill = "white", col = 0))
124
125
        ## main title
126
        top <- viewport(layout.pos.col=2, layout.pos.row=1)
127
        pushViewport(top)
128
    mainlab <- paste(ifelse(id, paste("Node", node$nodeID, "(n = "), "n = "),
129
                     sum(node$weights), ifelse(id, ")", ""), sep = "")
130
        grid.text(mainlab)
131
        popViewport()
132
    
133
        plot <- viewport(layout.pos.col=2, layout.pos.row=2,
134
                         xscale=xscale, yscale=yscale,
135
             name = paste("node_surv", node$nodeID, "plot", 
136
                         sep = ""))
137
138
        pushViewport(plot)
139
        grid.lines(a$x/max(survobj[,1]), a$y)
140
        grid.xaxis()
141
        grid.yaxis()
142
        grid.rect(gp = gpar(fill = "transparent"))
143
        upViewport(2)
144
    }
145
146
    return(rval)
147
}
148
class(node_surv) <- "grapcon_generator"
149
150
node_barplot <- function(ctreeobj,
151
                         col = "black",
152
                     fill = NULL,
153
             beside = NULL,
154
                 ymax = NULL,
155
                 ylines = NULL,
156
                 widths = 1,
157
                 gap = NULL,
158
             reverse = NULL,
159
                 id = TRUE)
160
{   
161
    getMaxPred <- function(x) {
162
      mp <- max(x$prediction)
163
      mpl <- ifelse(x$terminal, 0, getMaxPred(x$left))
164
      mpr <- ifelse(x$terminal, 0, getMaxPred(x$right))
165
      return(max(c(mp, mpl, mpr)))
166
    }
167
168
    y <- response(ctreeobj)[[1]]
169
    
170
    if(is.factor(y) || class(y) == "was_ordered") {
171
        ylevels <- levels(y)
172
    if(is.null(beside)) beside <- if(length(ylevels) < 3) FALSE else TRUE
173
        if(is.null(ymax)) ymax <- if(beside) 1.1 else 1
174
    if(is.null(gap)) gap <- if(beside) 0.1 else 0
175
    } else {
176
        if(is.null(beside)) beside <- FALSE
177
        if(is.null(ymax)) ymax <- getMaxPred(ctreeobj@tree) * 1.1
178
        ylevels <- seq(along = ctreeobj@tree$prediction)
179
        if(length(ylevels) < 2) ylevels <- ""
180
    if(is.null(gap)) gap <- 1
181
    }
182
    if(is.null(reverse)) reverse <- !beside
183
    if(is.null(fill)) fill <- gray.colors(length(ylevels))
184
    if(is.null(ylines)) ylines <- if(beside) c(3, 2) else c(1.5, 2.5)
185
186
    ### panel function for barplots in nodes
187
    rval <- function(node) {
188
    
189
        ## parameter setup
190
        pred <- node$prediction
191
    if(reverse) {
192
      pred <- rev(pred)
193
      ylevels <- rev(ylevels)
194
    }
195
        np <- length(pred)
196
    nc <- if(beside) np else 1
197
198
    fill <- rep(fill, length.out = np)  
199
        widths <- rep(widths, length.out = nc)
200
    col <- rep(col, length.out = nc)
201
    ylines <- rep(ylines, length.out = 2)
202
203
    gap <- gap * sum(widths)
204
        yscale <- c(0, ymax)
205
        xscale <- c(0, sum(widths) + (nc+1)*gap)
206
207
        top_vp <- viewport(layout = grid.layout(nrow = 2, ncol = 3,
208
                           widths = unit(c(ylines[1], 1, ylines[2]), c("lines", "null", "lines")),
209
                           heights = unit(c(1, 1), c("lines", "null"))),
210
                           width = unit(1, "npc"), 
211
                           height = unit(1, "npc") - unit(2, "lines"),
212
               name = paste("node_barplot", node$nodeID, sep = ""))
213
214
        pushViewport(top_vp)
215
        grid.rect(gp = gpar(fill = "white", col = 0))
216
217
        ## main title
218
        top <- viewport(layout.pos.col=2, layout.pos.row=1)
219
        pushViewport(top)
220
    mainlab <- paste(ifelse(id, paste("Node", node$nodeID, "(n = "), "n = "),
221
                     sum(node$weights), ifelse(id, ")", ""), sep = "")
222
        grid.text(mainlab)
223
        popViewport()
224
    
225
        plot <- viewport(layout.pos.col=2, layout.pos.row=2,
226
                         xscale=xscale, yscale=yscale,
227
             name = paste("node_barplot", node$nodeID, "plot", 
228
                         sep = ""))
229
230
        pushViewport(plot)
231
    
232
    if(beside) {
233
      xcenter <- cumsum(widths+gap) - widths/2
234
      for (i in 1:np) {
235
            grid.rect(x = xcenter[i], y = 0, height = pred[i], 
236
                      width = widths[i],
237
                  just = c("center", "bottom"), default.units = "native",
238
                  gp = gpar(col = col[i], fill = fill[i]))
239
      }
240
          if(length(xcenter) > 1) grid.xaxis(at = xcenter, label = FALSE)
241
      grid.text(ylevels, x = xcenter, y = unit(-1, "lines"), 
242
                    just = c("center", "top"),
243
                default.units = "native", check.overlap = TRUE)
244
          grid.yaxis()
245
    } else {
246
      ycenter <- cumsum(pred) - pred
247
248
      for (i in 1:np) {
249
            grid.rect(x = xscale[2]/2, y = ycenter[i], height = min(pred[i], ymax - ycenter[i]), 
250
                      width = widths[1],
251
                  just = c("center", "bottom"), default.units = "native",
252
                  gp = gpar(col = col[i], fill = fill[i]))
253
      }
254
          if(np > 1) {
255
        grid.text(ylevels[1], x = unit(-1, "lines"), y = 0,
256
                      just = c("left", "center"), rot = 90,
257
                  default.units = "native", check.overlap = TRUE)
258
        grid.text(ylevels[np], x = unit(-1, "lines"), y = ymax,
259
                      just = c("right", "center"), rot = 90,
260
                  default.units = "native", check.overlap = TRUE)
261
      }
262
          if(np > 2) {
263
        grid.text(ylevels[-c(1,np)], x = unit(-1, "lines"), y = ycenter[-c(1,np)],
264
                      just = "center", rot = 90,
265
                  default.units = "native", check.overlap = TRUE)
266
      }
267
          grid.yaxis(main = FALSE)  
268
    }
269
    
270
        grid.rect(gp = gpar(fill = "transparent"))
271
        upViewport(2)
272
    }
273
    
274
    return(rval)
275
}
276
class(node_barplot) <- "grapcon_generator"
277
278
node_boxplot <- function(ctreeobj,
279
                         col = "black",
280
                 fill = "lightgray",
281
                 width = 0.5,
282
                 yscale = NULL,
283
                 ylines = 3,
284
             cex = 0.5,
285
                 id = TRUE)
286
{
287
    y <- response(ctreeobj)[[1]]
288
    if (!is.numeric(y))
289
        stop(sQuote("ctreeobj"), " is not a regression tree")
290
    if (is.null(yscale)) 
291
        yscale <- range(y) + c(-0.1, 0.1) * diff(range(y))
292
         
293
    ### panel function for boxplots in nodes
294
    rval <- function(node) {
295
    
296
        ## parameter setup
297
    x <- boxplot(rep.int(y, node$weights), plot = FALSE)
298
299
        top_vp <- viewport(layout = grid.layout(nrow = 2, ncol = 3,
300
                           widths = unit(c(ylines, 1, 1), 
301
                                         c("lines", "null", "lines")),  
302
                           heights = unit(c(1, 1), c("lines", "null"))),
303
                           width = unit(1, "npc"), 
304
                           height = unit(1, "npc") - unit(2, "lines"),
305
               name = paste("node_boxplot", node$nodeID, sep = ""))
306
307
        pushViewport(top_vp)
308
        grid.rect(gp = gpar(fill = "white", col = 0))
309
310
        ## main title
311
        top <- viewport(layout.pos.col=2, layout.pos.row=1)
312
        pushViewport(top)
313
    mainlab <- paste(ifelse(id, paste("Node", node$nodeID, "(n = "), "n = "),
314
                     sum(node$weights), ifelse(id, ")", ""), sep = "")
315
        grid.text(mainlab)
316
        popViewport()
317
    
318
        plot <- viewport(layout.pos.col = 2, layout.pos.row = 2,
319
                         xscale = c(0, 1), yscale = yscale,
320
             name = paste("node_boxplot", node$nodeID, "plot", 
321
                         sep = ""))
322
323
        pushViewport(plot)
324
    
325
    xl <- 0.5 - width/4
326
    xr <- 0.5 + width/4
327
328
        ## box & whiskers
329
        grid.lines(unit(c(xl, xr), "npc"), 
330
                   unit(x$stats[1], "native"), gp = gpar(col = col))
331
        grid.lines(unit(0.5, "npc"), 
332
                   unit(x$stats[1:2], "native"), gp = gpar(col = col, lty = 2))
333
        grid.rect(unit(0.5, "npc"), unit(x$stats[2], "native"), 
334
                  width = unit(width, "npc"), height = unit(diff(x$stats[c(2, 4)]), "native"),
335
                  just = c("center", "bottom"), 
336
                  gp = gpar(col = col, fill = fill))
337
        grid.lines(unit(c(0.5 - width/2, 0.5+width/2), "npc"), 
338
                   unit(x$stats[3], "native"), gp = gpar(col = col, lwd = 2))
339
        grid.lines(unit(0.5, "npc"), unit(x$stats[4:5], "native"), 
340
                   gp = gpar(col = col, lty = 2))
341
        grid.lines(unit(c(xl, xr), "npc"), unit(x$stats[5], "native"), 
342
                   gp = gpar(col = col))
343
344
        ## outlier
345
        n <- length(x$out)
346
        if (n > 0) {
347
            index <- 1:n ## which(x$out > yscale[1] & x$out < yscale[2])
348
            if (length(index) > 0)
349
                grid.points(unit(rep.int(0.5, length(index)), "npc"), 
350
                            unit(x$out[index], "native"),
351
                            size = unit(cex, "char"), gp = gpar(col = col))
352
        }
353
    
354
        grid.yaxis()
355
        grid.rect(gp = gpar(fill = "transparent"))
356
        upViewport(2)
357
    }
358
    
359
    return(rval)
360
}
361
class(node_boxplot) <- "grapcon_generator"
362
363
node_hist <- function(ctreeobj,
364
                      col = "black",
365
              fill = "lightgray",
366
              freq = FALSE,
367
              horizontal = TRUE,
368
              xscale = NULL,
369
              ymax = NULL,
370
              ylines = 3,
371
              id = TRUE,
372
              ...)
373
{
374
    y <- response(ctreeobj)[[1]]
375
    if (!is.numeric(y))
376
        stop(sQuote("ctreeobj"), " is not a regression tree")
377
    y <- rep.int(y, ctreeobj@tree$weights)
378
    yhist <- hist(y, plot = FALSE, ...)
379
    if (is.null(xscale)) 
380
        xscale <- range(yhist$breaks) + 
381
                        c(-0.05, 0.05) * diff(range(yhist$breaks))
382
    if (is.null(ymax)) {
383
        if (is.null(ymax)) 
384
            ymax <- if (freq) 0.7 * max(yhist$counts) 
385
                else 2.5 * max(yhist$density)
386
    }
387
    yscale <- c(0, ymax)
388
    
389
    if (horizontal) {
390
        yyy <- xscale
391
        xscale <- yscale
392
        yscale <- yyy
393
    }
394
         
395
    ### panel function for histograms in nodes
396
    rval <- function(node) {
397
    
398
        ## parameter setup
399
    yhist <- hist(rep.int(y, node$weights), plot = FALSE, ...)
400
401
        top_vp <- viewport(layout = grid.layout(nrow = 2, ncol = 3,
402
                           widths = unit(c(ylines, 1, 1), 
403
                                         c("lines", "null", "lines")),  
404
                           heights = unit(c(1, 1), c("lines", "null"))),
405
                           width = unit(1, "npc"), 
406
                           height = unit(1, "npc") - unit(2, "lines"),
407
               name = paste("node_hist", node$nodeID, sep = ""))
408
409
        pushViewport(top_vp)
410
        grid.rect(gp = gpar(fill = "white", col = 0))
411
412
        ## main title
413
        top <- viewport(layout.pos.col=2, layout.pos.row=1)
414
        pushViewport(top)
415
    mainlab <- paste(ifelse(id, paste("Node", node$nodeID, "(n = "), "n = "),
416
                     sum(node$weights), ifelse(id, ")", ""), sep = "")
417
        grid.text(mainlab)
418
        popViewport()
419
    
420
        plot <- viewport(layout.pos.col = 2, layout.pos.row = 2,
421
                         xscale = xscale, yscale = yscale,
422
             name = paste("node_hist", node$nodeID, "plot", 
423
                         sep = ""))
424
425
        pushViewport(plot)
426
    
427
        ## histogram
428
        xpos <- yhist$breaks[-1]
429
    ypos <- 0
430
    yheight <- if (freq) yhist$counts else yhist$density
431
    xwidth <- diff(yhist$breaks)
432
433
        if (horizontal) {
434
              yyy <- xpos
435
              xpos <- ypos
436
              ypos <- yyy
437
              yyy <- xwidth
438
              xwidth <- -yheight
439
              yheight <- yyy
440
        }
441
442
    grid.rect(x = xpos, y = ypos,
443
              width = xwidth, height = yheight,
444
          just = c("right", "bottom"), default.units = "native",
445
          gp = gpar(col = col, fill = fill))
446
    
447
        grid.xaxis()
448
        grid.yaxis()
449
        grid.rect(gp = gpar(fill = "transparent"))
450
        upViewport(2)
451
    }
452
    return(rval)
453
}
454
class(node_hist) <- "grapcon_generator"
455
456
node_density <- function(ctreeobj,
457
                         col = "black",
458
                 rug = TRUE,
459
                 horizontal = TRUE,
460
                 xscale = NULL,
461
                 yscale = NULL,
462
                 ylines = 3,
463
                 id = TRUE)
464
{
465
    y <- response(ctreeobj)[[1]]
466
    if (!is.numeric(y))
467
        stop(sQuote("ctreeobj"), " is not a regression tree")
468
    y <- rep.int(y, ctreeobj@tree$weights)
469
    ydens <- density(y)
470
    if (is.null(xscale)) 
471
        xscale <- range(ydens$x) + c(-0.05, 0.05) * diff(range(ydens$x))
472
    if (is.null(yscale)) {
473
        ymin <- if (rug) -max(ydens$y) * 0.1 else 0    
474
        yscale <- c(ymin, max(ydens$y) * 2.5)
475
    }
476
477
    xr <- xscale
478
    yr <- 0
479
    if (horizontal) {
480
        yyy <- xscale
481
        xscale <- yscale
482
        yscale <- yyy
483
    }
484
         
485
    ### panel function for density plots in nodes
486
    rval <- function(node) {
487
    
488
        ## parameter setup
489
    ydens <- density(rep.int(y, node$weights))
490
491
        top_vp <- viewport(layout = grid.layout(nrow = 2, ncol = 3,
492
                           widths = unit(c(ylines, 1, 1), 
493
                                         c("lines", "null", "lines")),  
494
                           heights = unit(c(1, 1), c("lines", "null"))),
495
                           width = unit(1, "npc"), 
496
                           height = unit(1, "npc") - unit(2, "lines"),
497
               name = paste("node_density", node$nodeID, sep = ""))
498
499
        pushViewport(top_vp)
500
        grid.rect(gp = gpar(fill = "white", col = 0))
501
502
        ## main title
503
        top <- viewport(layout.pos.col=2, layout.pos.row=1)
504
        pushViewport(top)
505
    mainlab <- paste(ifelse(id, paste("Node", node$nodeID, "(n = "), "n = "),
506
                     sum(node$weights), ifelse(id, ")", ""), sep = "")
507
        grid.text(mainlab)
508
        popViewport()
509
    
510
        plot <- viewport(layout.pos.col = 2, layout.pos.row = 2,
511
                         xscale = xscale, yscale = yscale,
512
             name = paste("node_density", node$nodeID, "plot", 
513
                         sep = ""))
514
515
        pushViewport(plot)
516
    
517
        ## density
518
        yd <- ydens$y
519
    xd <- ydens$x
520
521
        if (horizontal) {
522
            yyy <- xd
523
            xd <- yd
524
            yd <- yyy
525
            yyy <- xr
526
            xr <- yr
527
            yr <- yyy
528
        }
529
530
    if (rug) {
531
            if (horizontal)
532
            grid.rect(x = xscale[1], y = y[node$weights > 0],
533
                      height = 0, width = xscale[1],
534
                  default.units = "native", 
535
                          just = c("right", "bottom"))
536
            else
537
                grid.rect(x = y[node$weights > 0], y = yscale[1],
538
                      width = 0, height = abs(yscale[1]),
539
                  default.units = "native", 
540
                          just = c("center", "bottom"))
541
542
            grid.lines(x = xr, y = yr, gp = gpar(col = "lightgray"), 
543
                       default.units = "native")
544
            grid.lines(x = xr, y = yr, gp = gpar(col = "lightgray"), 
545
                       default.units = "native")
546
    }
547
    grid.lines(x = xd, y = yd, default.units = "native",
548
           gp = gpar(col = col))
549
    
550
        grid.xaxis()
551
        grid.yaxis()
552
        grid.rect(gp = gpar(fill = "transparent"))
553
        upViewport(2)
554
    }
555
    
556
    return(rval)
557
}
558
class(node_density) <- "grapcon_generator"
559
560
node_terminal <- function(ctreeobj,
561
                          digits = 3,
562
                  abbreviate = FALSE,
563
                  fill = c("lightgray", "white"),
564
                  id = TRUE)
565
{
566
    getLabel1 <- function(x) {
567
        if (!x$terminal) return(rep.int("", 2))
568
        nlab <- paste("n =", sum(x$weights))
569
        ylab <- if (length(x$prediction) > 1)
570
                    paste("y =", paste("(", paste(round(x$prediction, digits),
571
                  collapse = ", "), ")", sep =""))
572
                else
573
                paste("y =", round(x$prediction, digits))
574
      return(c(nlab, ylab))
575
    }
576
577
    maxstr <- function(node) {
578
        lab <- getLabel1(node)
579
        msl <- ifelse(node$terminal, "", maxstr(node$left))
580
        msr <- ifelse(node$terminal, "", maxstr(node$right))
581
        lab <- c(lab, msl, msr)
582
        return(lab[which.max(nchar(lab))])
583
    }
584
585
    nstr <- maxstr(ctreeobj@tree)
586
587
    ### panel function for simple n, Y terminal node labelling
588
    rval <- function(node) {
589
        fill <- rep(fill, length.out = 2)   
590
591
        node_vp <- viewport(x = unit(0.5, "npc"),   
592
                       y = unit(0.5, "npc"),   
593
                       width = unit(1, "strwidth", nstr) * 1.1,
594
                       height = unit(3, "lines"),
595
               name = paste("node_terminal", node$nodeID, sep = ""))
596
        pushViewport(node_vp)
597
598
        lab <- getLabel1(node)
599
    
600
        grid.rect(gp = gpar(fill = fill[1]))
601
        grid.text(y = unit(2, "lines"), lab[1])
602
        grid.text(y = unit(1, "lines"), lab[2])
603
604
        if (id) {
605
            nodeIDvp <- viewport(x = unit(0.5, "npc"), y = unit(1, "npc"),
606
            width = max(unit(1, "lines"), unit(1.3, "strwidth", as.character(node$nodeID))),
607
            height = max(unit(1, "lines"), unit(1.3, "strheight", as.character(node$nodeID))))
608
            pushViewport(nodeIDvp)
609
            grid.rect(gp = gpar(fill = fill[2], lty = "solid"))
610
            grid.text(node$nodeID)
611
            popViewport()
612
    }
613
        upViewport()
614
    }
615
    return(rval)
616
}
617
class(node_terminal) <- "grapcon_generator"
618
619
edge_simple <- function(treeobj, digits = 3, abbreviate = FALSE)
620
{
621
    ### panel function for simple edge labelling
622
    function(split, ordered = FALSE, left = TRUE) {
623
  
624
        if (is.numeric(split)) 
625
            split <- round(split, digits = digits)
626
        if (is.character(split) & abbreviate > 0) 
627
            split <- abbreviate(split, as.numeric(abbreviate))
628
629
        if (!ordered) {
630
            if (length(split) > 1) 
631
                split <- paste("{", paste(split, collapse = ", "), 
632
                               "}", sep="")
633
        } else {
634
            ### <FIXME> phantom and . functions cannot be found by
635
            ###         codetools
636
            ### </FIXME>
637
            if (left) split <- as.expression(bquote(phantom(0) <= .(split)))
638
                else split <- as.expression(bquote(phantom(0) > .(split)))
639
        }
640
        grid.rect(gp = gpar(fill = "white", col = 0), 
641
                  width = unit(1, "strwidth", split)) 
642
        grid.text(split, just = "center")
643
    }
644
}
645
class(edge_simple) <- "grapcon_generator"
646
647
plotTree <- function(node, xlim, ylim, nx, ny, 
648
               terminal_panel, inner_panel, edge_panel,
649
           tnex = 2, drop_terminal = TRUE, debug = FALSE) {
650
651
    ### the workhorse for plotting trees
652
653
    ### set up viewport for terminal node
654
    if (node$terminal) {
655
        x <- xlim[1] + diff(xlim)/2
656
        y <- ylim[1] + 0.5
657
       
658
        tn_vp <- viewport(x = unit(x, "native"),
659
                          y = unit(y, "native") - unit(0.5, "lines"),
660
                          width = unit(1, "native"), 
661
                          height = unit(tnex, "native") - unit(1, "lines"),
662
              just = c("center", "top"),
663
                          name = paste("Node", node$nodeID, sep = ""))
664
        pushViewport(tn_vp)
665
        if (debug)
666
            grid.rect(gp = gpar(lty = "dotted", col = 4))
667
        terminal_panel(node) 
668
        upViewport()
669
        return(NULL)
670
    }    
671
672
    ### number of left leafs
673
    nl <- nterminal(node$left)
674
675
    ### number of right leafs
676
    nr <- nterminal(node$right)
677
678
    ### position of inner node
679
    x0 <- xlim[1] + (nl / (nl + nr)) * diff(xlim)
680
    y0 <- max(ylim)
681
682
    ### proportion of left terminal nodes in left node
683
    if (node$left$terminal) {
684
        lf <- 1/2
685
    } else {
686
        lf <- nterminal(node$left$left) / (nterminal(node$left$left) + 
687
                                           nterminal(node$left$right))
688
    }
689
690
    ### proportion of left terminal nodes in right node
691
    if (node$right$terminal) {
692
        rf <- 1/2
693
    } else {
694
        rf <- nterminal(node$right$left) / (nterminal(node$right$left) + 
695
                                            nterminal(node$right$right))
696
    }
697
698
    ### position of left and right daugher node
699
    x1l <- xlim[1] + (x0 - xlim[1]) * lf
700
    x1r <- x0 + (xlim[2] - x0) * rf
701
    
702
    if (!drop_terminal) {
703
        y1l <- y1r <- y0 - 1
704
    } else {
705
        y1l <- if (node$left$terminal) tnex - 0.5 else y0 - 1
706
        y1r <- if (node$right$terminal) tnex - 0.5 else y0 - 1
707
    }
708
709
    ### draw edges
710
    grid.lines(x = unit(c(x0, x1l), "native"), 
711
               y = unit(c(y0, y1l), "native"))
712
    grid.lines(x = unit(c(x0, x1r), "native"), 
713
               y = unit(c(y0, y1r), "native"))
714
715
    ### create viewport for inner node
716
    in_vp <- viewport(x = unit(x0, "native"),
717
                      y = unit(y0, "native"),
718
                      width = unit(1, "native"),
719
                      height = unit(1, "native") - unit(1, "lines"), 
720
                      name = paste("Node", node$nodeID, sep = ""))
721
    pushViewport(in_vp)
722
    if (debug)
723
        grid.rect(gp = gpar(lty = "dotted"))
724
    inner_panel(node)
725
    upViewport()
726
727
    ps <- node$psplit
728
    if (ps$ordered) {
729
        if (!is.null(attr(ps$splitpoint, "levels"))) {
730
            split <- attr(ps$splitpoint, "levels")[ps$splitpoint]
731
        } else {
732
            split <- ps$splitpoint
733
        }
734
    } else {
735
        ### <FIXME>: always to the left? </FIXME>
736
        split <- attr(ps$splitpoint, "levels")[as.logical(ps$splitpoint) & (ps$table > 0)]
737
    }
738
739
740
    ### position of labels
741
    y1lr <- max(y1l, y1r)
742
    ypos <- y0 - (y0 - y1lr) * 0.5
743
    xlpos <- x0 - (x0 - x1l) * 0.5 * (y0 - y1lr)/(y0 - y1l)
744
    xrpos <- x0 - (x0 - x1r) * 0.5 * (y0 - y1lr)/(y0 - y1r)
745
746
    ### setup left label
747
    lsp_vp <- viewport(x = unit(xlpos, "native"),
748
                       y = unit(ypos, "native"),
749
                       width = unit(xlpos - xrpos, "native"),
750
                       height = unit(1, "lines"), 
751
                       name =  paste("lEdge", node$nodeID, sep = ""))
752
    pushViewport(lsp_vp)
753
    if (debug)
754
        grid.rect(gp = gpar(lty = "dotted", col = 2))
755
    edge_panel(split, ordered = ps$ordered, left = TRUE)
756
    upViewport()
757
758
    ### setup right label
759
    if (ps$ordered) {
760
        if (!is.null(attr(ps$splitpoint, "levels"))) {
761
            split <- attr(ps$splitpoint, "levels")[ps$splitpoint]
762
        } else {
763
            split <- ps$splitpoint
764
        }
765
    } else {
766
        split <- attr(ps$splitpoint, "levels")[!as.logical(ps$splitpoint) & (ps$table > 0)]
767
    }
768
769
    rsp_vp <- viewport(x = unit(xrpos, "native"),
770
                       y = unit(ypos, "native"),
771
                       width = unit(xlpos - xrpos, "native"),
772
                       height = unit(1, "lines"),
773
                       name =  paste("rEdge", node$nodeID, sep = ""))
774
    pushViewport(rsp_vp) 
775
    if (debug)
776
        grid.rect(gp = gpar(lty = "dotted", col = 2))
777
    edge_panel(split, ordered = ps$ordered, left = FALSE)
778
    upViewport()
779
780
    plotTree(node$left, c(xlim[1], x0), c(y1l, 1), nx, ny, 
781
      terminal_panel, inner_panel, edge_panel,
782
      tnex = tnex, drop_terminal = drop_terminal, debug = debug)
783
    plotTree(node$right, c(x0, xlim[2]), c(y1r, 1), nx, ny,
784
      terminal_panel, inner_panel, edge_panel,
785
      tnex = tnex, drop_terminal = drop_terminal, debug = debug)
786
}
787
788
789
plot.BinaryTree <- function(x, main = NULL, type = c("extended", "simple"),
790
                            terminal_panel = NULL, tp_args = list(),
791
                inner_panel = node_inner, ip_args = list(),
792
                            edge_panel = edge_simple, ep_args = list(),
793
                drop_terminal = (type[1] == "extended"),
794
                tnex = (type[1] == "extended") + 1, 
795
                newpage = TRUE,
796
                pop = TRUE,
797
                ...) {
798
799
    ### plot BinaryTree objects
800
801
    ### extract tree
802
    ptr <- x@tree
803
    ### total number of terminal nodes
804
    nx <- nterminal(ptr)
805
    ### maximal depth of the tree
806
    ny <- maxdepth(ptr)
807
808
    ### compute default settings
809
    type <- match.arg(type)
810
    if (type == "simple") {
811
        if (is.null(terminal_panel)) 
812
            terminal_panel <- node_terminal
813
        if (is.null(tnex)) tnex <- 1
814
    } else {
815
        if (is.null(terminal_panel))
816
            terminal_panel <- switch(class(response(x)[[1]])[1],
817
                                 "Surv" = node_surv,
818
                                     "factor" = node_barplot,
819
                                     "was_ordered" = node_barplot,
820
                                     "ordered" = node_barplot,
821
                                     node_boxplot)
822
        if (is.null(tnex)) tnex <- 2
823
    }
824
825
    ## setup newpage
826
    if (newpage) grid.newpage()
827
828
    ## setup root viewport
829
    root_vp <- viewport(layout = grid.layout(3, 3, 
830
                heights = unit(c(ifelse(is.null(main), 0, 3), 1, 1), 
831
                                      c("lines", "null", "lines")),
832
                widths = unit(c(1, 1, 1), 
833
                                     c("lines", "null", "lines"))), 
834
                name = "root")       
835
    pushViewport(root_vp)
836
  
837
    ## viewport for main title (if any)
838
    if (!is.null(main)) {
839
        main_vp <- viewport(layout.pos.col = 2, layout.pos.row = 1, 
840
                            name = "main")
841
        pushViewport(main_vp)
842
        grid.text(y = unit(1, "lines"), main, just = "center")
843
        upViewport()
844
    }
845
846
    ## setup viewport for tree
847
    tree_vp <- viewport(layout.pos.col = 2, layout.pos.row = 2, 
848
                xscale = c(0, nx), yscale = c(0, ny + (tnex - 1)), 
849
                        name = "tree")
850
    pushViewport(tree_vp)
851
852
    ### setup panel functions (if necessary)
853
    ### the heuristic is as follows: If the first argument
854
    ### is `ctreeobj' than we assume a panel generating function, 
855
    ### otherwise the function is treated as a panel function
856
    if(inherits(terminal_panel, "grapcon_generator"))
857
      terminal_panel <- do.call("terminal_panel", c(list(x), as.list(tp_args)))
858
    if(inherits(inner_panel, "grapcon_generator"))
859
      inner_panel <- do.call("inner_panel", c(list(x), as.list(ip_args)))
860
    if(inherits(edge_panel, "grapcon_generator"))
861
      edge_panel <- do.call("edge_panel", c(list(x), as.list(ep_args)))
862
863
864
    if((nx <= 1 & ny <= 1)) {
865
      pushViewport(plotViewport(margins = rep(1.5, 4), name = paste("Node", ptr$nodeID, sep = "")))
866
      terminal_panel(ptr)    
867
    } else {
868
      ## call the workhorse
869
      plotTree(ptr,
870
        xlim = c(0, nx), ylim = c(0, ny - 0.5 + (tnex - 1)),
871
        nx = nx, ny = ny, 
872
        terminal_panel = terminal_panel,
873
        inner_panel = inner_panel,
874
        edge_panel = edge_panel,
875
        tnex = tnex,
876
        drop_terminal = drop_terminal,
877
        debug = FALSE)
878
    }
879
    upViewport()
880
    if (pop) popViewport() else upViewport()
881
}