Skip to content

NodeModel

Prediction models attached to tree nodes.

A NodeModel encapsulates how a node turns input features into a prediction (e.g. a constant, a one dimensional linear model, or a full linear model).

NodeModel

Bases: ABC

Abstract base class for all node prediction models.

Source code in imote/nodes/node_model.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class NodeModel(ABC):
    """Abstract base class for all node prediction models."""

    @abstractmethod
    def get_label(self) -> str:
        """Returns a short display label describing the model.

        Returns:
            String of label
        """
        pass

    def to_dict(self) -> dict:
        """Serializes the model to a dictionary.

        Subclasses should call this and extend the result with their
        own parameters.

        Returns:
            Dictionary containing at least the model's class name
            under "class_type".
        """
        return {"class_type": self.__class__.__name__}

    @classmethod
    @abstractmethod
    def from_dict(cls, dic: dict) -> "NodeModel":
        """Reconstructs a model instance from a dictionary.

        Args:
            dic: Dictionary of NodeModel previously produced by to_dict().

        Returns:
            A new instance of cls with parameters restored.
        """
        pass

    @abstractmethod
    def predict(self, X):
        """Predicts the target value(s) for the given input.

        Args:
            X: MatriX of feature values.

        Returns:
            Predicted value(s).
        """
        pass

get_label() -> str abstractmethod

Returns a short display label describing the model.

Returns:

Type Description
str

String of label

Source code in imote/nodes/node_model.py
14
15
16
17
18
19
20
21
@abstractmethod
def get_label(self) -> str:
    """Returns a short display label describing the model.

    Returns:
        String of label
    """
    pass

to_dict() -> dict

Serializes the model to a dictionary.

Subclasses should call this and extend the result with their own parameters.

Returns:

Type Description
dict

Dictionary containing at least the model's class name

dict

under "class_type".

Source code in imote/nodes/node_model.py
23
24
25
26
27
28
29
30
31
32
33
def to_dict(self) -> dict:
    """Serializes the model to a dictionary.

    Subclasses should call this and extend the result with their
    own parameters.

    Returns:
        Dictionary containing at least the model's class name
        under "class_type".
    """
    return {"class_type": self.__class__.__name__}

from_dict(dic: dict) -> NodeModel abstractmethod classmethod

Reconstructs a model instance from a dictionary.

Parameters:

Name Type Description Default
dic dict

Dictionary of NodeModel previously produced by to_dict().

required

Returns:

Type Description
NodeModel

A new instance of cls with parameters restored.

Source code in imote/nodes/node_model.py
35
36
37
38
39
40
41
42
43
44
45
46
@classmethod
@abstractmethod
def from_dict(cls, dic: dict) -> "NodeModel":
    """Reconstructs a model instance from a dictionary.

    Args:
        dic: Dictionary of NodeModel previously produced by to_dict().

    Returns:
        A new instance of cls with parameters restored.
    """
    pass

predict(X) abstractmethod

Predicts the target value(s) for the given input.

Parameters:

Name Type Description Default
X

MatriX of feature values.

required

Returns:

Type Description

Predicted value(s).

Source code in imote/nodes/node_model.py
48
49
50
51
52
53
54
55
56
57
58
@abstractmethod
def predict(self, X):
    """Predicts the target value(s) for the given input.

    Args:
        X: MatriX of feature values.

    Returns:
        Predicted value(s).
    """
    pass

NoneNodeModel

Bases: NodeModel

Identity model used as a placeholder when no model is applied.

Source code in imote/nodes/node_model.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class NoneNodeModel(NodeModel):
    """Identity model used as a placeholder when no model is applied."""

    def get_label(self) -> str:
        """Returns an empty label, since no model is applied.

        Returns:
            Empty string.
        """
        return ""

    @classmethod
    def from_dict(cls, dic: dict) -> "NoneNodeModel":
        """Reconstructs a NoneNodeModel instance.

        Args:
            dic: Unused; present for interface consistency.

        Returns:
            A new NoneNodeModel instance.
        """
        return cls.__new__(cls)

    def predict(self, X):
        """Predicts 0

        Args:
            X: Matrix of feature values.

        Returns:
            List containing 0 for each input row.
        """
        if X.ndim == 1:
            X_shaped = X.reshape(1, -1)
        else:
            X_shaped = X
        return np.zeros(X_shaped.shape[0])

