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 Altair AI 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.mytest.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 Altair AI Studio. You can see that the operator now has an input and an output port.
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.
This example shows an operator, which creates a completely new table with one nominal and one numerical column.
Check out the code:
package com.rapidminer.extension.mytest.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 ten 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.