如何使用 NodeDialog 在 NodeModel 中实现自己的算法

我们首先解释一个非常简单的 biner。bin 是等距间隔的,这样某个属性的整个范围被划分为 n 个区间。在第 k 个区间内具有属性值的数据点被认为属于第 k 个 bin。因此,输出是原始表,并为每个实例(即行)附加了分箱信息。该节点还需要一个对话框,因为用户应该能够确定 bin 的数量,并指定应该对值进行分箱的列。

节点模型:
在我们开始在 execute 方法中实现实际的分箱算法之前,我们必须在 NodeModel 中定义我们需要的字段。(创建后,NodeModel 已经包含可以删除的示例代码)。SettingsModel 提供了一种从 NodeModel 到 NodeDialog 交换设置的便捷方式。正如您稍后将看到的,NodeDialog 还与 SettingsModel 一起使用,这就是为什么我们将它们用于 bin 的数量和值应该被分箱的列的原因:

// bin 数量的设置模型 
私有最终 SettingsModelIntegerBounded m_numberOfBins =
	新的 SettingsModelIntegerBounded(NumericBinnerNodeModel.CFGKEY_NR_OF_BINS,
                NumericBinnerNodeModel.DEFAULT_NR_OF_BINS,
                1, Integer.MAX_VALUE);

// 将列存储到 bin 的设置模型
private final SettingsModelString m_column = new SettingsModelString(
        NumericBinnerNodeModel.CFGKEY_COLUMN_NAME, "");

为了从对话框中获取设置,必须将它们写入 NodeSettings 对象。NodeSettings 将设置从对话框传输到模型,反之亦然。每个字段都需要一个键来标识和从 NodeSettings 中检索它。定义用作 NodeModel 键的静态最终字符串是一种很好的做法。

/** bin 数量的配置键。*/
public static final String CFGKEY_NR_OF_BINS = "numberOfbins"; 
/** 所选列的配置键。*/
public static final String CFGKEY_COLUMN_NAME = "columnName";

将设置从 NodeModel 传输到 NodeDialog 是通过实现 validateSettings、loadValidatedSettings 和 saveSettings 方法来实现的。所有这些方法都可以安全地委托给 SettingsModels。在 validateSettings 方法中,会检查这些值是否存在且有效(例如在有效范围内等)。

/**
 * @see org.knime.core.node.NodeModel
 * #validateSettings(org.knime.core.node.NodeSettingsRO)
 */
 @覆盖
protected void validateSettings(最终的 NodeSettingsRO 设置)
        抛出 InvalidSettingsException {
        
	// 将此委托给设置模型
	
    m_numberOfBins.validateSettings(settings);
    m_column.validateSettings(settings);
}

当 loadValidatedSettings 方法被调用时,设置已经被验证并且可以加载到本地字段中,在这种情况下是 bin 数量和所选列的 SettingsModels。

/**
 * @see org.knime.core.node.NodeModel
 * #loadValidatedSettingsFrom(org.knime.core.node.NodeSettingsRO)
 */
 @覆盖
protected void loadValidatedSettingsFrom(最终 NodeSettingsRO 设置)
        抛出 InvalidSettingsException {
        
	// 将设置中的值加载到模型中。
    // 可以安全地假设设置由 
    // 下面的方法。
    
    m_numberOfBins.loadSettingsFrom(settings);
    m_column.loadSettingsFrom(settings);

}

在 saveSettings 方法中,本地字段被写入设置,以便对话框显示当前值。

/**
 * @see org.knime.core.node.NodeModel
 * #saveSettingsTo(org.knime.core.node.NodeSettings)
 */
 @覆盖
protected void saveSettingsTo(最终 NodeSettingsWO 设置){

    // 将设置保存到配置对象。
	
    m_numberOfBins.saveSettingsTo(settings);
    m_column.saveSettingsTo(settings);
}

上述方法只是检查节点是否可以使用当前设置执行的一个步骤。检查它是否适用于传入的数据表也非常重要。这是通过配置方法完成的。一旦连接了输入端口,就会执行配置方法。在我们的数字合并器的小示例中,执行检查以查看是否至少有一个数字列可用,以及传入的数据表是否包含具有所选列名称的列。否则节点不可执行。DataTableSpec 包含所需的信息并传递给 configure 方法。

