Part predictions and unfitting data

What if we want only part of the predictions, not the end-nodes?

See Tutorial 1

[38]:
from nbnode.nbnode import NBNode
import nbnode.nbnode_trees as nbtree
simple_tree = nbtree.tree_simple()
simple_tree.pretty_print("__long__")
a (counter:0, decision_name:None, decision_value:None)
├── a0 (counter:0, decision_name:m1, decision_value:-1)
├── a1 (counter:0, decision_name:m1, decision_value:1)
│   └── a1a (counter:0, decision_name:m2, decision_value:test)
└── a2 (counter:0, decision_name:m3, decision_value:another)

The following dictionary would usually have raised a ValueError after it does not reach an endnode:

[39]:
try:
    simple_tree.predict({"m1":1, "m2":0, "m3":0})
except ValueError:
    print("ValueError: Could not find a fitting endnode for the data you gave. You also did not allow for part predictions.")
ValueError: Could not find a fitting endnode for the data you gave. You also did not allow for part predictions.

However, allowing for partial predictions enables a more flexible approach to the problem. The previous example actually DOES identify a node (a1), however it does not find an endnode. We can enable this by setting allow_part_predictions argument to True:

[40]:
simple_tree.predict({"m1":1, "m2":0, "m3":0}, allow_part_predictions=True)
[40]:
NBNode('/a/a1', counter=0, decision_name='m1', decision_value=1)

Note that this enables more complex results than just a single NBnode. In the following example, the data finds a matching part and endnode!

[41]:
simple_tree.predict({"m1":1, "m2":0, "m3":"another"}, allow_part_predictions=True)
[41]:
[NBNode('/a/a1', counter=0, decision_name='m1', decision_value=1),
 NBNode('/a/a2', counter=0, decision_name='m3', decision_value='another')]

The following prediction fails because:

1.1 Check if m1=-1 (no)
2.1 Check if m1=1 (yes)
2.2 Check if m2='test' (no), no endnode!
  raise exception because in this path no proper endnode was able to be
  found with the given values
3.1 Check if m3='another' (yes) -> return this node
[42]:
try:
    simple_tree.predict(values={"m1": 1, "m2": -1, "m3": "another"})
except ValueError:
    print("ValueError: Could not find a fitting endnode for the data you gave. You also did not allow for part predictions.")
ValueError: Could not find a fitting endnode for the data you gave. You also did not allow for part predictions.

With allow_unfitting_data=True, the previously ValueError is not called and a proper endnode returned!

1.1 Check if m1=-1 (no)
2.1 Check if m1=1 (yes)
2.2 Check if m2='test' (no), no endnode!
  raise exception because in this path no proper endnode was able to be
  found with the given values
3.1 Check if m3='another' (yes) -> return this node
[43]:
simple_tree.predict(
    values={"m1": 1, "m2": -1, "m3": "another"}, allow_unfitting_data=True
)

[43]:
NBNode('/a/a2', counter=0, decision_name='m3', decision_value='another')