Categories

Versions

You are viewing the RapidMiner Developers documentation for version 9.2 -Check here for latest version

Adding Your Own Data Objects

You might find that the standard data objects don't fulfill all your requirements. For example, maybe you are going to analyze data recorded from a game engine. The format of the original data can't be expressed directly as a table. Although you could write a single operator that reads in data from a file and does all the translation and feature extraction, you may decide that it's best to split the task. Instead, create multiple operators -- one that can handle the new data object and one that extracts part of the data as an ExampleSet. With this modularity, it is much easier to extend the mechanism later on and to optimize steps separately.

Defining the object class

First, you must define a new class that holds the information you need. This class should preferably extend theResultObjectAdapter, an abstract class that already implements much non-specialized functionality. If a data-containing class with important functionality already exists, it might be more convenient to directly implement theIOObjectinterface. An empty implementation of theResultObjectAdapterwould look like this:

进口com.rapidminer.operator.ResultObjectAdapter; public class DemoDataIOObject extends ResultObjectAdapter { private static final long serialVersionUID = 1725159059797569345L; }

The above is only an empty object that holds no information except for theserialVersionUID. This constant is needed during serialization, when loading an instance of the new object from the repository, or when storing it. Now, add some content:

进口com.rapidminer.operator.ResultObjectAdapter; public class DemoDataIOObject extends ResultObjectAdapter { private static final long serialVersionUID = 1725159059797569345L; private DemoData data; public DemoDataIOObject(DemoData data) { this.data = data; } public DemoData getDemoData() { return data; } @Override public String toString() { return "Custom data specific string"; } }

This class wraps an object of the classDemoDataas anIOObject(through theResultObjectAdapter), thus making it possible to pass it between RapidMiner operators and storing it in the RapidMiner repository. By overwriting thetoStringmethod ofResultObjectAdapteralready allows for simple visualization of data provided by the class inside RapidMiner. More advanced visualizations are also possible by creating complex renderers (see below).

While it might be more complex to implement in real-world applications, this example helps to illustrate how things work in general. You want to extract attribute values from the demo data, which an operator can then store in a table. You need a mechanism to add data to the IOObject. For simplicity, assume you only have numerical attributes to save the effort of remembering the correct types of the data. Add a map for storing the values with an identifier as a local variable:

private Map valueMap = new HashMap();

Then we extend the DemoDataIOObject with two methods for accessing the map:

public void setValue(String identifier, double value) { valueMap.put(identifier, value); } public Map getValueMap() { return valueMap; }

配置

To make your data object accessible, adapt the fileioobjectsNAME.xml(in the resources folder), which contains some examples of how to define yourIOObject.This is how theDemoDataIOObjectis defined:

  com.rapidminer.gui.renderer.DefaultTextRenderer  

Theioobjectneeds anameparameter for identifying the object inside RapidMiner, the path to theclass我们为新对象定义,以及一个option (reportable) whether the object can be used by the Reporting Extension or not. Adding the optional attributeiconallows to define the icon displayed next to the new data objects visualization in the result view tab of the renderer output. By setting the renderer visualization behavior can be defined. Shown is a simple text renderer, which calls thetoString()method of yourIOObjectto display the object in theResultsperspective. Writing more complex renderers is described below.

Similarly to operators, you can also give your own data objects a color. The connection between two operators that exchange your data object will be colored in the assigned color. To control colors, change the filegroupsNAME.propertiesin the resources folder to add a line defining the color:

# IOObjects io.com.rapidminer.operator.demo.DemoDataIOObject.color = #28C68C

Processing your own IOObjects

使用这些方法,您可以现在小鬼lement your first operator, which extracts values of theDemoData.

import java.util.List; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.OperatorException; import com.rapidminer.operator.ports.OutputPort; /** * Operator that generates {@link DemoData} */ public class GenerateDemoDataOperator extends Operator { private OutputPort outputPortDemoData = getOutputPorts().createPort("demo data"); /** * Operator to create {@link DemoData} * * @param description of the operator */ public GenerateDemoDataOperator(OperatorDescription description) { super(description); getTransformer().addGenerationRule(outputPortDemoData, DemoDataIOObject.class); } @Override public void doWork() throws OperatorException { DemoDataIOObject ioobject = new DemoDataIOObject(new DemoData()); List values = ioobject.getDemoData().getValues(); for (int i = 0; i < values.size(); i++) { ioobject.setValue("" + (i + 1), values.get(i)); } outputPortDemoData.deliver(ioobject); } }

The example fetches the values from theDemoDataobject and sets the values of theIOObject. Then, the output port delivers theDemoDataIOObject, thus making it available for other operators or connecting it to other ports in general.

Of course, it's possible to build more complex constructions. You might, for example, use one super operator that handles yourDemoDatawith different inner operators. You could build operators that getDemoDataas input and transform them to an example set. Every method of treating your own IOObjects is possible by combining what we have learned.

Looking into your IOObject - Custom Renderers

When building a process for yourIOObjects, it's an excellent idea to set breakpoints with the process and take a look at what's contained in the objects. To continue with the example above, it would be interesting to see which values have been extracted. If you set a breakpoint, RapidMiner will display the result of thetoStringmethod as the default fallback.

There is plenty of space you could fill with information about the object. How can you do this? The simplest approach is to override thetoStringmethod of theIOObject. However, it is better to override thetoResultString方法,在默认情况下,只有调用toString方法。

Although text output has its advantages, writing Courier characters to the screen is a bit outdated. How do you add nice representations to the output as is done with nearly all core RapidMinerIOObjects?

