DataCleaner---19.1 扩展开发教程

19.1 扩展开发教程

        对于那些从事开发扩展的人来说,有很多有用的资源。(插件/附加组件)到DataCleaner。为了帮助你,这里有一个有用的拓展开发。如果您认为此开发还不够,请告知我们:

===================================================================
教程1:开发转换器transformer

(2010/09/07)
使用DataCleaner Java API开发值转换器

        在这篇博客文章中,我将演示DataCleaner的Java Api来创建转换器,即用于基于数据集的现有值进行转换/可变/标记化/生成新值的组件。您将需要Java编程技能来学习本教程。
我发现解释这个过程最简单的方法就是运行一个例子。所以这里是我的例子:我想把人的出生日期(表示为Date 字段)转换为年龄字段(表示为number 字段)。场景描述如下:
DateToAgeTransformer
转换后,我将能够独立处理年龄字段,例如,通过数字分析、值分布或应用一些依赖于年龄的业务规则。
构建转换器transformer 的要求如下:

  • 该类必须要实现Transformer接口
  • 该类上面必须用 (javax.inject)@Named注解。该注解接受一个参数:转换器的可读名称。我们将这样注释:@Named(“Date to age”)
  • 为了从传入字段中读取数据,我们需要注入一个 InputColumn<E> 实例(或者一个数组),其中<E>是传入字段的数据类型。为了注入,我们使用@Configured注释。在我们的示例中,这转换为:@Configured InputColumn<Date> dateColumn;

在这些步骤之后,我们的代码将如下所示:

@Named("Date to age")//就是转换器的名字
public class DateToAgeTransformer implements Transformer {

  @Configured
  InputColumn<Date> dateColumn;//从数据表中传入的字段数据

  @Override
  public OutputColumns getOutputColumns() {
    // TODO
    return null;
  }

  @Override
  public Object[] transform(InputRow inputRow) {
    // TODO
    return null;
  }
}

如我们所见,Transformer接口定义了两种方法,我们需要实现它们。他们是:

  • getOutputColumns(): 框架调用此方法以确定转换器将生成哪些虚拟列。在我们的例子中,它非常简单:transformer为age创建虚拟列(以天为单位和以年为单位,只是为了使它更灵活)。因此,方法主体应该是:
return new OutputColumns(Integer.class, "Age in days", "Age in years");
  • transform(InputRow): 对于每一行要转换的值,都将调用此方法。方法的返回类型是表示行的新值的对象数组。返回数组的索引应与输出列匹配,即索引0表示“以天为单位的年限”,索引1表示“以年为单位的年限”。让我们看一下方法实现:
Integer[] result = new Integer[2];
Date date = inputRow.getValue(dateColumn);

if (date != null) {
  long diffMillis = today.getTime() - date.getTime();
  int diffDays = (int) (diffMillis / (1000 * 60 * 60 * 24));

  result[0] = diffDays;

  // use Joda time to easily calculate the diff in years
  int diffYears = Years.yearsBetween(new DateTime(date), new DateTime(today)).getYears();
   result[1] = diffYears;
}

return result;

        当然,在编写本教程的过程中,我并没有在没有嵌入代码的情况下完成所有的工作,这样您就可以实际使用它了。这里提供了 ”Date to age” 转换器的代码,这里还有一个单元测试,可以用来演示如何对转换器进行单元测试。我希望你们中的一些人参与开发转换器,让我知道结果如何。在我的下一篇博客文章中,我将解释如何构建分析器,这是为DataCleaner开发组件时显而易见的下一步。
这里还有其他一些转换器的好例子:

  • 转换为日期转换器,它将尝试将任何值转换为日期。这对于我刚刚在本教程中解释的转换器来说可能很有用。换句话说:如果要转换的出生日期存储在基于字符串的字段中,那么这两个转换器可能需要链接。
  • 标记器转换器,因为它具有基于用户配置的灵活数量的输出列。请注意该例子中用到的的 @Configured Integer numTokens变量。