get_label() -> str

Returns an empty label, since no model is applied.

Returns:

Type Description
str

Empty string.

Source code in imote/nodes/node_model.py
78
79
80
81
82
83
84
def get_label(self) -> str:
    """Returns an empty label, since no model is applied.

    Returns:
        Empty string.
    """
    return ""

from_dict(dic: dict) -> NoneNodeModel classmethod

Reconstructs a NoneNodeModel instance.

Parameters:

Name Type Description Default
dic dict

Unused; present for interface consistency.

required

Returns:

Type Description
NoneNodeModel

A new NoneNodeModel instance.

Source code in imote/nodes/node_model.py
86
87
88
89
90
91
92
93
94
95
96
@classmethod
def from_dict(cls, dic: dict) -> "NoneNodeModel":
    """Reconstructs a NoneNodeModel instance.

    Args:
        dic: Unused; present for interface consistency.

    Returns:
        A new NoneNodeModel instance.
    """
    return cls.__new__(cls)

predict(X)

Predicts 0

Parameters:

Name Type Description Default
X

Matrix of feature values.

required

Returns:

Type Description

List containing 0 for each input row.

Source code in imote/nodes/node_model.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def predict(self, X):
    """Predicts 0

    Args:
        X: Matrix of feature values.

    Returns:
        List containing 0 for each input row.
    """
    if X.ndim == 1:
        X_shaped = X.reshape(1, -1)
    else:
        X_shaped = X
    return np.zeros(X_shaped.shape[0])

ConstantNodeModel

Bases: NodeModel

Model that predicts a single constant value.

Attributes:

Name Type Description
value float

The constant value predicted for any input.

Source code in imote/nodes/node_model.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
class ConstantNodeModel(NodeModel):
    """Model that predicts a single constant value.

    Attributes:
        value: The constant value predicted for any input.
    """

    value: float
    def __init__(self, value: float):
        """
        Args:
            value: The constant value to predict.
        """
        self.value = value

    def get_label(self) -> str:
        """Returns the constant value as a display label.

        Returns:
            The value formatted to 3 significant digits.
        """
        return f"{self.value:.3g}"

    def to_dict(self) -> dict:
        """Serializes the model to a dictionary.

        Returns:
            Dictionary with class_type and value.
        """
        dic = super().to_dict()
        dic["value"] = self.value
        return dic

    @classmethod
    def from_dict(cls, dic: dict) -> "ConstantNodeModel":
        """Reconstructs a ConstantNodeModel instance.

        Args:
            dic: Dictionary previously produced by to_dict().

        Returns:
            A new ConstantNodeModel instance.
        """
        obj = cls.__new__(cls)
        obj.value = dic["value"]
        return obj

    def predict(self, X: np.ndarray) -> np.ndarray:
        """Predicts the constant value for each input row.

        Args:
            X: Matrix of feature values.

        Returns:
            List containing the constant value for each input row.
        """
        if X.ndim == 1:
            X_shaped = X.reshape(1, -1)
        else:
            X_shaped = X
        return np.full(X_shaped.shape[0], self.value)

__init__(value: float)

Parameters:

Name Type Description Default
value float

The constant value to predict.

required
Source code in imote/nodes/node_model.py
121
122
123
124
125
126
def __init__(self, value: float):
    """
    Args:
        value: The constant value to predict.
    """
    self.value = value

get_label() -> str

Returns the constant value as a display label.

Returns:

Type Description
str

The value formatted to 3 significant digits.

