Mark Heckmann
Mark Heckmann

Reputation: 11441

Add horizontal line to terminal barplots in party / partykit trees

I am using the ctree function from partykit.

library(rpart)
library(partykit)

fit <- ctree(Kyphosis ~ Age + Number + Start, data=kyphosis)
plot(fit, terminal_panel=node_barplot)

enter image description here

I want to add an additional horizonal line to each barplot, indicating the average reponse across the dataset, i.e. at 0.79 here.

prop.table(table(kyphosis$Kyphosis))

    absent   present 
 0.7901235 0.2098765

Approach: I started to modify the node_barplot function that is passed to the terminal_panel argument. But the source code is very long and comes with almost no comments. So I tried to go step by step, stripping down the function to its first two lines of code (plus an extra print command). However, if I run it, the object y is NULL and an error is thrown.

node_barplot2 <- function(obj, ...)
{   
  y <- obj$fitted[["(response)"]]   # first lime from node_barplot source
  print(y)
  stopifnot(is.factor(y) || isTRUE(all.equal(round(y), y)) || is.data.frame(y))
}

plot(fit, terminal_panel=node_barplot2)

> Error in round(y) : Non-numeric argument in mathematical function

As its the original code, I do not quite see where I go wrong here and how I could draw the horizontal line. Any ideas?

Upvotes: 1

Views: 661

Answers (2)

Mark Heckmann
Mark Heckmann

Reputation: 11441

Just for completeness: As Achim explained, the class attribute was not correct to indicate that the function has to be passed the whole tree, not just a node. Setting it to class(node_barplot2) <- "grapcon_generator" does the trick.

I slightly tweaked the node_barplot code and added two new arguments to the function: hline and h.gp. The first one specifies where the horizontal line is drawn (a value between 0 and 1). The line is the same across all terminal panels. The second one takes a gpar object used to style the drawn line. The function is named node_barplot2, you can find the gist here. The code that draws the line is at the end.

Example

library(devtools)
source_gist("0313362f0c84b21625bd")

plot(fit, terminal_panel = node_barplot2, 
     tp_args= list(hline = .8, 
                   h.gp = gpar(lwd=4, col="blue")))

enter image description here

Upvotes: 1

Achim Zeileis
Achim Zeileis

Reputation: 17203

The partykit distinguishes "panel" functions and "panel-generating" functions:

  • The former just expect the node of a tree as their sole argument and then draw this node (using grid graphics).

  • The latter expect a full tree as their first argument plus further arguments for customization. They return a "panel" function (with only argument node) where certain informations like the x and y ranges are stored in the function environment.

To signal that a function is a panel-generating function, it has to have class "grapcon_generator". Hence

class(node_barplot)
## [1] "grapcon_generator"

To add certain graphical elements to the function, I would recommend copying the whole node_barplot source code (including the class assignment at the end) and then add the elements you need, e.g., a horizontal reference line you can draw with grid.lines().

Upvotes: 2

Related Questions