我有一个CSV文件,货币兑换欧元兑美元。该文件是从Bank of Canada下载的。我从2013年10月10日开始下载CSV数据。
然而,数据存在差距,即。没有转换率的天数。
我一直在与Spoon Kettle战斗(第一天),找出一种简单(但一般)的方法来填补空白,比如,使用最后一个非空值。我设法完成此任务的唯一方法是链接4“获取前一行字段”并使用计算器中的NVL获取第一个非空值。但这只有在间隙不超过流中的4行时才有效。
图像代表转型:
我的第一个问题简化为:是否有通用的方法在带间隙的流中进行插值/外推?
我尝试使用“修改过的JavaScript值”,但API仍然让我感到厌烦。此外,似乎这一步只有MapReduce组合的Map部分,我可能需要两者。
所以,我的第二个问题是:有没有办法用非Java语言(Scala,Clojure,Jython或JS)编写MapReduce组合?
答案 0 :(得分:8)
您可以使用以下三个步骤的组合:
1)分析查询 - 允许您在当前行之前或之后获取字段N行的值;在您的情况下,您将需要提前1行(下一个可用日期)
2)计算器 - 确定了行的上一个日期,用它来计算日期之间的天数;
3)将字段number_of_clones计算为dbd-1(缺少的天数;
4)在Clone Rows步骤中使用该字段,根据需要多次连续多次;添加clone_number字段
5)将clone_number添加为日期的天数,然后获得它所引用的日期。
此外,分析查询步骤允许您通过"将字段指定为"组。字段,这样如果你有美元的x费率,然后你有英镑的x费率,最后的美元x利率日将检索空值作为下一个值。
这是一个示例KTR文件:
数据网格步骤会生成几行,其中包含一些数据空白:
Analytical查询获取相同货币值的下一个日期
然后计算器步骤计算缺少的行数。请注意,每种货币的最后一天将以null
作为值,因此我们需要调整它并使用0代替(如果A为空,则NVL(A,B)返回B,否则返回A)
克隆行:占用一行并创建副本。
clone_number字段允许我们计算行引用的实际日期
最后,这是数据。您想要的字段是new_date,currency和exchange_rate。使用选择值重新排序字段列表,并删除您不再需要的字段。
如您所见,现在我们使用之前的已知值获得2014-01-03和2014-01-04的数据。
答案 1 :(得分:1)
虽然这不是您要求的,但您可以通过使用具有通用功能的用户定义的Java类组件来实现您的目标。通过此组件的单个实例替换您的步骤Get previous row fields
到Non-values in row
。在此组件的 Classes - Processor 部分中插入以下代码:
Object[] previousRow;
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
{
// First, get a row from the default input hop
Object[] r = getRow();
// If the row object is null, we are done processing.
if (r == null) {
setOutputDone();
return false;
}
// It is always safest to call createOutputRow() to ensure that your output row's Object[] is large
// enough to handle any new fields you are creating in this step.
Object[] outputRow = createOutputRow(r, data.outputRowMeta.size());
// copy all input fields to the output fields
for (int i=0; i < getInputRowMeta().size(); i++) {
logBasic(data.inputRowMeta.getString(r, i));
if (data.inputRowMeta.getString(r, i) == null && (previousRow != null)) {
// if the current field is empty take it from the previous row
outputRow[i] = previousRow[i];
}
else {
// otherwise use the current row
outputRow[i] = r[i];
}
}
putRow(data.outputRowMeta, outputRow);
// store the current row as future previous row
previousRow = data.outputRowMeta.cloneRow(outputRow);
return true;
}
Janino类始终保留上一行的副本以填充当前行的空字段。
以下测试设置演示了组件的使用。在最简单的情况下,我们处理从CSV文件读取的流:
输入文件配置如下:
并包含以下数据
NUMBER;STRING;DATE;CURRENCY
1;A;01.02.2014;12,5
2;B;;13,5
;;03.12.2001;
4;;;
5;C;;
6;;20.03.2005;18,2
7;D;;
用户定义的Java类组件的配置如下:
输出文本文件包含增强行“无间隙”:
NUMBER;STRING;DATE;CURRENCY
1;A;01.02.2014; 012,50
2;B;01.02.2014; 013,50
2;B;03.12.2001; 013,50
4;B;03.12.2001; 013,50
5;C;03.12.2001; 013,50
6;C;20.03.2005; 018,20
7;D;20.03.2005; 018,20
注意:
null
时,该机制才有效。只包含空格的字符串可能会破坏它,因此请确保在将所有字符串输送到组件之前修剪它们。代码是使用http://wiki.pentaho.com/display/EAI/User+Defined+Java+Class作为教程编写的。
<强>附录强>
@manu提供的链接包含以下代码。它包含对数字格式的特定处理。请注意,它不再是完全通用的。
Object[] previousRow;
RowMetaInterface outputMeta;
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
{
// First, get a row from the default input hop
Object[] r = getRow();
// If the row object is null, we are done processing.
if (r == null) {
setOutputDone();
return false;
}
if (outputMeta == null) {
outputMeta = data.outputRowMeta.clone();
for(int i=0; i < outputMeta.size(); i++) {
ValueMetaInterface meta = outputMeta.getValueMeta(i);
if (meta.getType() == ValueMetaInterface.TYPE_NUMBER) {
meta.setPrecision(4);
meta.setConversionMask("#.####");
}
}
}
// It is always safest to call createOutputRow() to ensure that your output row's Object[] is large
// enough to handle any new fields you are creating in this step.
Object[] outputRow = createOutputRow(r, data.outputRowMeta.size());
// copy all input fields to the output fields
for (int i=0; i < getInputRowMeta().size(); i++) {
if ((r[i] == null) && (previousRow != null)) {
// if the current field is empty take it from the previous row
outputRow[i] = previousRow[i];
}
else {
// otherwise use the current row
outputRow[i] = r[i];
}
}
putRow(outputMeta, outputRow);
// store the current row as future previous row
previousRow = outputMeta.cloneRow(outputRow);
return true;
}