Source code in imote/nodes/node_model.py
128
129
130
131
132
133
134
def get_label(self) -> str:
    """Returns the constant value as a display label.

    Returns:
        The value formatted to 3 significant digits.
    """
    return f"{self.value:.3g}"

to_dict() -> dict

Serializes the model to a dictionary.

Returns:

Type Description
dict

Dictionary with class_type and value.

Source code in imote/nodes/node_model.py
136
137
138
139
140
141
142
143
144
def to_dict(self) -> dict:
    """Serializes the model to a dictionary.

    Returns:
        Dictionary with class_type and value.
    """
    dic = super().to_dict()
    dic["value"] = self.value
    return dic

from_dict(dic: dict) -> ConstantNodeModel classmethod

Reconstructs a ConstantNodeModel instance.

Parameters:

Name Type Description Default
dic dict

Dictionary previously produced by to_dict().

required

Returns:

Type Description
ConstantNodeModel

A new ConstantNodeModel instance.

Source code in imote/nodes/node_model.py
146
147
148
149
150
151
152
153
154
155
156
157
158
@classmethod
def from_dict(cls, dic: dict) -> "ConstantNodeModel":
    """Reconstructs a ConstantNodeModel instance.

    Args:
        dic: Dictionary previously produced by to_dict().

    Returns:
        A new ConstantNodeModel instance.
    """
    obj = cls.__new__(cls)
    obj.value = dic["value"]
    return obj

predict(X: np.ndarray) -> np.ndarray

Predicts the constant value for each input row.

Parameters:

Name Type Description Default
X ndarray

Matrix of feature values.

required

Returns:

Type Description
ndarray

List containing the constant value for each input row.

Source code in imote/nodes/node_model.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def predict(self, X: np.ndarray) -> np.ndarray:
    """Predicts the constant value for each input row.

    Args:
        X: Matrix of feature values.

    Returns:
        List containing the constant value for each input row.
    """
    if X.ndim == 1:
        X_shaped = X.reshape(1, -1)
    else:
        X_shaped = X
    return np.full(X_shaped.shape[0], self.value)

SimpleLinearNodeModel

Bases: NodeModel

Model with a one dimensional linear regression model.

Attributes:

Name Type Description
idx int

Index of the feature used in the linear term.

coefficient float

Coefficient applied to the feature at idx.

intercept float

Constant offset added to the prediction.

Source code in imote/nodes/node_model.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
class SimpleLinearNodeModel(NodeModel):
    """Model with a one dimensional linear regression model.

    Attributes:
        idx: Index of the feature used in the linear term.
        coefficient: Coefficient applied to the feature at idx.
        intercept: Constant offset added to the prediction.
    """

    idx: int
    coefficient: float
    intercept: float

    def __init__(self, idx: int, coefficient: float, intercept: float):
        """
        Args:
            idx: Index of the feature used in the linear term.
            coefficient: Coefficient applied to the feature at idx.
            intercept: Constant offset added to the prediction.
        """
        self.idx = idx
        self.coefficient = coefficient
        self.intercept = intercept

    def get_label(self) -> str:
        """Returns the linear term as a display label.

        Returns:
            Label of the linear model.
        """
        return f"{self.coefficient:.3g}·X{self.idx} + {self.intercept:.3g}"

    def to_dict(self) -> dict:
        """Serializes the model to a dictionary.

        Returns:
            Dictionary with class_type, idx, coefficient, and intercept.
        """
        dic = super().to_dict()
        dic.update({"idx": self.idx,
                     "coefficient": self.coefficient,
                     "intercept": self.intercept})
        return dic

    def predict(self, x: np.ndarray) -> np.ndarray:
        """Predict using the one-dimensional linear regression model.

        Args:
            x: List of feature values.

        Returns:
            Array of predicted values.
        """
        return self.intercept + self.coefficient * x

    @classmethod
    def from_dict(cls, dic: dict) -> "SimpleLinearNodeModel":
        """Reconstructs a SimpleLinearNodeModel instance.

        Args:
            dic: Dictionary previously produced by to_dict().

        Returns:
            A new SimpleLinearNodeModel instance.
        """
        obj = cls.__new__(cls)
        obj.idx = dic["idx"]
        obj.coefficient = dic["coefficient"]
        obj.intercept = dic["intercept"]
        return obj

