---
title: "Manually specifying FFTs"
author: "Nathaniel Phillips and Hansjörg Neth"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
bibliography: fft.bib
csl: apa.csl
vignette: >
  %\VignetteIndexEntry{Manually specifying FFTs}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, echo = FALSE}
knitr::opts_chunk$set(collapse = FALSE, 
                      comment = "#>", 
                      prompt = FALSE,
                      tidy = FALSE,
                      echo = TRUE, 
                      message = FALSE,
                      warning = FALSE,
                      # Default figure options:
                      dpi = 100,
                      fig.align = 'center',
                      fig.height = 6.0, 
                      fig.width  = 6.5, 
                      out.width = "600px")
```


```{r pkgs, echo = FALSE, message = FALSE, results = 'hide'}
library(FFTrees)
```


## Manually specifying FFTs 

We usually create fast-and-frugal trees (FFTs) from data by using the `FFTrees()` function (see the [Main guide: FFTrees overview](guide.html) and the vignette on [Creating FFTs with FFTrees()](FFTrees_function.html) for instructions). 
However, we occasionally may want to design and evaluate specific FFTs (e.g., to test a hypothesis or include or exclude some variables based on theoretical considerations). 

There are two ways to manually define fast-and-frugal trees with the `FFTrees()` function: 

1. as a sentence using the `my.tree` argument (the easier way), or  

2. as a data frame using the `tree.definitions` argument (the harder way).  

<!-- Role of 'train' vs. 'test' data with manually defined FFTs: -->

Both of these methods require some data to evaluate the performance of FFTs, but will bypass the tree construction algorithms built into the **FFTrees** package. 
As manually created FFTs are not optimized for specific data, the key difference between fitting and predicting disappears for such FFTs. 
Although we can still use two sets of 'train' vs.\ 'test' data, a manually defined FFT is not fitted and hence should not be expected to perform systematically better on 'train' data than on 'test' data. 


### 1. Using `my.tree`

The first method for manually defining a FFT is to use the `my.tree` argument, where `my.tree` is a sentence describing a (single) FFT. 
When this argument is specified in `FFTrees()`, the function ---\ or rather its auxiliary `fftrees_wordstofftrees()` function\ --- attempts to interpret the verbal description and convert it into a valid definition of an FFT (as part of an `FFTrees` object). 

For example, let's use the `heartdisease` data to find out how some predictor variables (e.g., `sex`, `age`, etc.) predict the criterion variable (`diagnosis`):

```{r heart-data, echo = FALSE}
knitr::kable(head(heartdisease[c("sex", "age", "thal", "cp", "ca", 
                                 "diagnosis")]))
```

Table: **Table 1**: Five cues and the binary criterion variable `diagnosis` for the first cases of the `heartdisease` data. 

<!-- Verbal description of an FFT: -->

Here's how we could verbally describe an FFT by using the first three cues in conditional sentences: 

```{r my-tree-describe-fft-1}
in_words <- "If sex = 1, predict True.
             If age < 45, predict False. 
             If thal = {fd, normal}, predict True. 
             Otherwise, predict False."
```

As we will see shortly, the `FFTrees()` function accepts such descriptions (assigned here to a character string `in_words`) as its `my.tree` argument, create a corresponding FFT, and evaluate it on a corresponding dataset. 


#### Verbally defining FFTs 

Here are some **instructions** for manually specifying trees: 

- Each node must start with the word "If" and should correspond to the form: `If <CUE> <DIRECTION> <THRESHOLD>, predict <EXIT>`.

- Numeric thresholds should be specified directly (without brackets), like `age > 21`. 

- For categorical variables, factor thresholds must be specified within curly braces, like `sex = {male}`. 
For factors with sets of values, categories within a threshold should be separated by commas like `eyecolor = {blue,brown}`. 

- To specify cue directions, standard logical comparisons `=`, `!=`, `<`, `>=` (etc.) are valid. 
For numeric cues, only use `>`, `>=`, `<`, or\ `<=`. For factors, only use `=` or\ `!=`.

- Positive exits are indicated by `True`, while negative exits are specified by `False`. 

- The final node of an FFT is always bi-directional (i.e., has both a positive and a negative exit). 
The description of the final node always mentions its positive (`True`) exit first. 
The text `Otherwise, predict EXIT` that we have included in the example above is actually not necessary (and ignored). 


#### Example 

Now, let's use our verbal description of an FFT (assigned to `in_words` above) as the `my.tree` argument of the `FFTrees()` function. 
This creates a corresponding FFT and applies it to the `heartdisease` data:

```{r my-tree-create-1, message = FALSE, results = 'hide'}
# Create FFTrees from a verbal FFT description (as my.tree): 
my_fft <- FFTrees(formula = diagnosis ~.,
                  data = heartdisease,
                  main = "My 1st FFT", 
                  my.tree = in_words)
