Overriding default functionality
Rationale
Overriding, or making exceptions, is an inherent inevitability in many data analysis applications. For example, when analyzing many similar data items, we can often devise a processing scheme that works well on most of the data items, yet requires some adjustments for outlier items (say, we may have many images that are each normalized according to some default normalization scheme, but we need to make an exception for few of these images that were taken in different lighting conditions). Quibbler allows overriding such default functional behavior in a simple, interactive, yet also transparent and well-documented fashion. Such exceptions are made by overriding assignments.
Consider the following simple, where we define a default value to be used for some presumed downstream processing of some n data items:
# Imports
import pyquibbler as qb
from pyquibbler import iquib
qb.initialize_quibbler()
import numpy as np
import matplotlib.pyplot as plt
%matplotlib tk
# Number of data items:
n = iquib(5)
# Define default factor:
default_factor = iquib(np.array([7.]))
# Define a per-item factor by replicating the default factor:
per_item_factor = np.tile(default_factor, n)
per_item_factor.get_value()
array([7., 7., 7., 7., 7.])
The per_item_factor
is a function quib, providing a “default” value
to be used for n
data items. In this simple example, this per-item
default is simply a result of applying the tile
function to the
underlying i-quib default_factor
. In general, though, such per-item
decisions could be a result of other, more complex, functionalities.
Yet, as sophisticated as our automatic choice could get, we may still
sometime need to make exceptions, overriding the default functional
behavior. To allow such overriding of the defualt funational values of a
given quib, we first need to turn on its allow_overriding
property:
per_item_factor.allow_overriding = True
Now, say we want to override the per_item_factor
, for instance
substituting 9 in position 1, we can simply use standard assignment
syntax:
per_item_factor[1] = 9
Overriding choices:
(1) per_item_factor
(2) default_factor
Choose the number of the quib to override
(press enter without a choice to cancel): 1
Overriding: per_item_factor
There are two options for actualizing this assignment. First, as we
have seen in the chapter on Assignments, the assignment can
inverse-propagate to an upstream i-quib, in our case to be actualized as
a chnage to the default_factor
. Second, since we have now set
per_item_factor.allow_overriding = True
there is also an option to
actualize the assignment as an override to the per_item_factor
. We
can programatically specifiy which of these two options to choose by
setting the assigned_quibs
property (expalined below). But, here, we
did not specify a choice and Quibbler thereby prompt us, asking to
choose at which of these two levels to actualize the assignment.
Choosing to actualize at the per_item_factor
(choosing “1” in the
menu above) will cause an overriding assignment to this function quib:
per_item_factor.get_value()
array([7., 9., 7., 7., 7.])
As we see, the quib has been overridden to have a value of 9 at position
1. All other values remain functional: they are the result of the
function tile
applied to default_factor
. Changing
default_factor
will change the downstream per_item_factor
in all
but the overridden positions:
default_factor[0] = 8
per_item_factor.get_value()
array([8., 9., 8., 8., 8.])
The choice we made in the dialog box is recorded in the quib. So further assignments do not require bringing up the dialog box again:
per_item_factor[3] = 7
per_item_factor.get_value()
array([8., 9., 8., 7., 8.])
Assignments are actualized as a list of overrides to a quib’s ‘default’ value
When we make overriding assignments to a quib, these assignments are actualized as a list of overrides that apply to the quib’s ‘default’ value (the functional value).
This override list is accessible through the get_override_list()
method:
per_item_factor.get_override_list()
quib[1] = 9
quib[3] = 7
In addition, we can check which element positions are overridden, using
the get_override_mask()
method:
per_item_factor.get_override_mask().get_value()
array([False, True, False, True, False])
Graphics-driven overriding assignments
Overriding can also be used together with graphics-driven assignments, easily yielding interactive GUIs for default-overriding parameter specification.
# Figure setup
plt.axis([-0.5, n - 0.5, 0, 10])
plt.xticks(np.arange(n))
# Plot the default_factor as an horizontal dragganle line
plt.plot([-0.5, n - 0.5], default_factor[[0, 0]], 'k',
linewidth=5, picker=True);
# Plot the per_item_factor as an bars and as draggable markers
x = np.arange(n)
plt.bar(x, per_item_factor, color=(0.7, 0.7, 0.7))
plt.plot(x, per_item_factor, 's', picker=True);
For more complex, two-level overriding, see Overriding default values (two-levels).
Clearing assignments by assigning the Default value
Overriding assignments to a quib can be cleared, thereby restoring the
default functionality. Clearing overriding assignments can be done
graphically, simply by right-clicking a graphic quib (see in the video
above). Alternatively, overriding can be removed programatically using
an assigning syntax where the assigned value is the Quibbler
default
value:
per_item_factor[1:4] = [101, 102, 103]
per_item_factor.get_value()
array([ 8., 101., 102., 103., 8.])
per_item_factor[1:3] = qb.default
per_item_factor.get_value()
array([ 8., 8., 8., 103., 8.])
All assignments to a quib can be cleared using:
per_item_factor.assign(qb.default)
per_item_factor.get_value()
array([8., 8., 8., 8., 8.])
Out-of-range overriding are ignored
When we try to assign out of range, we get an exception. For example,
per_item_factor[10] = 3
# yields: IndexError: index 10 is out of bounds for axis 0 with size 5
However, it is also possible that an originally within-range assignment will become out-of-range. For example:
per_item_factor[4] = 3
per_item_factor.get_value()
array([8., 8., 8., 8., 3.])
This assignment will become out-of-range if we now change n
. In such
a case, Quibbler gives a warning and otherwise ignores the
out-of-range assignment:
n.assign(4)
per_item_factor.get_value()
array([8., 8., 8., 8.])
Overriding is controlled by the ‘allow_overriding’ and ‘assigned_quibs’ properties
When a de novo assignment is being made to a specific quib (the “assigned quib”) the assignment can be actualized as overriding of this focal quib, or can inverse-propagate upstream and actualized as overrides of some higher up quibs (“inverse-assigned quibs”). The choice of which quibs should be chosen for actualizing the overriding assignment is controlled by the following two quib properties:
allow_overriding. A boolean property specifying for each quib whether it accepts overriding. By default, i-quibs accept overriding and f-quibs do not. In order to allow overriding of a specific f-quib, we need to explicitly set its
allow_overriding
toTrue
.assigned_quibs. Indicates a set of possible upstream quibs into which an assignment to the current quib should be inverse-propagated and actualized:
None
(default): If there is only one upstream quib with allow_overriding=True, inverse-assign to it. If multiple options exist, bring up a dialog box to ask the user which quib to inverse-assign to.{}
: Do not allow de novo assignments to this quib.{quibs}
: Set of upstream quibs into which to actualize de novo assignments made to the current quib. If multiple options exist, bring up a dialog box.
In the default settings, where assigned_quibs=None
and
allow_overriding=True
only for i-quibs, any de novo assignment to
an f-quib is inverse-propagated all the way to the respective upstream
i-quibs, where it is ultimately actualized.
Though, when overriding of specific intermediate f-quibs is enabled
(allow_overriding=True
), multiple options for actualizing a de
novo assignment to a downstream quib may be available. The choice among
these options is determined by the assigned_quibs
property of the
quib to which the de novo assignment was made.
The following diagram depicts such inverse assignment behaviors: