WEKA学习——CSVLoader 实例训练 和 源码分析

简介:

Weka支持多种数据导入方式,CSVLoader是能从csv文件加载数据集,也可以保存为arff格式文件。官方介绍文件:Converting CSV to ARFF (

http://weka.wikispaces.com/Converting+CSV+to+ARFF)

CSVLoader加载文件,关键是对文件字段属性名称和属性的类型需要自己定义,这样才能得到满足自己需要的数据集。

CSVLoader通过options设置,可以设置每一列的属性为Nominal,String,Date类型。如"-N 1-2 -S 3 -D 4" Options 就是将属性1-2设置为Nominal类型,属性3设置为String类型,属性4设置为Date类型。

实例训练:

  • 准备数据

(第一个字段为序列号,第二个字段为分词后的结果,格式为csv格式,utf-8编码):

  1. 110112006582760,修理 水泵 安装 制冷 设备 工程 和 技术研究 与 试验 发展 技术开发
  2. 110108003557082,销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外
  3. 110107000885559,技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备
  4. 110109002641736,汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件
  5. 110102000765431,技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品
  6. 110109004903736,建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品
  7. 110108003533570,计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售
  8. 110101000171791,软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询
  9. 110108000938562,不含 中介 服务 劳务 服务 销售 五金交电 电子计算机 百货 汽车配件

  • 目的:

1. 转换为想要的arff格式文件,并保存

2. 利用Filter中的StringToWordVector对其进行过滤,方便后面根据TFIDF对文件进行分类聚类。

  • 实验代码:

  1. public static void main(String[] args) throws Exception {
  2. String filename = "datasets/companies.csv";
  3. String savearff = "datasets/companies.arff";
  4. CSVLoader loader = new CSVLoader();
  5. loader.setSource(new File(filename));
  6. // 在这里才能设置你读取的那个字段是String,而不是nominal
  7. loader.setStringAttributes("2"); // from 1
  8. loader.setNominalAttributes("1");
  9. Instances datasrc = loader.getDataSet();
  10.                 datasrc.renameAttribute(0, "regId");// rename attribu
  11. datasrc.renameAttribute(1, "text");
  12. datasrc.setClassIndex(0);
  13. // dataRaw.setRelationName(newName); //这里可以设置relationName
  14. //这里可以输出读取后Instances的结构信息,当然自己还可以数去其他信息
  15. //System.out.println(datasrc.stringFreeStructure());
  16. // save ARFF
  17. ArffSaver saver = new ArffSaver();
  18. saver.setInstances(datasrc);
  19. saver.setFile(new File(savearff));
  20. // saver.setDestination(new File(args[1]));
  21. saver.writeBatch();
  22. }

这里读取到的Instances和保存的Instances都是一致的,数据文件如下:

  1. @relation companies
  2. @attribute regId {1.10108003557082E14,1.10107000885559E14,1.10109002641736E14,1.10102000765431E14,1.10109004903736E14,1.1010800353357E14,1.10101000171791E14,1.10108000938562E14}
  3. @attribute text string
  4. @data
  5. 1.10108003557082E14,‘销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外 ‘
  6. 1.10107000885559E14,‘技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备 ‘
  7. 1.10109002641736E14,‘汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件 ‘
  8. 1.10102000765431E14,‘技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品 ‘
  9. 1.10109004903736E14,‘建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品 ‘
  10. 1.1010800353357E14,‘计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售‘
  11. 1.10101000171791E14,‘软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询 ‘
  12. 1.10108000938562E14,‘不含 中介 服务 劳务 服务 销售 五金交电 电子计算机 百货 汽车配件 ‘

这里需要注意两个地方:

1. regId 为nominal,所以attribute后面列出了所有可能值。但是原数据id 110112006582760比较大,采用了科学技术法来保存double数据,所以出现了以下情况。但是这样并不影响数据分析和后续处理,两个不同的id数字,他们的科学表示法一定不一样。(具体原因和解决办法,见下面的源码分析)

2.第二个字段设置名字为text,类型为string。 对于其他date日期类型,自己也可以根据CSVLoader的setDateAttributes(String value)和setDateFormat(String value);两个方法来设定。

对于第一个注意的地方,我们可以通过对源码进行修改,然后得到修正。

CSVLoader源码分析:

CSVLoader代码加载文件的流程主要在getDataSet()函数里面,一般是先设置参数,然后执行getDataSet()函数。

CSVLoader首先会判断参数设置,根据首行数据判断每个字段的类型。然后执行getInstance(StreamTokenizer tokenizer) 函数一行行加载数据。下面只对getInstance函数进行简要分析:

/**

* Attempts to parse a line of the data set.

* 这个类是对每一行进行解析,首先看每个字段是否转换为double,否则转换为string

* 注意其中注释部分 //
Ma:

*/

private FastVector getInstance(StreamTokenizer tokenizer) throws IOException {

FastVector current = new FastVector();

// Check if end of file reached.

ConverterUtils.getFirstToken(tokenizer);

if (tokenizer.ttype == StreamTokenizer.TT_EOF) {

return null;

}

boolean first = true;

boolean wasSep;

while (tokenizer.ttype != StreamTokenizer.TT_EOL && tokenizer.ttype != StreamTokenizer.TT_EOF) {

// Get next token

if (!first) {

ConverterUtils.getToken(tokenizer);

}

if (tokenizer.ttype == ‘,‘ || tokenizer.ttype == ‘\t‘ || tokenizer.ttype == StreamTokenizer.TT_EOL) {

current.addElement(m_MissingValue);

wasSep = true;

else {

wasSep = false;

if (tokenizer.sval.equals(m_MissingValue)) {

current.addElement(new String(m_MissingValue));

else {

// try to parse as a number

try {

// Ma:

// 注意这行代码总会将“大数”进行科学技术法表示,

// 如:110112006582760表示成:1.10108003557082E14。

// Ma:但有时我们的"大数"一般指注册号什么的,应该是nominal,没有大小之分

// 所以这里可以修改,让Double完整显示

//double val = Double.valueOf(tokenizer.sval).doubleValue();
//注释掉

//current.addElement(new Double(val));   // 注释掉

// 按如下修改,会把所有的numeric字段修改为nominal or string

current.addElement(new String(tokenizer.sval)); 
//修改添加

catch (NumberFormatException e) {

// otherwise assume its an enumerated value

current.addElement(new String(tokenizer.sval));

}

}

}

if (!wasSep) {

ConverterUtils.getToken(tokenizer);

}

first = false;

}

// check number of values read

if (current.size() != m_structure.numAttributes()) {

ConverterUtils.errms(tokenizer, "wrong number of values. Read " + current.size() + ", expected "

+ m_structure.numAttributes());

}

// check for structure update

try {

checkStructure(current);

catch (Exception ex) {

ex.printStackTrace();

}

return current;

}

根据上面的源代码,对其中的阴影部分进行修改,这样CSVLoader就不会对长数字进行转换。然后import修改后的CSVLoader,在执行上面的代码,得到如下的数据结果:

  1. @relation companies
  2. @attribute regId {110108003557082,110107000885559,110109002641736,110102000765431,110109004903736,110108003533570,110101000171791,110108000938562}
  3. @attribute text string
  4. @data
  5. 110108003557082,‘销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外 ‘
  6. 110107000885559,‘技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备 ‘
  7. 110109002641736,‘汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件 ‘
  8. 110102000765431,‘技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品 ‘
  9. 110109004903736,‘建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品 ‘
  10. 110108003533570,‘计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售‘
  11. 110101000171791,‘软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询 ‘
  12. 110108000938562,‘不含 中介 服务 劳务 服务 销售 五金交电 电子计算机 百货 汽车配件 ‘

但这样会有一个问题,若数据还有其他numeric属性字段,同样也会处理成nomial。只要知道是这么部分代码其的作用,并不一定需要这样修改,这样的内部表示挺好的,只需在输出文件时定义显示格式就好了。如:

  1. DecimalFormat df = new DecimalFormat("0.0");//保留一位小数
  2. String str="1.10108003557082E14";
  3. double val=Double.valueOf(str);
  4. System.out.println(val); //默认科学表示法输出
  5. System.out.println(df.format(val));//采用指定格式输出

Weka内部为什么没有这么做,可能是因为他不知道具体要保存什么的格式,就以科学计数法给保存了。

这里还有一个简单的方法,若里面还有其他numerical字段,可以将想要处理为nominal的数字字段,前面加上几个字符,对于本例可以将第一个序列号的字段前面都加id(这样就会抛出NumberFormatException),得到如下数据格式,这样CSVLoader就能正常处理,你也能得到你想要的结果了。

  1. @relation companies
  2. @attribute regId {id110108003557082,id110107000885559,id110109002641736,id110102000765431,id110109004903736,id110108003533570,id110101000171791,id110108000938562}
  3. @attribute text string
  4. @data
  5. id110108003557082,‘销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外 ‘
  6. id110107000885559,‘技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备 ‘
  7. id110109002641736,‘汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件 ‘
  8. id110102000765431,‘技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品 ‘
  9. id110109004903736,‘建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品 ‘
  10. id110108003533570,‘计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售‘
  11. id110101000171791,‘软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询 ‘
  12. id110108000938562,‘不含 中介 服务 劳务 服务 销售 五金交电 电子计算机 百货 汽车配件 ‘

拓展知识:

  • Instances实例集 Attributes类型修改

对已经处理好的Instances进行属性类型的改变(通过Filter),如下面例子:

import weka.filters.unsupervised.attribute.NumericToNominal;

NumericToNominal num2Nominal=new NumericToNominal();

num2Nominal.setInputFormat(datasrc);

num2Nominal.setAttributeIndices("1");

Instances dataFiltered = Filter.useFilter(datasrc, filter);

当然还有NominalToString这样的Filter类,用法类似。

  • StringToWordVector使用

利用StringToWordVector可以计算每个文本的TF or TFIDF值,然后在计算不同文本见的(余弦orJacobi)相似性。最后可以用来文本的分类聚类。

  1. /* ......紧接最上面的代码 */
  2. /* filter 分词后的 string */
  3. StringToWordVector filter = new StringToWordVector();
  4. filter.setInputFormat(datasrc);
  5. // String stopwordfile = "datasets/stopwords.en";
  6. // filter.setStopwords(new File(stopwordfile));
  7. // filter.setUseStoplist(true);
  8. String optionStr = "-R first-last -W 1000 -prune-rate -1.0 -C -I -N 1";
  9. filter.setOptions(Utils.splitOptions(optionStr));
  10. Instances dataFiltered = Filter.useFilter(datasrc, filter);
  11. //....接下来可以对dataFilered数据集进行分类或聚类
时间: 2025-01-18 10:27:21

WEKA学习——CSVLoader 实例训练 和 源码分析的相关文章

Java显式锁学习总结之五:ReentrantReadWriteLock源码分析

概述 我们在介绍AbstractQueuedSynchronizer的时候介绍过,AQS支持独占式同步状态获取/释放.共享式同步状态获取/释放两种模式,对应的典型应用分别是ReentrantLock和Semaphore,AQS还可以混合两种模式使用,读写锁ReentrantReadWriteLock就是如此. 设想以下情景:我们在系统中有一个多线程访问的缓存,多个线程都可以对缓存进行读或写操作,但是读操作远远多于写操作,要求写操作要线程安全,且写操作执行完成要求对当前的所有读操作马上可见. 分析

Redis学习之intset整数集合源码分析

1.整数集合:整数的集合,升序排序,无重复元素 2.整数集合intset是集合键的底层实现之一,当一个集合只包含整数值的元素,并且这个集合的元素数量不多时,redis会使用整数集合作为集合键的底层实现 3.intset可通过属性自定义编码方式(int16_t/int32_t/int64_t) 4.当往inset插入新元素时,如果新元素的类型比inset的原编码类型长,那么要先对inset进行"升级"操作 5.升级操作:(不可逆操作) 1)根据新元素的类型,扩展intset底层数组的空间

Android学习——涉及意识形态的LitePal源码分析

原创技术博客,请认准Azzssss的原文http://www.cnblogs.com/Azzssss/p/4147704.html. 这两天项目终于上线了,松了一口气,虽然还是很不稳定,见一步走一步吧.反正总算能抽出时间来写博客了.在项目中用到了LitePal,LitePal是什么鬼东西呢?它的Github主页是什么介绍的:“LitePal是一个开源的android库,允许开发者极其方便地去使用sqlite数据库.通过它,你甚至可以不写一行SQL语句来完成大部分的数据库操作,包括创建和更新表,c

Junit学习笔记(二): 源码分析(1)

使用ModelGoon画出来的UML图如下: 图中可以分析出: 1)Test是一个接口,TestSuit和TestCase,JUnit4TestAdapter,JUnit4TestCaseFacade都实现了Test类,这一种命令模式,只需要使用Test接口就可以使用到具体的类: 2)JUnit4TestAdapter是适配器, Filterable实现了对用例的过滤, Sortable实现了对用例的排序(按照字母进行排序): 3)TestResult实现了返回结果,TestFailure记录失

Junit学习笔记(二): 源码分析(2)-命令和组合模式

命令模式 命令模式的优点: 命令模式将调用操作的对象与如何实现该操作的对象解耦. 将命令当成一个头等对象,它们可以像一般对象那样进行操纵和扩展 可以将多个命令复合成一个命令,与Composite模式结合使用 增加新的命令很容易,隔离对现有类的影响 可以与备忘录模式配合,实现撤销功能. 命令模式图: 由此带来的好处:1.客户无需使用任何条件语句去判断测试的类型,可以用统一的方式调用测试和测试套件,解除了客户与具体测试子类的耦合2.如果要增加新的TestCase也很容易,实现Test接口即可,不会影

JStorm与Storm源码分析(七)--BasicBoltExecutor与装饰模式

在Storm中IBasicBolt的主要作用是为用户提供一种更为简单的Bolt编写方式,更为简单体现在Storm框架本身帮你处理了所发出消息的Ack.Fail和Anchor操作,而这部分操作是由执行器BasicBoltExecutor 实现的. 下面我们看一下BasicBoltExecutor的源码: /** * BasicBoltExecutor实现了IRichBolt接口 * 在该类中持有一个IBasicBolt成员变量用于调用转发 * 说明: * 该类是基于装饰模式实现的. */ publ

【转】ArrayList源码分析

java源码分析之ArrayList 标签: ArrayListarraylistjavaJavaJAVA 2013-01-25 08:52 8612人阅读 评论(7) 收藏 举报 分类: java(35) 版权声明:本文为博主原创文章,未经博主允许不得转载. ArrayList就是传说中的动态数组,就是Array的复杂版本,它提供了如下一些好处:动态的增加和减少元素.灵活的设置数组的大小...... 认真阅读本文,我相信一定会对你有帮助.比如为什么ArrayList里面提供了一个受保护的rem

集合类学习之Arraylist 源码分析

1.概述 ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小. 每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小.它总是至少等于列表的大小(如果不指定capacity,默认是10).    /**      * Constructs an empty list with an initial capacity of ten.

Android 上千实例源码分析以及开源分析

Android 上千实例源码分析以及开源分析(百度云分享) 要下载的直接翻到最后吧,项目实例有点多. 首先 介绍几本书籍(下载包中)吧. 01_Android系统概述 02_Android系统的开发综述 03_Android的Linux内核与驱动程序 04_Android的底层库和程序 05_Android的JAVA虚拟机和JAVA环境 06_Android的GUI系统 07_Android的Audio系统 08_Android的Video 输入输出系统 09_Android的多媒体系统 10_