__init__(idx: int, coefficient: float, intercept: float)

Parameters:

Name Type Description Default
idx int

Index of the feature used in the linear term.

required
coefficient float

Coefficient applied to the feature at idx.

required
intercept float

Constant offset added to the prediction.

required
Source code in imote/nodes/node_model.py
188
189
190
191
192
193
194
195
196
197
def __init__(self, idx: int, coefficient: float, intercept: float):
    """
    Args:
        idx: Index of the feature used in the linear term.
        coefficient: Coefficient applied to the feature at idx.
        intercept: Constant offset added to the prediction.
    """
    self.idx = idx
    self.coefficient = coefficient
    self.intercept = intercept

get_label() -> str

Returns the linear term as a display label.

Returns:

Type Description
str

Label of the linear model.

Source code in imote/nodes/node_model.py
199
200
201
202
203
204
205
def get_label(self) -> str:
    """Returns the linear term as a display label.

    Returns:
        Label of the linear model.
    """
    return f"{self.coefficient:.3g}·X{self.idx} + {self.intercept:.3g}"

to_dict() -> dict

Serializes the model to a dictionary.

Returns:

Type Description
dict

Dictionary with class_type, idx, coefficient, and intercept.

Source code in imote/nodes/node_model.py
207
208
209
210
211
212
213
214
215
216
217
def to_dict(self) -> dict:
    """Serializes the model to a dictionary.

    Returns:
        Dictionary with class_type, idx, coefficient, and intercept.
    """
    dic = super().to_dict()
    dic.update({"idx": self.idx,
                 "coefficient": self.coefficient,
                 "intercept": self.intercept})
    return dic

predict(x: np.ndarray) -> np.ndarray

Predict using the one-dimensional linear regression model.

Parameters:

Name Type Description Default
x ndarray

List of feature values.

required

Returns:

Type Description
ndarray

Array of predicted values.

Source code in imote/nodes/node_model.py
219
220
221
222
223
224
225
226
227
228
def predict(self, x: np.ndarray) -> np.ndarray:
    """Predict using the one-dimensional linear regression model.

    Args:
        x: List of feature values.

    Returns:
        Array of predicted values.
    """
    return self.intercept + self.coefficient * x

from_dict(dic: dict) -> SimpleLinearNodeModel classmethod

Reconstructs a SimpleLinearNodeModel instance.

Parameters:

Name Type Description Default
dic dict

Dictionary previously produced by to_dict().

required

Returns:

Type Description
SimpleLinearNodeModel

A new SimpleLinearNodeModel instance.

Source code in imote/nodes/node_model.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
@classmethod
def from_dict(cls, dic: dict) -> "SimpleLinearNodeModel":
    """Reconstructs a SimpleLinearNodeModel instance.

    Args:
        dic: Dictionary previously produced by to_dict().

    Returns:
        A new SimpleLinearNodeModel instance.
    """
    obj = cls.__new__(cls)
    obj.idx = dic["idx"]
    obj.coefficient = dic["coefficient"]
    obj.intercept = dic["intercept"]
    return obj

LinearNodeModel

Bases: NodeModel

Model with a full linear regression model.

Attributes:

Name Type Description
coefficients ndarray

Coefficient for each feature.

intercept float

Constant offset added to the prediction.

Source code in imote/nodes/node_model.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
class LinearNodeModel(NodeModel):
    """Model with a full linear regression model.

    Attributes:
        coefficients: Coefficient for each feature.
        intercept: Constant offset added to the prediction.
    """

    coefficients: np.ndarray
    intercept: float

    def __init__(self, coefficients: np.ndarray, intercept: float):
        """
        Args:
            coefficients: Coefficient for each feature.
            intercept: Constant offset added to the prediction.
        """
        self.coefficients = coefficients
        self.intercept = intercept

    def get_label(self) -> str:
        """Returns the linear model as a display label.

        Only features with a non-zero coefficient are included.

        Returns:
            String of the linear model.
        """
        parts = []
        for i, coef in enumerate(self.coefficients):
            if coef != 0:
                parts.append(f"{coef:.3g}·X{i}")
        parts.append(f"{self.intercept:.3g}")
        return " + ".join(parts)

    def to_dict(self) -> dict:
        """Serializes the model to a dictionary.

        Returns:
            Dictionary with class_type, coefficients, and intercept.
        """
        dic = super().to_dict()
        dic.update({"coefficients": self.coefficients.tolist(),
                     "intercept": self.intercept})
        return dic

    def predict(self, X: np.ndarray) -> np.ndarray:
        """Predicts using the full linear regression model.

        Args:
            X: Input feature matrix, one row per sample.

        Returns:
            Array of predicted values.
        """
        if X.ndim == 1:
            X_shaped = X.reshape(1, -1)
        else:
            X_shaped = X
        return self.intercept + X_shaped @ self.coefficients

    def add_model(self, model: NodeModel):
        """Merges another model's parameters into this one, in place.

        Args:
            model: A SimpleLinearNodeModel, ConstantNodeModel, or
                LinearNodeModel to merge in.

        Raises:
            ValueError: If model is a LinearNodeModel with a different
                number of coefficients than this model.
        """
        if isinstance(model, SimpleLinearNodeModel):
            self.coefficients[model.idx] += model.coefficient
            self.intercept += model.intercept
        elif isinstance(model, ConstantNodeModel):
            self.intercept += model.value
        elif isinstance(model, LinearNodeModel):
            if len(self.coefficients) != len(model.coefficients):
                raise ValueError(f"Number of coefficients does not match: {len(self.coefficients)} vs {len(model.coefficients)}")
            self.coefficients = self.coefficients + model.coefficients
            self.intercept += model.intercept

    @classmethod
    def from_dict(cls, dic: dict) -> "LinearNodeModel":
        """Reconstructs a LinearNodeModel instance.

        Args:
            dic: Dictionary previously produced by to_dict().

        Returns:
            A new LinearNodeModel instance.
        """
        obj = cls.__new__(cls)
        obj.coefficients = np.asarray(dic["coefficients"])
        obj.intercept = dic["intercept"]
        return obj

__init__(coefficients: np.ndarray, intercept: float)

Parameters:

Name Type Description Default
coefficients ndarray

Coefficient for each feature.

required
intercept float

Constant offset added to the prediction.

required
Source code in imote/nodes/node_model.py
257
258
259
260
261
262
263
264
def __init__(self, coefficients: np.ndarray, intercept: float):
    """
    Args:
        coefficients: Coefficient for each feature.
        intercept: Constant offset added to the prediction.
    """
    self.coefficients = coefficients
    self.intercept = intercept

get_label() -> str

Returns the linear model as a display label.

Only features with a non-zero coefficient are included.

Returns:

Type Description
str

String of the linear model.

Source code in imote/nodes/node_model.py
266
267
268
269
270
271
272
273
274
275
276
277
278
279
def get_label(self) -> str:
    """Returns the linear model as a display label.

    Only features with a non-zero coefficient are included.

    Returns:
        String of the linear model.
    """
    parts = []
    for i, coef in enumerate(self.coefficients):
        if coef != 0:
            parts.append(f"{coef:.3g}·X{i}")
    parts.append(f"{self.intercept:.3g}")
    return " + ".join(parts)

to_dict() -> dict

Serializes the model to a dictionary.

Returns:

Type Description
dict

Dictionary with class_type, coefficients, and intercept.

