We would like to assume you would like to implement a learner that learns a certain model from the data and that you would subsequently like to have a predictor node that uses the learned model in order to classify new data. To accomplish this, you need a facility to pass the learned model from the learner to the predictor. The KNIME framework provides this functionality using the ModelPort concept. To roughly explain this concept we will now create the learned model for our numeric binner and write the interval bounds for every bin into a model. For this purpose it is necessary to overwrite the saveModelContent method of the NodeModel. If we had a node with a ModelInport we would have to overwrite the loadModelContent method in order to be able to load the model when it is connected to the inport. Although it is possible to write your model directly to the ModelContent object it is good practice and highly recommended to have an object that represents the external model of your node and which is solely responsible for the loading from and saving to the ModelContent. In our case we create a class NumericBinModel. It is then very easy to share your model with other nodes. To increase the usability of the model we store the lower and upper bound as an interval for every bin. Since we only have a ModelOutport we only have to implement the saveTo method, as can be seen below:
public void saveTo(final ModelContentWO modelContent) {
modelContent.addInt(NUMBER_OF_BINS, m_intervals.size());
int intervalNr = 0;
for (Interval interval : m_intervals) {
ModelContentWO intervalModel = modelContent.addModelContent(
INTERVAL + intervalNr++);
intervalModel.addDouble(LOWER_BOUND, interval.getLowerBound());
intervalModel.addDouble(UPPER_BOUND, interval.getUpperBound());
}
}
The interval simply stores the lower bound upper bound pair. In addition the model provides some methods to add intervals and retrieve the intervals:
public double getLowerBoundForInterval(final int binNumber) {
return m_intervals.get(binNumber).getLowerBound();
}
public double getUpperBoundForInterval(final int binNumber) {
return m_intervals.get(binNumber).getUpperBound();
}
public int getNumberOfBins() {
return m_intervals.size();
}
public void addInterval(final double lowerBound, final double upperBound) {
Interval interval = new Interval(lowerBound, upperBound);
m_intervals.add(interval);
}
Now, we have to fill the model in the for-loop of the NodeModel's execute method:
...
double intervalUpperBound = lowerBound;
m_model = new NumericBinModel();
double intervalLowerBound = lowerBound;
for (int i = 0; i < m_numberOfBins.getIntValue(); i++) {
intervalLowerBound = intervalUpperBound;
intervalUpperBound += interval;
m_model.addInterval(intervalLowerBound, intervalUpperBound);
splitPoints.add(intervalUpperBound);
m_bins[i] = new NumericBin();
}
...
Once the model has been filled with the information about the intervals it is possible to save it in the saveModelContent method of the NodeModel:
@Override
protected void saveModelContent(final int index,
final ModelContentWO modelContent) throws InvalidSettingsException {
m_model.saveTo(modelContent);
}
Since we now provide an external model of our node we have to add a ModelOutport. This is completed in the constructor of the NodeModel, where the number of DataIn- and Outports is defined with the first two parameters and the number of ModelIn- and Outports with the last two parameters. Since we have no ModelInport and one ModelOutport the resulting new constructor looks like this:
protected NumericBinnerNodeModel() {
super(1, 1, 0, 1);
}
Having adapted your constructor in this way you will notice the following error the next time you start your workbench:
ERROR NumericBinnerNodeFactory CODING PROBLEM Missing or surplus predictor output port name
To avoid this you have to adapt your node description, which is explained in the next section.