Categories

Versions

Adding Input and Output Ports

To define ports, you simply add them as private variables using the following lines of code:

private InputPort tableInput = getInputPorts().createPort("example set");
private OutputPort tableOutput = getOutputPorts().createPort("example set");

You can add more input ports, but you must set unique names for input ports and output ports for each operator. To follow the name convention, write the names in lower case and use blanks to separate words.

To fill the doWork() method with content, add a new column with random values from 1 to 10 to a copy of the table received from the input port. You receive the original table by calling:

IOTable ioTable = tableInput.getData(IOTable.class);
Table table = ioTable.getTable();

The Table class is the representation of example sets in RapidMiner Studio. Now, add a new column named newAttribute by using the TableBuilder. Since we want to add a column with random real values use addReal.

TableBuilder builder = Builders.newTableBuilder(table);
builder.addReal("newAttribute", i -> Math.random() * 10);

The addReal method takes a column name and a IntToDoubleFunction that is used as a data generator. The i-th row will contain the value that the generator returns for the index i. Once you are finished defining the new table you can build it via

Table newTable = builder.build(BeltTools.getContext(this));

Now deliver the resulting table to the output port:

IOTable newIOTable = new IOTable(newTable);
newIOTable.getAnnotations().addAll(ioTable.getAnnotations());
tableOutput.deliver(newIOTable);

Objects delivered to / received from operator ports need to implement the IOObject interface. Since the Table class itself is not an IOObject we need to wrap it with the IOTable class. Also it is important to copy the annotations of the original IOTable to the new IOTable because otherwise they will be lost.

package com.rapidminer.extension.operator;

import java.util.logging.Level;

import com.rapidminer.adaption.belt.IOTable;
import com.rapidminer.belt.table.Builders;
import com.rapidminer.belt.table.Table;
import com.rapidminer.belt.table.TableBuilder;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.OutputPort;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.belt.BeltTools;


/**
 * Operator that takes a {@link Table} and returns a copy of the table with
 * an additional real column holding random values between zero and ten.
 */
public class MyOwnOperator extends Operator {

    private InputPort tableInput = getInputPorts().createPort("example set");
    private OutputPort tableOutput = getOutputPorts().createPort("example set");

    public MyOwnOperator(OperatorDescription description) {
        super(description);
    }

    @Override
    public void doWork() throws OperatorException {
        LogService.getRoot().log(Level.INFO, "Doing something...");

        // fetch table from input port
        IOTable ioTable = tableInput.getData(IOTable.class);
        Table table = ioTable.getTable();

        // initialize builder with existing table
        TableBuilder builder = Builders.newTableBuilder(table);

        // add real column to builder
        builder.addReal("newAttribute", i -> Math.random() * 10);

        // build new table in parallel using the operator's context
        Table newTable = builder.build(BeltTools.getContext(this));

        // wrap the result into an IOTable
        IOTable newIOTable = new IOTable(newTable);

        // copy the annotations from the original IOTable
        newIOTable.getAnnotations().addAll(ioTable.getAnnotations());

        // deliver the new IOTable to the port
        tableOutput.deliver(newIOTable);
    }
}

Install the extension again (with the Gradle task) and restart RapidMiner Studio. You can see that the operator now has an input and an output port.

../img/operator-with-ports-connected.png

When you run the process that loads sample data and applies the example operator to it, you can see that the new column appears in the resulting example set.

../img/operator-with-ports-example-set.png

This example shows an operator, which creates a completely new table with one nominal and one numerical column.

../img/my-own-operator-creating-example-set-result.png

Check out the code:

package com.rapidminer.extension.operator;

import java.util.UUID;

import com.rapidminer.adaption.belt.IOTable;
import com.rapidminer.belt.table.Builders;
import com.rapidminer.belt.table.Table;
import com.rapidminer.belt.table.TableBuilder;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ports.OutputPort;
import com.rapidminer.tools.belt.BeltTools;


/**
 * Operator creating a completely new {@link Table} with then rows and two columns.
 * The first column holding random nominal UUIDs and the second column holding 
 * random real values.
 */
public class MyOwnOperator extends Operator {

    private OutputPort tableOutput = getOutputPorts().createPort("example set");

    public MyOwnOperator(OperatorDescription description) {
        super(description);
        getTransformer().addGenerationRule(tableOutput, IOTable.class);
    }

    @Override
    public void doWork() throws OperatorException {
        // initialize empty builder for table with 10 rows
        TableBuilder builder = Builders.newTableBuilder(10);

        // add two columns and the corresponding data generators
        builder.addNominal("ID", i -> UUID.randomUUID().toString());
        builder.addReal("random number", i -> Math.random());

        // build table in parallel using the operator's context
        Table table = builder.build(BeltTools.getContext(this));

        // wrap the table into an IOTable and deliver it to the port
        IOTable ioTable = new IOTable(table);
        tableOutput.deliver(ioTable);
    }
}

This operator just creates 10 random examples. An enhancement could be to create a parameter that defines how many examples the operator creates.

The next step is to define preconditions to be sure that you only get the type of input you want to process.