Source code in imote/nodes/node_model.py
281
282
283
284
285
286
287
288
289
290
def to_dict(self) -> dict:
    """Serializes the model to a dictionary.

    Returns:
        Dictionary with class_type, coefficients, and intercept.
    """
    dic = super().to_dict()
    dic.update({"coefficients": self.coefficients.tolist(),
                 "intercept": self.intercept})
    return dic

predict(X: np.ndarray) -> np.ndarray

Predicts using the full linear regression model.

Parameters:

Name Type Description Default
X ndarray

Input feature matrix, one row per sample.

required

Returns:

Type Description
ndarray

Array of predicted values.

Source code in imote/nodes/node_model.py
292
293
294
295
296
297
298
299
300
301
302
303
304
305
def predict(self, X: np.ndarray) -> np.ndarray:
    """Predicts using the full linear regression model.

    Args:
        X: Input feature matrix, one row per sample.

    Returns:
        Array of predicted values.
    """
    if X.ndim == 1:
        X_shaped = X.reshape(1, -1)
    else:
        X_shaped = X
    return self.intercept + X_shaped @ self.coefficients

add_model(model: NodeModel)

Merges another model's parameters into this one, in place.

Parameters:

Name Type Description Default
model NodeModel

A SimpleLinearNodeModel, ConstantNodeModel, or LinearNodeModel to merge in.

required

Raises:

Type Description
ValueError

If model is a LinearNodeModel with a different number of coefficients than this model.

Source code in imote/nodes/node_model.py
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
def add_model(self, model: NodeModel):
    """Merges another model's parameters into this one, in place.

    Args:
        model: A SimpleLinearNodeModel, ConstantNodeModel, or
            LinearNodeModel to merge in.

    Raises:
        ValueError: If model is a LinearNodeModel with a different
            number of coefficients than this model.
    """
    if isinstance(model, SimpleLinearNodeModel):
        self.coefficients[model.idx] += model.coefficient
        self.intercept += model.intercept
    elif isinstance(model, ConstantNodeModel):
        self.intercept += model.value
    elif isinstance(model, LinearNodeModel):
        if len(self.coefficients) != len(model.coefficients):
            raise ValueError(f"Number of coefficients does not match: {len(self.coefficients)} vs {len(model.coefficients)}")
        self.coefficients = self.coefficients + model.coefficients
        self.intercept += model.intercept

from_dict(dic: dict) -> LinearNodeModel classmethod

Reconstructs a LinearNodeModel instance.

Parameters:

Name Type Description Default
dic dict

Dictionary previously produced by to_dict().

required

Returns:

Type Description
LinearNodeModel

A new LinearNodeModel instance.

Source code in imote/nodes/node_model.py
329
330
331
332
333
334
335
336
337
338
339
340
341
342
@classmethod
def from_dict(cls, dic: dict) -> "LinearNodeModel":
    """Reconstructs a LinearNodeModel instance.

    Args:
        dic: Dictionary previously produced by to_dict().

    Returns:
        A new LinearNodeModel instance.
    """
    obj = cls.__new__(cls)
    obj.coefficients = np.asarray(dic["coefficients"])
    obj.intercept = dic["intercept"]
    return obj

node_model_from_dict(dic: dict) -> NodeModel

Reconstructs a NodeModel instance of the correct subclass.

Parameters:

Name Type Description Default
dic dict

Dictionary previously produced by a NodeModel's to_dict(), containing a "class_type" key.

required

Returns:

Type Description
NodeModel

A new NodeModel instance of the matching subclass.

Source code in imote/nodes/node_model.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def node_model_from_dict(dic: dict) -> NodeModel:
    """Reconstructs a NodeModel instance of the correct subclass.

    Args:
        dic: Dictionary previously produced by a NodeModel's to_dict(),
            containing a "class_type" key.

    Returns:
        A new NodeModel instance of the matching subclass.
    """
    class_type = globals().get(dic["class_type"])
    if class_type is None:
        raise ValueError(f"Unknown node model class: {dic['class_type']!r}")
    return class_type.from_dict(dic)