As a starting point, we can use datree to solve Kim's party problem, from the Decision Analysis courses at Stanford or the Foundations of Decision Analysis textbook by Howard and Abbas.

library(datree)

# Create outcomes
outdoor_sun <- o_node(~ v_os, "Outdoor-Sun")
porch_sun <- o_node(~ v_ps, "Porch-Sun")
indoor_rain <- o_node(~ v_ir, "Indoor-Rain")
indoor_sun <- o_node(~ v_is, "Indoor-Sun")
porch_rain <- o_node(~ v_pr, "Porch-Rain")
outdoor_rain <- o_node(~ v_or, "Outdoor-Rain")

# Uncertainty nodes at each alternative
outdoor <- u_node(list(outdoor_sun, outdoor_rain),
                  probs = c(~ p_sun, ~ 1- p_sun),
                  name = "Outdoor")
porch <- u_node(list(porch_sun, porch_rain),
                  probs = c(~ p_sun, ~ 1- p_sun),
                  name = "Porch")
indoor <- u_node(list(indoor_sun, indoor_rain),
                  probs = c(~ p_sun, ~ 1- p_sun),
                  name = "Indoor")

# Decision of party location
location <- d_node(list(outdoor, porch, indoor),
                   "Location")

The nodes of the tree are defined with abstract values --- expressed with formula (starting with ~). Actual variable values must be defined within the calling environment:

# Load initial values
v_os <- 1
v_ps <- .95
v_ir <- .67
v_is <- .57
v_pr <- .32
v_or <- 0
p_sun <- .4

Once the variables are defined, you can compute the concrete value of any node for the given parameters, using evaluate().

evaluate(outdoor)
evaluate(indoor)
evaluate(porch)

evaluate(location)

Note that for sensitivity analysis, you can also provide a list-like data type (e.g., data.frame) that replaces the global environment values.

evaluate(outdoor, list(p_sun = .5))

Also, for now, it's up to you to keep track and make sure the probabilities add to one!

# This makes no sense, but still works ... (unfortunately)
evaluate(outdoor, list(p_sun = 500))

Use decide() to explore the different alternatives of a decision node.

decide(location)

Like evaluate(), you can also specify parameters for decide().

decide(location, list(p_sun = .8))

This setup works well for sensitivity analysis.

library(tidyverse)
map_df(c(0, .5, 1), ~ cbind(
  decide(location, list(p_sun = .x)), p_sun = .x)) %>% 
  ggplot(aes(x = p_sun, y = evalue)) +
  geom_line(aes(color = alternative))

Alternatively, the tree can be built as recursive calls to node functions, which in some situations might be more readable, especially if you don't need/want to keep track of intermediate nodes.

# Decision of party location
location <- d_node(list( # Alternatives
  # Outdoor
  u_node(list(  # Outcomes for Outdoor
    o_node(~ v_os, "Outdoor-Sun"),
    o_node(~ v_or, "Outdoor-Rain")), 
    probs = c(~ p_sun, ~ 1 - p_sun),  
    name = "Outdoor"),
  # Porch
  u_node(list(  # Outcomes for Porch
    o_node(~ v_ps, "Porch-Sun"),
    o_node(~ v_pr, "Porch-Rain")), 
    probs = c(~ p_sun, ~ 1 - p_sun),  
    name = "Porch"),
  # Indoor
  u_node(list(  # Outcomes for Indoor
    o_node(~ v_is, "Indoor-Sun"),
    o_node(~ v_ir, "Indoor-Rain")), 
    probs = c(~ p_sun, ~ 1 - p_sun),  
    name = "Indoor")
  ), "Location")

decide(location)


jongbinjung/datree documentation built on May 19, 2019, 7:30 p.m.