创建超级操作符
在RapidMiner中有两种类型的操作符——普通操作符和超级操作符。超级操作符包含一个或多个子进程。您从实现一个普通的操作符开始,但有时一个操作符依赖于其他操作符的执行。有时候这些运算符应该是用户自定义的。以交叉验证为例。用户可以指定学习者和衡量表现的方式;然后根据需要执行这些子流程。
假设您有一个应该循环遍历值的操作符,但是循环值RapidMiner Studio中的操作符循环遍历属性的值。您需要一个在给定范围内以给定步长循环遍历值的操作符,并且不希望为此目的创建属性。相反,构建一个超级操作符,在给定范围的每一步重新执行其内部操作符。为此,创建一个新类,但这次扩展OperatorChain类。与简单操作符一样,必须实现构造函数。空类看起来像这样:
公共类LoopValuesRange扩展OperatorChain{公共LoopValuesRange(OperatorDescription描述){超级(描述,“执行进程”);}}
与简单操作符相反,必须向超级构造函数提供将要在超级操作符中创建的子进程的名称。传递给超级构造函数的名称的数量决定了创建的子进程的数量。如果您希望遵循命名约定,请将每个单词大写,并用空格分隔单词。稍后,您可以通过索引访问这些子进程以执行它们。但首先,定义一些端口来将数据传递给超级操作符。
对超级操作符使用portpaireextender
你之前学过如何使用PortPairExtender来为简单的操作符创建吞吐量端口。您还需要这个类将数据从超级操作符传递到子进程并返回。以一种通用的方式进行,以便用户可以将任何数字和任何类型的对象传递给内部进程。你可能知道这个行为循环RapidMiner Studio的运营商。添加这个的代码PortPairExtender看起来像这样:
private final PortPairExtender inputPortPairExtender = new PortPairExtender("input", getInputPorts(), getSubprocess(0).getInnerSources());
除了PortPairExtender,也有PortExtender。使用PortPairExtender以获得相等数量的输入和输出端口。仔细看看PortPairExtender构造函数。除了名称之外,还必须指定扩展程序应该附加到哪个输入端口。的getInputPorts方法提供当前操作符的输入端口(因此端口扩展程序附加在操作符框的左侧)。配对端口被添加到第一个子流程的内部源。然后,您可以通过getSubprocess方法。
如果您熟悉RapidMiner的集成超级操作符,如循环,您知道子流程的左侧总是有输入端口,右侧总是有输出端口。为了将这些端口与超级操作符的输入和输出端口区分开来,RapidMiner将它们称为内部源和内部汇。实际上,内部源在技术上是超级操作符的输出端口(因为它将数据传递到该端口)。内部接收器是超级操作符的输入端口,超级操作符可以从中检索子流程的输出。要从循环中交付输出,您可以添加以下第二个变量PortPairExtender收集所有迭代的输出,并将其作为一个集合传递给超操作符的输出:
private final CollectingPortPairExtender outExtender = new CollectingPortPairExtender("output", getSubprocess(0). getinnersink (), getOutputPorts());
这将产生如下所示的操作符:
做…PortExtender工作时,必须在构造运算符时初始化它。只需在构造函数中添加以下几行:
inputPortPairExtender.start ();outExtender.start ();
要在输出端口获得适当的元数据,请添加一些规则:
getTransformer () .addRule (inputPortPairExtender.makePassThroughRule ());
您必须添加一个规则来定义何时转换子流程的元数据。规则定义的顺序是至关重要的,因为如果元数据没有转发到内部端口,那么内部操作符的元数据转换就无能为力。这一行添加了以下规则:
getTransformer()。addRule(新SubprocessTransformRule (getSubprocess (0)));
接下来,你需要一个规则来将元数据从内部接收器传递到输出端口:
getTransformer () .addRule (outExtender.makePassThroughRule ());
的最小设置doWork ()
方法看起来像这样:
doWork()抛出operatoreexception {outExtender.reset();inputPortPairExtender.passDataThrough ();getSubprocess (0) . execute ();outExtender.collect ();}
首先,它重置CollectingPortPairExtender,然后将数据从超级运算符的输入端口传递到内部端口。接下来,执行子流程并最终收集所有输出。
试试这个吧。添加四个参数—起始值、结束值、范围的步长,以及一个可以输入宏名称的字段(其中包含循环执行期间的当前值)。然后,调整doWork ()
方法。循环遍历给定范围内的值,在每次迭代中定义宏值,并在每次迭代中执行子流程。最终的结果是这样的:
可以看到,操作符现在有了定义循环值范围的形参。在子流程中,您可以读取宏值(循环中的当前值),并使用开始时创建的第一个简单操作符将其打印出来。
日志条目显示,在每次迭代中,宏的值发生了变化,并且执行了子流程。
最后,超级操作符看起来是这样的:
/** *超级运算符的示例,循环遍历由范围和步长给定的值。*/ public class LoopValuesRange extends OperatorChain{public static final String PARAMETER_START = "start";public static final String PARAMETER_END = "end";PARAMETER_STEP_SIZE =“步长”;public static final String PARAMETER_MACRO_NAME = "迭代宏";private final PortPairExtender inputPortPairExtender = new PortPairExtender("input", getInputPorts(), getSubprocess(0).getInnerSources());private final CollectingPortPairExtender outExtender = new CollectingPortPairExtender("output", getSubprocess(0). getinnersink (), getOutputPorts());/** *构造函数* @参数描述*/公共LoopValuesRange(OperatorDescription描述){super(描述,“执行进程”);inputPortPairExtender.start ();outExtender.start (); getTransformer().addRule(inputPortPairExtender.makePassThroughRule()); getTransformer().addRule(new SubprocessTransformRule(getSubprocess(0))); getTransformer().addRule(outExtender.makePassThroughRule()); } @Override public void doWork() throws OperatorException { outExtender.reset(); inputPortPairExtender.passDataThrough(); double start = getParameterAsDouble(PARAMETER_START); double end = getParameterAsDouble(PARAMETER_END); double stepsize = getParameterAsDouble(PARAMETER_STEP_SIZE); String macro = getParameterAsString(PARAMETER_MACRO_NAME); for(double i=start; i getParameterTypes() { List types = super.getParameterTypes(); types.add(new ParameterTypeDouble(PARAMETER_START, "start value of the value range", Integer.MIN_VALUE, Integer.MAX_VALUE, 0, false)); types.add(new ParameterTypeDouble(PARAMETER_END, "end value of the value range", Integer.MIN_VALUE, Integer.MAX_VALUE, 1, false)); types.add(new ParameterTypeDouble(PARAMETER_STEP_SIZE, "step size of the value range", 0, Integer.MAX_VALUE, 0.1, false)); types.add(new ParameterTypeString(PARAMETER_MACRO_NAME, "This parameter specifies the name of the macro which holds "+ "the current value of the selected range in each iteration.", "loop_value")); return types; } }