RapidMiner uses a renderer concept for displaying the various types ofIOObjects. So, you should implement a renderer for yourDemoDataIOObject.

You must implement theRendererinterface for this purpose. Extend theAbstractRenderer, which has most of the methods already implemented. Most of the methods are used for handling parameters, since renderers might have parameters, just as operators do. They are used during automatic object reporting and control the output. The handling of these parameters and their values is done by the abstract class, you just need to take their values into account when rendering. Here is the example code of a renderer for our DemoData.

import java.awt.Component; import com.rapidminer.gui.renderer.AbstractRenderer; import com.rapidminer.operator.IOContainer; import com.rapidminer.report.Reportable; public class DemoDataRenderer extends AbstractRenderer { public static final String RENDERER_NAME = "demo_data_renderer"; @Override public String getName() { return RENDERER_NAME; } @Override public Component getVisualizationComponent(Object renderable, IOContainer ioContainer) { // TODO Auto-generated method stub return null; } @Override public Reportable createReportable(Object renderable, IOContainer ioContainer, int desiredWidth, int desiredHeight) { // TODO Auto-generated method stub return null; } }

The renderer has a name, defined by the getName() method. Best practice is to use a public static field variable to define the name of the renderer, as it is done by theRENDERER_NAMEfield in the example. The renderer name is used also as a key in configuration files, as we will see later.

Besides defining the renderer name, two methods have to be implemented. The method createReportable must return an object, that implements one of the sub interfaces ofReportable. This is used if you want to report the object in anyway, e.g. in the Reporting Extension. Please have a look at the interfaces and some of the implementations in the core. If you only want to visualize your object in the results panel of RapidMiner you can safely return null here. But then you should implement the toString() method of the underlyingIOObject.

The second method getVisualizationComponent returns an arbitrary Java component used for displaying content in Swing. You have all the flexibility of Swing, as long as you return a Java component.

There are also some abstract subclasses of theAbstractRendererin RapidMiner which you can use for the visualization. For example theAbstractTableModelTableRenderer. As the name indicates, it shows a table based upon a table model. All you need to do is to return this table model:

import java.util.ArrayList; import java.util.List; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; import com.rapidminer.gui.renderer.AbstractTableModelTableRenderer; import com.rapidminer.operator.IOContainer; import com.rapidminer.operator.demo.DemoDataIOObject; import com.rapidminer.tools.container.Pair; public class NewDemoDataRenderer extends AbstractTableModelTableRenderer { public static final String RENDERER_NAME = "demo_data_renderer"; @Override public String getName() { return RENDERER_NAME; } @Override public TableModel getTableModel(Object renderable, IOContainer ioContainer, boolean isReporting) { if (renderable instanceof DemoDataIOObject) { DemoDataIOObject object = (DemoDataIOObject) renderable; final List> values = new ArrayList<>(); // Fill the table value variable 'values' with entries from the DemoDataIOObject for (String key : object.getValueMap().keySet()) { values.add(new Pair(key, object.getValueMap().get(key))); } return new AbstractTableModel() { private static final long serialVersionUID = 1L; @Override public int getColumnCount() { return 2; } @Override public String getColumnName(int column) { if (column == 0) { return "Id"; } return "Value"; } @Override public int getRowCount() { return values.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { Pair pair = values.get(rowIndex); if (columnIndex == 0) { return pair.getFirst(); } return pair.getSecond(); } }; } return new DefaultTableModel(); } }

There are other convenience methods in theAbstractTableModelTableRendererfor changing the appearance of the table. For example, the following methods change the behavior of the table by enabling or disabling certain features:

@Override public boolean isSortable() { return true; } @Override public boolean isAutoresize() { return false; } @Override public boolean isColumnMovable() { return true; }

To use the new renderer, you must change the fileioobjectsNAME.xmlin the resources folder again. Just add the renderer before the default renderer:

  com.rapidminer.gui.renderer.demo.DemoDataRenderer com.rapidminer.gui.renderer.DefaultTextRenderer  

You can now see the result of your efforts in building a table representation of the DemoData values.

You can add as many customized renderers as you want. Each one will appear as a renderer tab in the list of the left side of the panel.

You can further customize the appearance of your renderer by giving it a title, a tooltip and its own icon. This is done in theGUI.propertiesfile in thei18nfolder of your extension resources.

gui.cards.result_view..icon = .png gui.cards.result_view..title = Demo data gui.cards.result_view..tip = Shows the values of the DemoData as a table representation.

Use the same string as you specified in theRENDERER_NAMEfield of your renderer class. Only icons with 32x32 pixels are supported. You can put your own icons in the corresponding folder of your extension or you can use the icons provided in the 32 pixels folder in RapidMiner Studio Core.

Other useful GUI classes in the core are the following:

RapidMinerGUI: gives you access to the Mainframe and other relevant structures of the GUI

DataViewerTable: A useful renderer to visualize ExampleSets.

ExtendedJTable: Extends the JTable class and adds functionality like sorting, row highlighting etc.

ExtendedJScrollPane: Improves scrolling behavior.

RendererService: Gives you access to already existing renderers

Some more tips to adapt the look of your renderer to the look of RapidMiner Studio:

  • Set borders to null if possible, especially borders of scroll panes
  • Most renderers should come with a white background -- tables and scroll panes often have children whose background color needs to be changed to white as well
  • Most renderers provide some empty borders, e.g. with:tableScrollPane.setBorder(BorderFactory.createEmptyBorder(15, 10, 10, 10));
  • The IOContainer object, which is provided when the getVisualizationComponent method is called, gives you access to all otherIOObjectsavailable at the moment. This can be used if the rendering depends on properties of other objects.