```

Running `FFTrees()` with the `my.tree` argument creates an object `my_fft` that contains one FFT. 
A verbal description of this tree can be printed by `inwords(my_fft)`, but we want to print or plot the object to evaluate the tree's performance on training or testing data. 
Let's see how well our manually constructed FFT (`my_fft`) did on the training data:

```{r my-tree-plot-1, fig.cap = "**Figure 1**: An FFT manually constructed using the `my.tree` argument of `FFTrees()`.", fig.show = 'hold'}
# Inspect FFTrees object:
plot(my_fft, data = "train")
```

<!-- Note absence of ROC curve: -->

When manually constructing a tree, the resulting `FFTrees` object only contains a single FFT. 
Hence, the ROC plot (in the right bottom panel of **Figure\ 1**) cannot show a range of FFTs, but locates the constructed FFT in ROC space. 

<!-- Note on tree definitions: -->

The formal definition of our new FFT is available from the `FFTrees` object\ `my_fft`:

```{r my-tree-def-1}
# Get FFT definition(s):
my_fft$trees$definitions
```

<!-- Note that noise exits (False/0) must have flipped direction symbols, as tree definitions always refer to signal (True/1): -->

Note that the 2nd\ node in this FFT (using the `age` cue) is predicting the _noise_ outcome (i.e., a non-final exit value of\ `0` or\ `FALSE`, shown to the left). 
As our tree definitions always refer to the _signal_ outcome (i.e., a non-final exit value of\ `1` or\ `TRUE`, shown to the right), 
the direction symbol of a left exit (i.e., the 2nd\ node in **Figure\ 1**: if `age < 45`, predict\ `0` or noise) must be flipped relative to its appearance in the tree definition (if `age >= 45`, predict\ `1` or signal). Thus, the plot and the formal definition describe the same\ FFT.

<!-- Performance: -->

As it turns out, the performance of our first FFT created from a verbal description is a mixed affair: 
The tree has a rather high sensitivity (of\ 91%), but its low specificity (of only\ 10%) allows for many false alarms. 
Consequently, its accuracy measures are only around baseline level. 


#### Creating an alternative FFT

Let's see if we can come up with a better FFT. 
The following example uses the cues\ `thal`, `cp`, and\ `ca` in the `my.tree` argument: 

```{r my-tree-fft-2-create, eval = FALSE, message = FALSE, results = 'hide'}
# Create a 2nd FFT from an alternative FFT description (as my.tree): 
my_fft_2 <- FFTrees(formula = diagnosis ~.,
                    data = heartdisease, 
                    main = "My 2nd FFT", 
                    my.tree = "If thal = {rd,fd}, predict True.
                               If cp != {a}, predict False. 
                               If ca > 1, predict True. 
                               Otherwise, predict False.")
```

<!-- Show the flexibility in interpreting `my.tree`: -->

As **FFTrees** aims to interpret the `my.tree` argument to the best of its abilities, there is some flexibility in entering a verbal description of an\ FFT. For instance, we also could have described our desired FFT in more flowery terms:

```{r my-tree-fft-2-create-2, eval = TRUE, message = FALSE, results = 'hide'}
# Create a 2nd FFT from an alternative FFT description (as my.tree): 
my_fft_2 <- FFTrees(formula = diagnosis ~.,
                    data = heartdisease, 
                    main = "My 2nd FFT", 
                    my.tree = "If thal equals {rd,fd}, we shall say True.  
                               If Cp differs from {a}, let's predict False.   
                               If CA happens to exceed 1, we insist on True.  
                               Else, give up and go away.") 
```

However, as the vocabulary of **FFTrees** is limited, it is safer to enter cue directions in their symbolic form (i.e., using\ `=`, `<`, `<=`, `>`, `>=`, or\ `!=`).^[Unambiguous `my.tree` descriptions must avoid using "is" and "is not" without additional qualifications (like "equal", "different", "larger", "smaller", etc.).] 
To verify that **FFTrees** interpreted our `my.tree` description, let's check whether the FFT of `inwords(my_fft_2)` yields a description that corresponds to our intended\ FFT:

```{r my-tree-fft-2-inwords}
inwords(my_fft_2)
```

As this seems (a more prosaic version of) what we wanted, let's visualize the best training tree (to evaluate its performance) and briefly inspect its tree definition:

```{r my-tree-plot-fft-2, fig.cap = "**Figure 2**: Another FFT manually constructed using the `my.tree` argument of `FFTrees()`.", fig.show = 'hold', collapse = TRUE}
# Visualize FFT:
plot(my_fft_2)

# FFT definition:
my_fft_2$trees$definitions
# Note the flipped direction value for 2nd cue (exit = '0'):
# 'if (cp  = a), predict 1' in the tree definition corresponds to 
# 'if (cp != a), predict 0' in the my.tree description and plot.  
```

This alternative FFT is nicely balancing sensitivity and specificity and performs much better overall. 
Nevertheless, it is still far from perfect ---\ so check out whether you can create even better ones! 

<!-- TESTing: --> 

<!-- 1. In `fftrees_wordstofftrees()`, "Otherwise" part is being ignored. --> 
<!-- 2. In `fftrees_ffttowords()`, the final sentence always predicts positive (True) instances first. --> 

```{r my-tree-checks-1, echo = FALSE, eval = FALSE}
# 1. FFT with 2 cues (final cue is categorical): ------ 

fft_1 <- FFTrees(diagnosis ~.,
                 data = heartdisease, 
                 my.tree = "If age < 45, predict False. 
                               If thal = {fd, normal}, predict True.
                               Otherwise, predict the opposite.", 
                 main = "My test 1")

# inspect:
fft_1$trees$definitions
plot(fft_1)  # Note flipped direction for cue 1: exit = 0.
fft_1
inwords(fft_1)

# Note 1: 
# Corrected an error in the direction definition of the final node: 
# When 1st part of last sentence predicts TRUE, 
# the final direction must not be negated.


# 2. Reverse cue order (i.e, final cue is numeric), but set to True (by reversing cue direction): ------ 

fft_2 <- FFTrees(diagnosis ~.,
                 data = heartdisease, 
                 my.tree = "If thal = {fd, normal}, predict true!
                               If age >= 45, predict TRUE (again)! 
                               Otherwise, go crazy (but mention 'FALSE' somewhere)...",
                 main = "My test 2")
# inspect:
fft_2$trees$definitions
plot(fft_2)
fft_2
inwords(fft_2)

# Notes:
# - The final sentence always predicts positive (True) instances first. 
#   When the description predicted False instances first, the cue direction WAS reversed. 
# - Note improved robustness against lower/uppercase spellings/typos in cue labels. 

# Comparing my_fft_1 and my_fft_2 shows that cue order matters:
# In my_fft_1, the initial age cue causes few misses, but the 2nd thal cue makes more error than correct cases.
# In my_fft_2, the initial thal cue causes many false alarms, and the 2nd age cue also is too liberal (on FA).


# 3. Example used by Nathaniel (and skipping "otherwise" part of final node): ------ 

fft_3 <- FFTrees(formula = diagnosis ~., 
                 data = heart.train,
                 data.test = heart.test, 
                 decision.labels = c("Healthy", "Disease"),
                 my.tree = "If sex = 1, predict Disease.
                            If age < 45, predict Healthy.
                            If thal = {fd, normal}, predict Disease. 
                            (etc.)",
                 main = "My test 3")

fft_3$trees$definitions
plot(fft_3)
inwords(fft_3)
fft_3


# 4. The shortest possible my.tree: ------ 

fft_4 <- FFTrees(formula = survived ~., 
                 data = titanic, 
                 my.tree = "If sex = {female} true.",  # ignore otherwise/else part
                 main = "My test 4") 

plot(fft_4, n.per.icon = 50, what = "all", show.iconguide = TRUE)

# Note:
# - Warning that 'False' does not occur in specification, but FFT is valid and constructed.
# - fft_4 shows good specificity (i.e., few false alarms, relative to high number of correct rejections), 
#   but poor sensitivity (many misses).
#   Overall accuracy is 10% above baseline (predicting False for all cases).
```

<!-- ToDo: Illustrate 4., as case is instructive. -->

<!-- ToDo: 2nd way to specify an FFT:  -->
<!-- 2. as a data frame using the `tree.definitions` argument  -->


### 2. Using `tree.definitions`

More experienced users may want to define and evaluate more than one FFTs at a time. 
To achieve this, the `FFTrees()` function allows providing sets of `tree.definitions` (as a data frame). 
However, as questions regarding specific trees usually arise late in an exploration of FFTs, the `tree.definitions` argument is mostly used in combination with an existing `FFTrees` object\ `x`. 
In this case, the parameters (e.g., regarding the `formula`, `data` and goals to be used) from\ `x` are being used, but its tree definitions (stored in `x$trees$definitions`) are replaced by those in `tree.definitions` and the object is re-evaluated for those FFTs.


#### Example

We illustrate a typical workflow by redefining some FFTs that were built in the [Tutorial: FFTs for heart disease](FFTrees_heart.html) and evaluating them on the (full) `heartdisease` data. 

First, we use our default algorithms to create an `FFTrees` object `heart.fft`:

```{r fft-treedef-01, message = FALSE}
# Create an FFTrees object x:
x <- FFTrees(formula = diagnosis ~ .,           # criterion and (all) predictors
             data = heart.train,                # training data
             data.test = heart.test,            # testing data
             main = "Heart Disease 1",          # initial label
             decision.labels = c("low risk", "high risk"),  # exit labels
             quiet = TRUE)                      # hide user feedback
```

As we have seen in the [Tutorial](FFTrees_heart.html), evaluating this expression yields a set of `r x$trees$n`\ FFTs. 
Rather than evaluating them individually (by issuing `print(x)` or `plot(x)` commands to inspect specific trees), we can obtain both their definitions and their performance characteristics on a variety of measures either by running `summary(x)` or by inspecting corresponding parts of the `FFTrees` object. 
For instance, the following alternatives would both show the current definitions of the generated FFTs: 

```{r fft-treedef-02, message = FALSE}
# Tree definitions of x:
# summary(x)$definitions   # from summary()
x$trees$definitions        # from FFTrees object x
```

Each line in these tree definitions defines an FFT in the context of our current `FFTrees` object\ `x` (see the vignette on [Creating FFTs with FFTrees()](FFTrees_function.html) for help on interpreting tree definitions). 
As the "ifan" algorithm responsible for creating these trees yields a family of highly similar FFTs (as the FFTs vary only by their exits, and some truncate the last cue), we may want to examine alternative versions for these trees. 


#### Modifying tree definitions 

To demonstrate how to create and evaluate manual FFT definitions, we copy the existing tree definitions (as a data frame), select three FFTs (rows), and then create a 4th definition (with a different exit structure):

```{r fft-treedef-03, message = FALSE}
# 0. Copy and choose some existing FFT definitions:
tree_df <- x$trees$definitions    # get FFT definitions (as df)
tree_df <- tree_df[c(1, 3, 5), ]  # filter 3 particular FFTs

# 1. Add a tree with 1;1;0.5 exit structure (a "rake" tree with Signal bias):
tree_df[4, ] <- tree_df[1, ]        # initialize new FFT #4 (as copy of FFT #1)
tree_df$exits[4] <- c("1; 1; 0.5")  # modify exits of FFT #4

tree_df$tree <- 1:nrow(tree_df)   # adjust tree numbers
# tree_df
```

Moreover, let's define four additional FFTs that reverse the order of the 1st and 2nd cues. 
As both cues are categorical (i.e., of class\ `c`) and have the same direction (i.e., `=`), we only need to reverse the `thresholds` (so that they correspond to the new cue order):

```{r fft-treedef-04, message = FALSE}
# 2. Change cue orders:
tree_df[5:8, ] <- tree_df[1:4, ]     # 4 new FFTs (as copiess of existing ones)
tree_df$cues[5:8] <- "cp; thal; ca"       # modify order of cues
tree_df$thresholds[5:8] <- "a; rd,fd; 0"  # modify order of thresholds accordingly

tree_df$tree <- 1:nrow(tree_df)           # adjust tree numbers
# tree_df
```

The resulting data frame `tree_df` contains the definitions of eight FFTs. 
The first three are copies of trees in\ `x`, but the other five are new. 


#### Evaluating `tree.definitions` 

We can evaluate this set by running the `FFTrees()` function with 
the previous `FFTrees` object\ `x` (i.e., with its `formula` and `data` settings) and 
specifying `tree_df` in the `tree.definitions` argument:

```{r fft-treedef-05, message = TRUE, results = 'hide'}
# Create a modified FFTrees object y:
y <- FFTrees(object = x,                  # use previous FFTrees object x
             tree.definitions = tree_df,  # but with new tree definitions
             main = "Heart Disease 2"     # revised label
)
```

The resulting `FFTrees` object\ `y` contains the decisions and summary statistics of all eight FFTs for the data specified in\ `x`. 
Although it is unlikely that one of the newly created trees beats the automatically created FFTs, we find that reversing the order of the first cues has only minimal effects on training accuracy (as measured by `bacc`):

```{r fft-treedef-06, message = TRUE}
y$trees$definitions  # tree definitions
y$trees$stats$train  # training statistics
```

Note that the trees in\ `y` were sorted by their performance on the current `goal` (here `bacc`). 
For instance, the new rake tree with cue order `cp; thal; ca` and exits `1; 1; 0.5` is now FFT\ #6. 
When examining its performance on `"test"` data (i.e., for prediction):

```{r fft-treedef-07, eval = FALSE}
# Print and plot FFT #6:
print(y, tree = 6, data = "test")
plot(y,  tree = 6, data = "test")
```

we see that it has a balanced accuracy\ `bacc` of\ 70%. 
More precisely, its bias for predicting `disease` (i.e., signal or True) yields near-perfect sensitivity (96%), but very poor specificity (44%). 

If we wanted to change more aspects of\ `x` (e.g., use different `data` or `goal` settings), we could have created a new `FFTrees` object without supplying the previous object\ `x`, as long as the FFTs defined in `tree.definitions` fit to the settings of `formula` and `data`. 

<!-- +++ here now +++  -->

<!-- For experienced users: Method for manually or computationally defining sets of FFTs. -->
<!-- Start from existing definitions of FFTs (in an `FFTrees` object). -->

<!-- A. tree.definitions and object: -->

```{r design-fft-df, echo = FALSE, eval = FALSE}
# Modify and use an existing FFT:

# Create original FFTs:
fft_0 <- FFTrees(formula = diagnosis ~ .,           # Criterion and (all) predictors
                 data = heart.train,                # Training data
                 data.test = heart.test,            # Testing data
                 main = "Heart Disease (org)",      # General label
                 decision.labels = c("LOW Risk", "HIGH Rrisk"), # Labels for decisions
                 quiet = FALSE  # enable/suppress user feedback
                 )

# Copy object:
fft_1 <- fft_0  # to keep original safe

plot(fft_1, tree = 1)

# Current FFTs:
fft_1$trees$definitions

# (1) Modify an existing FFT:

# Start from an existing FFT: 
my_fft <- fft_1$trees$definitions[1, ]  # take a row of df
my_fft

# Make some changes: 
# Swap nodes 1 and 2 of Tree 1 (and add leading/trailing spaces):
my_fft$cues <- " cp ; thal ; ca "
my_fft$thresholds <- " a ; rd,fd ; 0 "  # swap according to cues
my_fft$exits <- "0 ; 1 ; 0.5 "         # swap according to cues

my_fft$tree <- 8  # signal new tree (with new number)

# Add my_fft to FFTrees object:

# Add definitions of 8th tree:
# fft_1$trees$definitions[8, ] <- my_fft

# # OR (combine tree definitions as rows of data frames):
my_fft_df <- rbind(fft_1$trees$definitions, my_fft)
my_fft_df

# (2) Manual replacement: ---- 

# Replace definitions in FFTrees object fft_1:
fft_1$trees$definitions <- my_fft_df
fft_1$trees$n <- as.integer(nrow(my_fft_df))

# HAS fft_1 been changed?
fft_1$trees$n
fft_1$trees$definitions  # APPEARS to have been swapped (8 trees)
fft_1$trees$definitions[8, ]

# Apply changed object fft_1 to data:
fft_2 <- fftrees_apply(fft_1, mydata = "test", newdata = heart.test)  # WORKS with new/8-th tree...

fft_2$trees$definitions  # 8 tree definitions, but:
fft_2$trees$stats$train  # "train" parts still only contain 7 trees, 
fft_2$trees$stats$test   # but "test" parts contain stats for 8 trees

# Note: Curious fact:
fft_2$trees$best$test  # NEW tree has the best bacc value (for "test" data)!

plot(fft_2, data = "test", tree = 8)


# (3) Automatic replacement (using FFTrees()): ---- 

# Cover 3 cases: 
# A. Provide tree.definitions without an `FFTrees` object
# B. Provide an `FFTrees` object, but no tree.definitions
# C. Provide both an object and tree.definitions

# C. Provide both an existing FFTrees object and tree.definitions: 
fft_3 <- FFTrees(#formula = diagnosis ~ .,            # as before
                 object = fft_1,                     # some valid FFTrees object 
                 tree.definitions = my_fft_df,       # new tree definitions (as df)
                 #data = heart.train,                 # training data
                 #data.test = heart.test,             # testing data
                 main = "Heart Disease (manual 1)",  # changed label
                 decision.labels = c("low risk", "high risk")  # changed labels for decisions
)

# => tree.definitions are being used, tree definitions of object are ignored!
# BUT: If formula and data is not specified, those of object are being used. 

plot(fft_3)

fft_3$trees$definitions  # 8 trees: New FFT is now #4

fft_3$trees$best  # FFT #4 is best test tree!

plot(fft_3, data = "train", tree = "best.train")  # FFT #1
plot(fft_3, data = "test", tree = "best.test")    # FFT #4

print(fft_3, data = "test", tree = "best.test")

# Use new FFT object to predict same / new data:
predict(fft_3, newdata = heartdisease, tree = 4)  # WORKS, qed. 
```

<!-- A. Provide tree.definitions but no object: -->

```{r design-fft-df-2, echo = FALSE, eval = FALSE}
# Provide tree.definitions but no object:
fft_4 <- FFTrees(formula = diagnosis ~ .,            # as before
                 object = NULL,                      # NO FFTrees object
                 tree.definitions = my_fft_df,       # new tree definitions (as df)
                 data = heart.train,                 # training data
                 data.test = heart.test,             # testing data
                 main = "Heart Disease (manual 2)",  # changed label
                 decision.labels = c("low R", "high R")  # changed labels for decisions
)
fft_4

fft_4$trees$definitions  # 8 trees: New FFT is #4

fft_4$trees$best  # FFT #4 is best test tree!

plot(fft_4, data = "train", tree = "best.train")  # FFT #1
plot(fft_4, data = "test", tree = "best.test")    # FFT #4

print(fft_4, data = "test", tree = "best.test")

```

<!-- B. Provide an object but no tree.definitions: -->

```{r design-fft-df-3, echo = FALSE, eval = FALSE}
# Provide an object, but no tree.definitions:
fft_5 <- FFTrees(formula = diagnosis ~ .,            # as before
                 object = fft_3,                     # an existing FFTrees object
                 tree.definitions = NULL,            # NO tree definitions (as df)
                 data = heart.train,                 # training data
                 data.test = heart.test,             # testing data
                 main = "Heart Disease (manual 2)",  # changed label
                 decision.labels = c("low R", "high R")  # changed labels for decisions
)

fft_5

fft_5$trees$definitions  # 8 trees: New FFT is #4

fft_5$trees$best  # FFT #4 is best 'test' tree!

plot(fft_5, data = "train", tree = "best.train")  # FFT #1
plot(fft_5, data = "test", tree = "best.test")    # FFT #4

print(fft_5, data = "test", tree = "best.test")
```


<!-- Demo: Create all permutations of cues (i.e., orders of a given tree): -->

```{r manual-tree-defs, echo = FALSE, eval = FALSE}
# Create FFTs by algorithm:
fft_0 <- FFTrees(formula = diagnosis ~ .,           # criterion and (all) predictors
                 data = heart.train,                # training data
                 data.test = heart.test,            # testing data
                 main = "Heart disease (auto)",     # some label
                 decision.labels = c("low risk", "high risk"), # labels for decisions
                 quiet = FALSE  # enable/suppress user feedback
                 )

print(fft_0, tree = "best.train", data = "train")
plot(fft_0, tree = "best.train", data = "train")
plot(fft_0, tree = "best.test", data = "test")

# Inspect trees:
fft_0$trees$definitions
fft_0$trees$best

# FFT #1:
fft_0$trees$definitions[1, ]

# Re-create FFT #1:
my_fft_df <- data.frame(tree = c(1),
                        nodes = c(3),
                        classes = c("c; c; n"),
                        cues = c("thal; cp; ca"),
                        directions = c("=; =; >"),
                        thresholds = c("rd,fd; a; 0"),
                        exits = c("1; 0; 0.5"),
                        stringsAsFactors = FALSE
)



# Re-evaluate FFT on same data:
fft_1 <- FFTrees(formula = diagnosis ~ .,           # Criterion and (all) predictors
                 data = heart.train,                # Training data
                 data.test = heart.test,            # Testing data
                 tree.definitions = my_fft_df,      # provide definition (as df)
                 main = "Heart Disease (manual)"   # new label
                 )
fft_1

print(fft_1, tree = "best.train", data = "train")
plot(fft_1, tree = "best.train", data = "train")
plot(fft_1, tree = "best.test", data = "test")

# Inspect trees:
fft_1$trees$definitions
fft_1$trees$best


# Re-create FFT #1 and permutations of cue orders:
my_fft_df_2 <- data.frame(tree = c(1, 2, 3),
                          nodes = c(3, 3, 3), 
                          classes = c("c; c; n", "c; n; c", "n; c; c"),
                          cues = c("thal; cp; ca", "thal; ca; cp", "ca; thal; cp"),
                          directions = c("=; =; >", "=; >; =", ">; =; ="),
                          thresholds = c("rd,fd; a; 0", "rd,fd; 0; a", "0; rd,fd; a"),
                          exits = c("1; 0; 0.5", "1; 1; 0.5", "0; 1; 0.5"),
                          stringsAsFactors = FALSE
)
my_fft_df_2

# Re-evaluate FFTs on same data:
fft_2 <- FFTrees(formula = diagnosis ~ .,           # Criterion and (all) predictors
                 data = heart.train,                # Training data
                 data.test = heart.test,            # Testing data
                 tree.definitions = my_fft_df_2,    # provide definitions (as df)
                 main = "Heart Disease (manual)"   # new label
                 )
fft_2

# Inspect trees:
fft_2$trees$definitions  # Note: FFTs #2 and #3 swapped positions!
fft_2$trees$best

fft_2$trees$stats

plot(fft_2, tree = 2, data = "train")
plot(fft_2, tree = 2, data = "test")

plot(fft_2, tree = 3, data = "train")
plot(fft_2, tree = 3, data = "test")
```


## Vignettes

<!-- Table of all vignettes: -->

Here is a complete list of the vignettes available in the **FFTrees** package: 

|   | Vignette | Description |
|--:|:------------------------------|:-------------------------------------------------|
|   | [Main guide: FFTrees overview](guide.html) | An overview of the **FFTrees** package |
| 1 | [Tutorial: FFTs for heart disease](FFTrees_heart.html)   | An example of using `FFTrees()` to model heart disease diagnosis |
| 2 | [Accuracy statistics](FFTrees_accuracy_statistics.html) | Definitions of accuracy statistics used throughout the package |
| 3 | [Creating FFTs with FFTrees()](FFTrees_function.html) | Details on the main `FFTrees()` function |
| 4 | [Manually specifying FFTs](FFTrees_mytree.html)   | How to directly create FFTs without using the built-in algorithms |
| 5 | [Visualizing FFTs](FFTrees_plot.html) | Plotting `FFTrees` objects, from full trees to icon arrays |
| 6 | [Examples of FFTs](FFTrees_examples.html) | Examples of FFTs from different datasets contained in the package |


<!-- eof. -->
