码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-07-21 16:25:13      阅读:433      评论:0      收藏:0      [点我收藏+]

标签:weka   csvloader   加载文本数据   stringtowordvector   

简介:

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数据集进行分类或聚类

 


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

标签:weka   csvloader   加载文本数据   stringtowordvector   

原文地址:http://blog.csdn.net/acema/article/details/38018443

踩
(0)
赞
(0)
   
举报
评论 一句话评论(0)
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!