/**
 * @see org.knime.core.node.NodeModel
 * #configure(org.knime.core.data.DataTableSpec[])
 */
受保护的 DataTableSpec[] 配置(最终 DataTableSpec[] inSpecs)
        抛出 InvalidSettingsException {
    // 首先验证传入的数据表规范
    
    boolean hasNumericColumn = false;
    boolean containsName = false;
    for (int i = 0; i < inSpecs[IN_PORT].getNumColumns(); i++) {
        DataColumnSpec columnSpec = inSpecs[IN_PORT].getColumnSpec(i);
        // 我们只能使用它,如果它至少包含一个 
        // 数字列
        如果 (columnSpec.getType().isCompatible(DoubleValue.class)) {
            // 找到一个数字列
            hasNumericColumn = true;
        }
        // 如果设置了列名,它必须包含在数据中 
        // 表格规格
        如果 (m_column != null 
                && columnSpec.getName().equals(m_column.getStringValue())) {
            containsName = true;
        }
        
    }
    如果(!hasNumericColumn){
        throw new InvalidSettingsException("输入表必须包含在" 
                + "最少一列数字");
    }
    
    如果(!包含名称){
        throw new InvalidSettingsException("输入表不包含" 
                + "column " + m_column.getStringValue() + " 。请(重新)配置 " 
                + "节点");
    }
    
    
    // 到目前为止,输入被检查并且算法可以与 
    // 传入数据
    ...

正如我们依赖传入的数据规范一样,后继节点也需要有关数据格式的信息,这些信息在执行后提供。出于这个原因,我们节点的输出规范也必须在配置方法中创建。

...
// 现在生成输出表规范, 
// 即指定这个节点的输出
DataColumnSpec newColumnSpec = createOutputColumnSpec();
// 和附加部分的 DataTableSpec
DataTableSpec appendSpec = new DataTableSpec(newColumnSpec);
// 因为它只是附加的,所以新的输出规范包含两个:
// 原始规范和附加规范
DataTableSpec outputSpec = new DataTableSpec(inSpecs[IN_PORT],
        附加规格);
返回新的 DataTableSpec[]{outputSpec};
...

由于必须在 configure 和 execute 方法中为新添加的列创建 DataColumnSpec,因此在单独的方法中提取用于创建 DataColumnSpec 的代码:

私有数据列规范 createOutputColumnSpec() {
    // 我们要添加一个带有 bin 编号的列 
    DataColumnSpecCreator colSpecCreator = new DataColumnSpecCreator(
            "Bin Number", IntCell.TYPE);
    // 如果我们知道 bin 的数量,我们也知道可能的数量
    // 该新列的值
    DataColumnDomainCreator domainCreator = new DataColumnDomainCreator(
            new IntCell(0), new IntCell(m_numberOfBins.getIntValue() - 1));
    // 并且可以将此域信息添加到输出规范中
    colSpecCreator.setDomain(domainCreator.createDomain());
    // 现在可以创建列规范
    DataColumnSpec newColumnSpec = colSpecCreator.createSpec();
    返回新列规范;
}

一旦完成并实施,就可以编写等距分箱的实际算法。对数据进行操作的算法必须放在 execute 方法中。在此示例中,只有一列附加到原始数据。为此,使用了所谓的 ColumnRearranger。它需要一个 CellFactory,它返回给定行的附加单元格。

    ...        
    // 实例化细胞工厂
    CellFactory cellFactory = new NumericBinnerCellFactory(
           createOutputColumnSpec(), splitPoints, colIndex);
    // 创建列重新排列器
    ColumnRearranger outputTable = new ColumnRearranger(
            inData[IN_PORT].getDataTableSpec());
    // 添加新列
    outputTable.append(cellFactory);
    ...

创建了 ColumnRearranger 后,它可以与输入表一起传输到 ExecutionContext 以创建一个 BufferedDataTable,该表由 execute 方法返回,即在输出端口提供。每个节点在 BufferedDataTable 中缓冲数据。为了避免相同数据的冗余缓冲,使用了 ColumnRearranger。通过这种方式,只有附加的列在我们的节点中被缓冲。这就是我们必须从 ExecutionContext 中检索 BufferedDataTable 的原因:

    ...
    // 并创建实际的输出表
    BufferedDataTable bufferedOutput = exec.createColumnRearrangeTable(
            inData[IN_PORT], outputTable, exec);
    // 把它返还
    返回新的 BufferedDataTable[]{bufferedOutput};	
    ...

为了 CellFactory 的目的,有必要实现一个 NumericBinnerCellFactory。这扩展了 SingleCellFactory 并且只实现了 getCell 方法。检查传递的行以找出哪个 bin 包含来自所选列的值。它将 bin 的编号作为 DataCell 返回。

/**
 * @see org.knime.core.data.container.SingleCellFactory#getCell(
 * org.knime.core.data.DataRow)
 */
@覆盖
public DataCell getCell(DataRow 行){
    DataCell currCell = row.getCell(m_colIndex);
	// 检查单元格是否有缺失值
    如果(currCell.isMissing()){
        返回 DataType.getMissingCell();
    }
    double currValue = ((DoubleValue)currCell).getDoubleValue();
    int binNr = 0;
    for (Double intervalBound : m_intervalUpperBounds) {
        如果(currValue <= intervalBound){
            返回新的 IntCell(binNr);
        }
        binNr++;
    }
    返回 DataType.getMissingCell();
}

节点对话框:
创建 NumericBinnerNodeDialog 后,您将看到构造函数已包含一些示例代码。您可以删除它并添加所需控制元素的代码。对于 NumericBinnerNodeDialog,我们需要两个 GUI 元素:一个用于设置 bin 的数量,另一个用于选择 binning 的列。KNIME 框架提供了一个非常方便的设置来将标准对话框元素应用到 NodeDialog。因此,您的 NumericBinnerNodeDialog 默认扩展 DefaultNodeSettingsPane。如果默认对话框组件不适合您的需要,例如,如果某些组件应该根据用户的设置启用或禁用,您可以直接扩展 NodeDialogPane。在我们的例子中,需要添加一个表示 bin 数量的 DialogComponentNumber 和一个 DialogComponentColumnSelection。每个组件’ s 构造函数需要一个 SettingsModel 的新实例。SettingsModel 需要一个字符串标识符,它用于存储和加载组件的值,以及一个默认值,它在加载新值之前一直保持。额外的参数是必要的,这取决于组件的类型。从设置加载和保存到设置是通过构造函数中传递的键自动执行的。我们建议使用 NodeModel 中定义的键。如果你这样做,你必须在此时公开它。从设置加载和保存到设置是通过构造函数中传递的键自动执行的。我们建议使用 NodeModel 中定义的键。如果你这样做,你必须在此时公开它。从设置加载和保存到设置是通过构造函数中传递的键自动执行的。我们建议使用 NodeModel 中定义的键。如果你这样做,你必须在此时公开它。

公共类 NumericBinnerNodeDialog 扩展了 DefaultNodeSettingsPane {

/**
 * 用于配置 NumericBinner 节点对话框的新窗格。
 * 包含用于调整 bin 数量的控制元素 
 * 并选择要合并的列。
 * 在这里禁止警告:这是不可避免的,因为 
 * 允许类型作为泛型数组传递。 
 */
@SuppressWarnings(“未选中”)
受保护的 NumericBinnerNodeDialog() {
    极好的();
    // bins 控制元素的数量
    addDialogComponent(新的DialogComponentNumber(
            新的 SettingsModelIntegerBounded(
                NumericBinnerNodeModel.CFGKEY_NR_OF_BINS,
                NumericBinnerNodeModel.DEFAULT_NR_OF_BINS,
                1, Integer.MAX_VALUE),
                "箱数:", /*step*/ 1));
    // 列到 bin
    添加DialogComponent(新的DialogComponentColumnNameSelection(
            新的设置模型字符串(
                NumericBinnerNodeModel.CFGKEY_COLUMN_NAME,
                "选择一列"),
                "选择要装箱的列",
                NumericBinnerNodeModel.IN_PORT,
                DoubleValue.class));                    
}

}
创建节点并实现 NodeModel 和 NodeDialog 后,不要忘记编辑 XML 文件中的节点描述(与 NodeFactory 同名)。描述您的节点、对话框设置、输入和输出端口以及稍后的视图。这在第 8 节中有详细解释

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值