首页 > 代码库 > WEKA学习——CSVLoader 实例训练 和 源码分析
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编码):
110112006582760,修理 水泵 安装 制冷 设备 工程 和 技术研究 与 试验 发展 技术开发
110108003557082,销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外
110107000885559,技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备
110109002641736,汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件
110102000765431,技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品
110109004903736,建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品
110108003533570,计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售
110101000171791,软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询
110108000938562,不含 中介 服务 劳务 服务 销售 五金交电 电子计算机 百货 汽车配件
- 目的:
1. 转换为想要的arff格式文件,并保存
2. 利用Filter中的StringToWordVector对其进行过滤,方便后面根据TFIDF对文件进行分类聚类。
- 实验代码:
public static void main(String[] args) throws Exception {
String filename = "datasets/companies.csv";
String savearff = "datasets/companies.arff";
CSVLoader loader = new CSVLoader();
loader.setSource(new File(filename));
// 在这里才能设置你读取的那个字段是String,而不是nominal
loader.setStringAttributes("2"); // from 1
loader.setNominalAttributes("1");
Instances datasrc = loader.getDataSet();
datasrc.renameAttribute(0, "regId");// rename attribu
datasrc.renameAttribute(1, "text");
datasrc.setClassIndex(0);
- // dataRaw.setRelationName(newName); //这里可以设置relationName
//这里可以输出读取后Instances的结构信息,当然自己还可以数去其他信息
//System.out.println(datasrc.stringFreeStructure());
// save ARFF
ArffSaver saver = new ArffSaver();
saver.setInstances(datasrc);
saver.setFile(new File(savearff));
// saver.setDestination(new File(args[1]));
saver.writeBatch();
}
这里读取到的Instances和保存的Instances都是一致的,数据文件如下:
@relation companies
@attribute regId {1.10108003557082E14,1.10107000885559E14,1.10109002641736E14,1.10102000765431E14,1.10109004903736E14,1.1010800353357E14,1.10101000171791E14,1.10108000938562E14}
@attribute text string
@data
1.10108003557082E14,‘销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外 ‘
1.10107000885559E14,‘技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备 ‘
1.10109002641736E14,‘汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件 ‘
1.10102000765431E14,‘技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品 ‘
1.10109004903736E14,‘建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品 ‘
1.1010800353357E14,‘计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售‘
1.10101000171791E14,‘软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询 ‘
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,在执行上面的代码,得到如下的数据结果:
@relation companies
@attribute regId {110108003557082,110107000885559,110109002641736,110102000765431,110109004903736,110108003533570,110101000171791,110108000938562}
@attribute text string
@data
110108003557082,‘销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外 ‘
110107000885559,‘技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备 ‘
110109002641736,‘汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件 ‘
110102000765431,‘技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品 ‘
110109004903736,‘建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品 ‘
110108003533570,‘计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售‘
110101000171791,‘软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询 ‘
110108000938562,‘不含 中介 服务 劳务 服务 销售 五金交电 电子计算机 百货 汽车配件 ‘
但这样会有一个问题,若数据还有其他numeric属性字段,同样也会处理成nomial。只要知道是这么部分代码其的作用,并不一定需要这样修改,这样的内部表示挺好的,只需在输出文件时定义显示格式就好了。如:
DecimalFormat df = new DecimalFormat("0.0");//保留一位小数
String str="1.10108003557082E14";
double val=Double.valueOf(str);
System.out.println(val); //默认科学表示法输出
System.out.println(df.format(val));//采用指定格式输出
Weka内部为什么没有这么做,可能是因为他不知道具体要保存什么的格式,就以科学计数法给保存了。
这里还有一个简单的方法,若里面还有其他numerical字段,可以将想要处理为nominal的数字字段,前面加上几个字符,对于本例可以将第一个序列号的字段前面都加id(这样就会抛出NumberFormatException),得到如下数据格式,这样CSVLoader就能正常处理,你也能得到你想要的结果了。
@relation companies
@attribute regId {id110108003557082,id110107000885559,id110109002641736,id110102000765431,id110109004903736,id110108003533570,id110101000171791,id110108000938562}
@attribute text string
@data
id110108003557082,‘销售 计算机 软件 及 辅助 设备 电子产品 未 取得 行政许可 的 项目 除外 ‘
id110107000885559,‘技术转让 销售 百货 针纺织品 五金 交电 化工 建筑材料 机械设备 电器设备 ‘
id110109002641736,‘汽车配件 计算机 软硬件 及 外围设备 家居装饰 设计 制作 服装 计算机 软硬件 ‘
id110102000765431,‘技术开发 动力 技术开发 咨询 销售 机械 电器设备 发电机组 五金交电 橡胶制品 ‘
id110109004903736,‘建筑材料 金属材料 除 黄金 化工产品 不含 化学 危险品 及 一类 易制毒 化学品 ‘
id110108003533570,‘计算机 软硬件 及 外设 数码 技术开发 技术开发 转让 咨询 服务 培训 技术推广 服务 销售‘
id110101000171791,‘软件 技术开发 技术咨询 技术培训 技术转让 技术服务 信息 咨询 ‘
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)相似性。最后可以用来文本的分类聚类。
/* ......紧接最上面的代码 */
/* filter 分词后的 string */
StringToWordVector filter = new StringToWordVector();
filter.setInputFormat(datasrc);
// String stopwordfile = "datasets/stopwords.en";
// filter.setStopwords(new File(stopwordfile));
// filter.setUseStoplist(true);
String optionStr = "-R first-last -W 1000 -prune-rate -1.0 -C -I -N 1";
filter.setOptions(Utils.splitOptions(optionStr));
Instances dataFiltered = Filter.useFilter(datasrc, filter);
//....接下来可以对dataFilered数据集进行分类或聚类
WEKA学习——CSVLoader 实例训练 和 源码分析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。