===================================================================
教程2:开发分析器analyzer

(2010/09/26)
使用 AnalyzerBeans Java API 开发分析器

      上文已经写了如何开发一个转换器。现在是时候来看看如何开发一个分析器了,它是一个用来消耗数据并将其转换成一个可供人阅读的结果的组件,希望它是有用的。Java API的Javadocs位于这里。AnalyzerBeans 中已经有很多不同的分析器,当您决定开发自己的分析器时,不妨看看这些分析器:

  • 对于典型的度量,有数字分析器(Number analyzer)和字符串分析器(String analyzer)之类的分析器。这些分析器计算这些数据类型的标准化度量。
  • 有一个值分布分析器(Value distribution)很有趣,因为它使用一个后备数据库(使用 @Provided注释)来计算唯一值,如果值超过了可用内存量。
  • 日期间隔分析器(Date gap analyzer)也是一个很好的例子,因为它命名了输入列,用于构建从日期到日期的时间线。
  • 模式查找器(PatternFinder)分析器,你可以在我以前的一篇博文中读到更多。

      让我们从一个简单的例子开始。假设您想构建一个非常简单的分析器,它使用基于日期date 或时间time 的值,并根据星期几确定值的分布(即,如何将值的分布分组在星期一、星期二、星期三等)。虽然这是一个相当幼稚的分析器示例,但它也能很好地工作作为一个示例。
我们将从构建分析器(analyzer)的要求开始:

  • 您需要定义一个实现 Analyzer<R>的类。泛型 ‘R’ 参数定义了分析器的结果类型。我们可以重用内置的结果类型,也可以编写自己的结果类型。
  • 该类需要用@AnalyzerBean注解。此注解接受一个参数:分析器的显示名称。
  • 您需要使用@Configured注释注入一个或多个InputColumn<E>,以便使用传入的数据。<E>参数定义感兴趣的数据类型,它还用于确定分析器支持哪些类型的数据类型。在本例中,我们将使用Date作为InputColumn类型,因为我们希望分析器使用日期值。

因此我们的类是按照上述要求创建的:

@AnalyzerBean("Average date analyzer")
public class AverageDateAnalyzer implements Analyzer<CrosstabResult> {

      @Configured
      InputColumn<Date> dateColumn;

      public void run(InputRow row, int distinctCount) { ... }
      public CrosstabResult getResult() { ... }
}

请注意,我们使用的是内置的结果类型CrosstabResult,它表示由维度交叉表组成的结果。我们可以使用其他内置的结果类型,也可以创建自己的结果类型,唯一的要求是它得实现AnalyzerResult接口。

分析器的其余部分应该是“熟知的Java”,但当然也要使用AnalyzerBeans中提供的API。这些事我以前都解释过,但我会再解释一遍。

所以现在来考虑如何实现具体的分析器逻辑。我们将使用常规 map 来保存分布值。我们将把工作日的数字映射到这个 map 上并计数。但我们需要为正在分析的每一列保留一个计数,因此它将是一个嵌套map:

private Map<InputColumn<Date>, Map<Integer, Integer>> distributionMap;

要初始化映射,我们需要先注入InputColumn。相反,我们可以用在方法上添加 @Initialize 注解,这将使AnalyzerBeans在bean被正确初始化时调用该方法。

@Initialize
public void init() {
  distributionMap = new HashMap<InputColumn<Date>, Map<Integer, Integer>>();
  for (InputColumn<Date> col : dateColumns) {
    Map<Integer, Integer> countMap = new HashMap<Integer, Integer>(7);
    for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
      // put a count of 0 for each day of the week
      countMap.put(i, 0);
    }
    distributionMap.put(col, countMap);
  }
}

现在 map 已经初始化,我们可以继续实现run(…)方法:

@Override
public void run(InputRow row, int distinctCount) {
  for (InputColumn<Date> col : dateColumns) {
    Date value = row.getValue(col);
    if (value != null) {
      Calendar c = Calendar.getInstance();
      c.setTime(value);
      int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
      Map<Integer, Integer> countMap = distributionMap.get(col);
      int count = countMap.get(dayOfWeek);
      count += distinctCount;
      countMap.put(dayOfWeek, count);
    }
  }
}

这应该相当于“Java日常使用”。如果您是一名经验丰富的Java开发人员,那么您唯一应该了解的是使用InputColumns作为限定符从InputRow提取值的方法:

Date value = row.getValue(col);

请注意,value变量具有日期Date 类型。AnalyzerBeans API 在很大程度上利用了类型安全性。由于注入的InputColumn被定义为日期列,这意味着我们可以安全大胆地假设传入行中的值一定是日期类型。此外,日期列将用于验证AnalyzerBeans job 的配置,如果用户尝试使用非日期列配置该特定Analyzer,则会向用户发送早期错误消息。现在开始创建结果。如前所述,我们将为此使用交叉表结果CrosstabResult 。交叉表结果是一种非常动态的结果类型,可以用于许多目的。它类似于DataCleaners结果矩阵,但增加了一些特性。下面是我们如何该分析器的结果交叉表:

@Override
public CrosstabResult getResult() {
  CrosstabDimension columnDimension = new CrosstabDimension("Column");
  CrosstabDimension weekdayDimension = new CrosstabDimension("Weekday");
  weekdayDimension.addCategory("Sunday").addCategory("Monday")
    .addCategory("Tuesday").addCategory("Wednesday").addCategory("Thursday")
    .addCategory("Friday").addCategory("Saturday");

  Crosstab crosstab = new Crosstab(Integer.class, columnDimension, weekdayDimension);
  for (InputColumn col : dateColumns) {
    columnDimension.addCategory(col.getName());
    CrosstabNavigator nav = crosstab.where(columnDimension, col.getName());
    Map countMap = distributionMap.get(col);
    nav.where(weekdayDimension, "Sunday").put(countMap.get(Calendar.SUNDAY));
    nav.where(weekdayDimension, "Monday").put(countMap.get(Calendar.MONDAY));
    nav.where(weekdayDimension, "Tuesday").put(countMap.get(Calendar.TUESDAY));
    nav.where(weekdayDimension, "Wednesday").put(countMap.get(Calendar.WEDNESDAY));
    nav.where(weekdayDimension, "Thursday").put(countMap.get(Calendar.THURSDAY));
    nav.where(weekdayDimension, "Friday").put(countMap.get(Calendar.FRIDAY));
    nav.where(weekdayDimension, "Saturday").put(countMap.get(Calendar.SATURDAY));
  }
  return new CrosstabResult(getClass(), crosstab);
}

现在我们结束了。你可以看看这里的最终结果。当我用三列中的小数据样本运行此分析器时,结果如下所示:

             Order date Shipment date Delivery date
Sunday                0             0             0
Monday                2             0             1
Tuesday               0             2             1
Wednesday             0             0             0
Thursday              1             0             0
Friday                1             1             2
Saturday              0             1             0

你也可以在这里查看这个分析器的单元测试。(网页失效了,但源代码中有)



===================================================================
教程3:实现自定义数据存储

(2012/04/09)
在DataCleaner中实现自定义数据存储

      DataCleaner的超级用户、合作伙伴和开发人员经常问我一个问题:如何在DataCleaner中为我的system/file-format XYZ构建自定义数据存储?最近,我处理这个问题是为了在即将到来的与Pentaho-Kettle的集成中使用,对于一个拥有自己开发的数据库代理系统的人工接口客户,就在今天,当它在DataCleaner论坛上被询问时。在这篇博文中,我将指导您完成这个过程,这需要一些基本的Java编程技能,但如果这一点已经到位的话,它并不十分复杂。
      首先,我应该说(对于那些喜欢“只看代码”的人来说),在DataCleaner的源代码已经有了一个简单示例( sample extension)。看一看这个 org.eobjects.datacleaner.sample.SampleDatastore 类。一旦您阅读、理解并编译了Java代码,您所需要做的就是在DataCleaner中的conf.xml文件 注册数据存储 (在< datastore-catalog >便签中):

