|
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 |
} |