This is a library to create decision trees and calculate the expected value of the best strategy.
To install the library, run the following command:
pip install precision-treeThe library has two main modules: nodes and tree.
The nodes module contains the classes to create the nodes of the decision tree. The tree module contains the class to wrap the root node and calculate the expected value of the best strategy and visualize the tree.
The nodes module contains the following classes:
-
DecisionNode: Represents a decision node. -
ChanceNode: Represents a chance node. -
PayoffNode: Represents a payoff node.
All of the classes are subclasses of the Node class.
The DecisionNode class represents a decision node (in graph it looks like a squere).
The following table shows the attributes of the DecisionNode class:
| Attribute | Description |
|---|---|
name |
The name of the node. |
branches |
A list of branches. |
The following table shows the methods of the DecisionNode class:
| Method | Description |
|---|---|
add_branch(self, label, child) |
Adds a branch to the node. label is the name of the branch and child is the child node. |
calculate_value |
Calculates the expected value of the best strategy. |
The ChanceNode class represents a chance node (in graph it looks like a circle).
The following table shows the attributes of the ChanceNode class:
| Attribute | Description |
|---|---|
name |
The name of the node. |
cost |
The cost of the node. |
years |
The number of years. |
branches |
A list of branches. |
The following table shows the methods of the ChanceNode class:
| Method | Description |
|---|---|
add_branch(self, label, child, probability) |
Adds a branch to the node. label is the name of the branch, child is the child node, and probability is the probability of the branch. |
calculate_value |
Calculates the expected value of the best strategy. |
The PayoffNode class represents a payoff node (in graph it looks like a triangle).
The following table shows the attributes of the PayoffNode class:
| Attribute | Description |
|---|---|
name |
The name of the node. |
value |
The value of the node. |
branches |
A list of branches. |
The following table shows the methods of the PayoffNode class:
| Method | Description |
|---|---|
calculate_value |
Calculates the expected value of the best strategy. |
The tree module contains the TreeWrapper class.
The following table shows the attributes of the TreeWrapper class:
| Attribute | Description |
|---|---|
root |
The root node of the tree. |
The following table shows the methods of the TreeWrapper class:
| Method | Description |
|---|---|
show(self, title: str, format: str): |
Shows the tree. title is the title of the tree and format is the format of the file png, pdf etc. |
get_optimal_path |
Returns the optimal path as a tuple, where the first element is the optimal nodes and second element is the optimal edges. |
calculate_value |
Calculates the expected value of the best strategy. |
Example
from precision_tree.nodes import DecisionNode, ChanceNode, PayoffNode
from precision_tree.tree import TreeWrapper
root = DecisionNode("Decision")
# Decision Node Without Analytics
without_analytics = DecisionNode("Without Analytics")
big_factory = ChanceNode("Big Factory 1", cost=0, years=1)
big_factory.add_branch(label="Favorable P(0.7)", child=PayoffNode("Favorable 1", 200), probability=0.7)
big_factory.add_branch(label="Unfavorable P(0.3)", child=PayoffNode("Unfavorable 1", -180), probability=0.3)
small_factory = ChanceNode("Small Factory 1", cost=0, years=1)
small_factory.add_branch(label="Favorable P(0.7)", child=PayoffNode("Favorable 2", 100), probability=0.7)
small_factory.add_branch(label="Unfavorable P(0.3)", child=PayoffNode("Unfavorable 2", -20), probability=0.3)
without_analytics.add_branch(label="Big Factory", child=big_factory)
without_analytics.add_branch(label="Small Factory", child=small_factory)
without_analytics.add_branch(label="Sell Patent", child=PayoffNode("Sell Patent 1", 10))
# Make Decision Node With Analytics
with_analytics = ChanceNode("With Analytics", cost=8, years=1)
# Positive Result
positive_result = DecisionNode("Positive Result")
big_factory_positive = ChanceNode("Big Factory 2", cost=0, years=1)
big_factory_positive.add_branch(label="Favorable", child=PayoffNode("Favorable 3", 200), probability=0.81)
big_factory_positive.add_branch(label="Unfavorable", child=PayoffNode("Unfavorable 3", -180), probability=0.19)
small_factory_positive = ChanceNode("Small Factory 2", cost=0, years=1)
small_factory_positive.add_branch(label="Favorable", child=PayoffNode("Favorable 4", 100), probability=0.81)
small_factory_positive.add_branch(label="Unfavorable", child=PayoffNode("Unfavorable 4", -20), probability=0.19)
positive_result.add_branch(label="Big Factory", child=big_factory_positive)
positive_result.add_branch(label="Small Factory", child=small_factory_positive)
positive_result.add_branch(label="Sell Patent", child=PayoffNode("Sell Patent 2", 10))
# Negative Result
negative_result = DecisionNode("Negative Result")
big_factory_negative = ChanceNode("Big Factory 3", cost=0, years=1)
big_factory_negative.add_branch(label="Favorable", child=PayoffNode("Favorable 5", 200), probability=0.28)
big_factory_negative.add_branch(label="Unfavorable", child=PayoffNode("Unfavorable 5", -180), probability=0.72)
small_factory_negative = ChanceNode("Small Factory 3", cost=0, years=1)
small_factory_negative.add_branch(label="Favorable", child=PayoffNode("Favorable 6", 100), probability=0.28)
small_factory_negative.add_branch(label="Unfavorable", child=PayoffNode("Unfavorable 6", -20), probability=0.72)
negative_result.add_branch(label="Big Factory", child=big_factory_negative)
negative_result.add_branch(label="Small Factory", child=small_factory_negative)
negative_result.add_branch(label="Sell Patent", child=PayoffNode("Sell Patent 3", 10))
with_analytics.add_branch(label="Positive Result", child=positive_result, probability=0.48)
with_analytics.add_branch(label="Negative Result", child=negative_result, probability=0.52)
# Add Branches to Root
root.add_branch(label="With Analytics", child=with_analytics)
root.add_branch(label="Without Analytics", child=without_analytics)
tree = TreeWrapper(root)
print(f"Expected Value of the best strategy: {tree.calculate_value():.2f}")
print(f"Optimal branch: {tree.get_optimal_path()[0]}")Output
Expected Value of the best strategy: 86.00
Optimal branch: {'Unfavorable 1', 'Favorable 1', 'Big Factory 1', 'Without Analytics', 'Decision'}