<custom-datastore class-name="org.eobjects.datacleaner.sample.SampleDatastore">
  <property name="Name" value="My datastore" />
</custom-datastore>

工作原理???
      首先,DataCleaner中的数据存储需要实现 Datastore 接口。但是我建议使用名为UsageAwareDatastore的抽象实现,而不是直接实现接口。这个抽象实现处理对数据存储的并发访问,重用现有连接等等。在扩展UsageAwareDatastore类时,您仍然需要提供的主要是 createDatastoreConnection() 方法,该方法在请求新连接时被调用。让我们看看新建一个数据存储实现是什么样子的:

public class ExampleDatastore extends UsageAwareDatastore<DataContext> {

 private static final long serialVersionUID = 1L;
 
 public ExampleDatastore() {
  super("My datastore");
 }

 @Override
 protected UsageAwareDatastoreConnection createDatastoreConnection() {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public PerformanceCharacteristics getPerformanceCharacteristics() {
  // TODO Auto-generated method stub
  return null;
 }
}

注意,我已经创建了一个无参数构造函数。这对于自定义数据存储是必需的,因为数据存储将由DataCleaner实例化。稍后我们将重点讨论如何调整名称(“My datastore”)。
首先,我们来看看两个未实现的方法:

  • createDatastoreConnection() : c用于创建新连接。DataCleaner构建在元模型框架之上,用于数据访问。您需要返回一个新的DatastoreConnectionImpl(…)。该类接受一个重要参数,即MetaModel DataContext implementation。通常在给定的配置情况下,已经有了一个可以使用的DataContext,例如JdbcDataContext、CsvDataContext、ExcelDataContext、MongoDbDatacontext或其他什么。
  • getPerformanceCharacteristics(): DataCleaner在执行作业时使用该方法来确定查询计划。通常只返回一个新的PerformanceCharacteristics(false);,阅读javadoc了解更多信息



可参数化属性???
      到目前为止,您应该能够实现一个定制的数据存储,这有望满足您的基本需求。但是也许你想用不同的文件、不同的主机名等重用datastore类,换句话说:也许你想让你的用户定义datastore的某些属性。
拯救方法是@Configured注解,它是DataCleaner中广泛使用的注解。它允许您在类中注释应该由用户配置的字段。字段的类型可以是字符串、整数、文件等。让我们看看如何公开特有连接的属性:

public class ExampleDatastore extends UsageAwareDatastore<DataContext> {
 // ...

 @Configured
 String datastoreName;

 @Configured
 String hostname;

 @Configured
 Integer port;

 @Configured
 String systemId;

 // ...
}

以及通常如何使用它们来实现方法:

public class ExampleDatastore extends UsageAwareDatastore<DataContext> {
 // ...

 @Override
 public String getName() {
  return datastoreName;
 }

 @Override
 protected UsageAwareDatastoreConnection createDatastoreConnection() {
  DataContext dataContext = createDataContext(hostname, port, systemId);
  return new DatastoreConnectionImpl(dataContext, this);
 }
}

如果我想使用上面的参数配置配出一个数据存储,我可以在我的conf.xml格式文件如下:

<custom-datastore class-name="foo.bar.ExampleDatastore">
  <property name="Datastore name" value="My datastore" />
  <property name="Hostname" value="localhost" />
  <property name="Port" value="1234" />
  <property name="System id" value="foobar" />
</custom-datastore>

请注意,属性的名称是通过反转Java使用的camelCase表示法推断出来的,这样 “datastoreName” 就变成了 “Datastore name” ,以此类推。或者,您可以在 @Configured 注解中提供显式名称。

我希望这个介绍教程对你有意义。我再次敦促您查看示例Sample DataCleaner extension,它还包括构建设置(基于Maven)和自定义元模型DataContext实现。






点这儿返回DataCleaner文档主目录

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值