# Ctree in R: how optimal is the optimal split point?

Hi I’m fairly new to using decion trees. I understand that to find the best split points, the ctree algorithm maximises a certain test statistic. I am interested to inspect the values of the test statistic for all possible split points, so that I can gain insight in how optimal the exact split point is.

As some background, in my data the ctree algorithm for variables Y ~ X returned an optimal binary split at X = 2.5. Indeed this creates two groups with different Y values. Yet it seems as if splitting the data at X = 2.4 or 2.6 gives practically equal results. I’d like to assess this, for instance by plotting the test statistic over the different split points.

This is a possible check but, unfortunately, ctree() does not include this information in its output as this would potentially require a lot of storage on larger data sets. But you can compute the information "by hand" using the coin package, see below for a worked example.

Moreover, you could assess the stability of the tree and the estimated split points by bootstrapping. This is available out of the box in the stablelearner package and in my opinion the more comprehensive check.

But first about the statistics for the split point. As a simple example consider a tree for the cars data:

library("partykit")
ct <- ctree(dist ~ speed, data = cars)
plot(ct)


Thus, the first split is speed <= 17". This can be found by computing a two-sampleindependence_test()with thecoin package for every possible split point:

library("coin")
sp <- 8:23
st <- sapply(sp, function(split)
statistic(independence_test(dist ~ factor(speed <= split), data = cars))
)
plot(sp, st, type = "b")
abline(v = sp[which.max(st)], lty = 2)


Alternatively, you can bootstrap the learning data and re-fit the tree on every bootstrap sample. This is what stabletree() from the stablelearner package offers along with nice functions to summarize and visualize the variation in the resulting split points. Here:

library("stablelearner")
sb <- stabletree(ct)
plot(sb)


Thus, most bootstrap samples lead to splits points that are very close to the two split points from the original tree (ct).

The following code might be better

library("coin")
sp <-sort(unique(cars\$speed))[-1]
p <- sapply(sp, function(split)
pvalue(independence_test(dist ~ factor(speed <= split), data = cars))
)

plot(sp, p, type = "b")
abline(v = sp[which.min(p)], lty = 2)
grid()

cat(sp[which.min(p)])
`

• Hello. This is effectively a code-only answer. Can you explain / comment on why this solution is better than the one by @AchimZeileis which also uses {coin}? Commented Apr 15 at 7:20
• Why is this code better? How does it help OP solve their problem? Please edit to clarify.
– Sycorax
Commented Apr 15 at 15:19
• The code shows that instead of maximizing the test statistic you can also minimize the p-value. These are flipsides of the same "coin" (Apologies...). Using the statistic is often easier to interpret for comparing splits in the same variable (as in this case) - while p-values are crucial for bringing everything to the same scale across variables. Commented Apr 15 at 18:05