一、什麼是arff格式檔案
1、arff是Attribute-Relation File Format縮寫,從英文字面也能大概看出什麼意思。它是weka資料挖掘開源程式使用的一種檔案模式。由于weka是個很出色的資料挖掘開源項目,是以使用的比較廣,這也無形中推廣了它的資料存儲格式。
2、下面是weka自帶的一個arff檔案例子(weather.arff)
1 @relation weather
2
3 @attribute outlook {sunny, overcast, rainy}
4 @attribute temperature real
5 @attribute humidity real
6 @attribute windy {TRUE, FALSE}
7 @attribute play {yes, no}
8
9 @data
10 sunny,85,85,FALSE,no
11 sunny,80,90,TRUE,no
12 overcast,83,86,FALSE,yes
13 rainy,70,96,FALSE,yes
14 rainy,68,80,FALSE,yes
15 rainy,65,70,TRUE,no
16 overcast,64,65,TRUE,yes
17 sunny,72,95,FALSE,no
18 sunny,69,70,FALSE,yes
19 rainy,75,80,FALSE,yes
20 sunny,75,70,TRUE,yes
21 overcast,72,90,TRUE,yes
22 overcast,81,75,FALSE,yes
23 rainy,71,91,TRUE,no
a) 第1行,是關系名稱,這個自己随便起,不過寫的最好要有意義。
b) 第3~7行是特征清單,其中第1列是特征說明,不可缺少,第2列是特征名稱,第3列是特征類型或特征取值範圍。
c) @data(第9行)是資料域說明,在它下面的全是資料。其中每一行體表一條資料。
d) 例子中給出的資料域是最基本的表示方法,實際應用中,一般都是用稀疏表示法。
e) 此處對于arff檔案格式不做進一步解釋,不懂的地方可以給我留言。
二、總體思路
1、生成特征檔案
2、檔案格式轉換
三、具體實作
1、特征的生成
這裡假設我們已經生成了特征。(因為特征選擇我會另寫一篇文章單獨介紹)
2、arff檔案生成
a) 生成檔案的接口
1 package com.lvxinjian.alg.models.generatefile;
2
3 /**
4 * @Descriptin : 生成指定格式檔案的接口
5 * @author :Lv Xinjian
6 *
7 */
8 public interface GenerateFile {
9 /**
10 * @function 生成檔案
11 * @param obj 輸入資料
12 * @param option 參數
13 * @return 是否生成成功
14 */
15 abstract public boolean GenerFile(Object obj , String option);
16 }
View Code
b) 生成arff的主檔案
1 package com.lvxinjian.alg.models.generatefile;
2
3 import java.io.IOException;
4 import java.nio.charset.Charset;
5 import java.util.ArrayList;
6 import java.util.HashSet;
7 import java.util.concurrent.ExecutorService;
8 import java.util.concurrent.Executors;
9 import java.util.concurrent.Future;
10
11 import weka.core.FastVector;
12 import weka.core.Instance;
13 import weka.core.Instances;
14
15 import com.iminer.alg.models.sampling.SVMSampleBean;
16 import com.iminer.alg.models.sampling.SampleBean;
17 import com.iminer.alg.models.sampling.SampleUtils;
18 import com.iminer.tool.common.util.FileTool;
19
20 /**
21 * @Description : 生成arff格式的檔案
22 * @author : Lv Xinjian
23 *
24 */
25 public class GenerateArffFile implements GenerateFile {
26
27 /**
28 * 儲存arff檔案
29 */
30 private static Instances data = null;
31 /**
32 * 分類标簽
33 */
34 private ClassifyAttribute classifyAttribute = new ClassifyAttribute();
35 /**
36 * Instance name
37 */
38 public final String InstanceName = "MyRelation";
39 /**
40 * 抽取instance的方法 ,預設為方法一
41 */
42 private String getInstancesMothed = "one";
43 /**
44 * 儲存轉換後的SVM資料
45 */
46 private ArrayList<SVMSampleBean> listSVMBean = new ArrayList<SVMSampleBean>();
47 /**
48 * 生成arff檔案時使用的詞表路徑
49 */
50 private String lexPath = null;
51 /**
52 * 儲存arff檔案的路徑
53 */
54 private String outputPath = null;
55 /**
56 * 生成arff檔案的主函數
57 */
58 private int threadNum = 5;
59 @Override
60 public boolean GenerFile(Object obj, String option) {
61 String [] paramArray = option.split(" ");
62 return GenerFile(obj,paramArray);
63 }
64 /**
65 * 生成arff檔案的主函數
66 */
67 public boolean GenerFile(Object obj, String [] paramArray) {
68 try {
69 if(!Initialization(obj , paramArray))
70 return false;
71 GenerateData();
72 } catch (IOException e) {
73 e.printStackTrace();
74 return false;
75 }
76 return true;
77 }
78 /**
79 * @function 解析指令行,初始化參數
80 * @param obj 傳入的資料
81 * @param paramArrayOfString 指令行
82 * @return 初始化是否生成成功,true:成功 false:失敗
83 */
84 private boolean Initialization(Object obj, String [] paramArrayOfString){
85 try{
86 ArrayList<?> list = (ArrayList<?>)obj;
87 for(Object s : list){
88 listSVMBean.add((SVMSampleBean)s);
89 }
90 //初始化詞表路徑
91 String lexPath = ParameterUtils.getOption("lexPath", paramArrayOfString);
92 if(lexPath.length() != 0)
93 this.lexPath = lexPath;
94 //初始化arff檔案儲存路徑
95 String outputPath = ParameterUtils.getOption("output", paramArrayOfString);
96 if(outputPath.length() != 0)
97 this.outputPath = outputPath;
98 //初始化instance抽取方法,預設為方法一
99 String getInstanceMothed = ParameterUtils.getOption("mothed", paramArrayOfString);
100 if(getInstanceMothed.length() != 0)
101 this.getInstancesMothed = getInstanceMothed;
102 String threadNum = ParameterUtils.getOption("thread", paramArrayOfString);
103 if(threadNum.length() != 0){
104 this.threadNum = Integer.parseInt(threadNum);
105 System.out.println("線程數 :" + this.threadNum);
106 }else{
107 System.out.println("使用預設線程數:5");
108 }
109 //初始化類别名稱,不可省略
110 String className = ParameterUtils.getOption("class", paramArrayOfString);
111 if(className.length() != 0)
112 classifyAttribute.setClassname(className);
113 //初始化類别标簽
114 String labels = ParameterUtils.getOption("label", paramArrayOfString);
115 if(labels.length() != 0){
116 classifyAttribute.setClassLabel(labels.split(","));
117 }
118 else{
119 System.out.println("please Initialize classify labels!");
120 return false;
121 }
122 }
123 catch(Exception e){
124 e.printStackTrace();
125 }
126
127 return true;
128 }
129 /**
130 * @function 生成arff的主函數
131 * @throws IOException
132 */
133 public void GenerateData() throws IOException {
134 ArrayList<String> words = new ArrayList<String>();
135 FastVector atts = ArffFileUtils.GetAttributes(this.lexPath , words ,this.classifyAttribute );
136
137 data = new Instances(this.InstanceName, atts, 0);
138
139 ExecutorService service = Executors.newFixedThreadPool(this.threadNum);
140 int count = 0;
141 for (SVMSampleBean it : listSVMBean) {
142 if (count++ % 1000 == 0)
143 System.out.println("processed " + count + " recoreds");
144
145 //擷取類别标簽的下标
146 double labelVal = data.attribute(classifyAttribute.getClassname()).indexOfValue(it.getLabel());
147
148 MyCallableClass task = new MyCallableClass(labelVal, it.getContent(), words ,this.getInstancesMothed);
149 Future is = service.submit(task);
150 try{
151 if(is.get() != null)
152 data.add((Instance) is.get());
153 }catch(Exception e){
154 e.printStackTrace();
155 }
156 }
157 service.shutdown();
158 //儲存data中的資料
159 ArffFileUtils.savaInstances(data,this.outputPath);
160 //清空data中的資料
161 data.delete();
162 }
163 /**
164 * @function
165 * @param lstContent
166 * @param lexPath
167 * @param labels
168 * @param outputPath
169 * @return
170 */
171 public static boolean generateArff(ArrayList<String> lstContent ,String lexPath , String labels, String outputPath){
172 try{
173 ArrayList<SampleBean> list = new ArrayList<SampleBean>();
174 HashSet<String> lstLabel = new HashSet<String>();
175 for(String str : lstContent){
176 SVMSampleBean svmBean = new SVMSampleBean(str.replace("\t", SampleUtils.SPECIAL_CHAR));
177 list.add(svmBean);
178 lstLabel.add(svmBean.label);
179 }
180 if(labels == null){
181 labels = "";
182 for(String label : lstLabel){
183 if(labels != "")
184 labels += ",";
185 labels += label;
186 }
187 }
188 String [] options = new String[]{"-lexPath" , lexPath,
189 "-output" , outputPath,
190 "-mothed" , "one",
191 "-class" , "CLASS",
192 "-label" , labels};
193 GenerateArffFile genArff =new GenerateArffFile();
194 genArff.GenerFile(list, options);
195 return true;
196 }catch(Exception e){
197 e.printStackTrace();
198 return false;
199 }
200
201 }
202 public static void main(String [] args){
203 try{
204 String root = "C:\\Users\\Administrator\\Desktop\\12_05\\模型訓練\\1219\\";
205 ArrayList<SampleBean> list = new ArrayList<SampleBean>();
206 ArrayList<String> lstContent = FileTool.LoadListFromFile(root + "不重合的部分.23.txt", 0 , Charset.forName("utf8"));
207 for(String str : lstContent){
208
209 SVMSampleBean svmBean = new SVMSampleBean(str.replace("\t", SampleUtils.SPECIAL_CHAR));
210 list.add(svmBean);
211 }
212 // DFFeatureSelector selector = new DFFeatureSelector();
213 // String options = "-maxFeatureNum 1000 -outputPath "+root + "lex.txt";
214 // selector.selectFeature(list, options);
215
216 // options = "-lexPath "+root +"lex.txt -output genArffTest.arff -mothed one -class CLASS -label -1,1,2";
217
218
219 String [] options = new String[]{"-lexPath" , root + "temp/temp.lex",
220 "-output" , root + "不重合的部分.23.arff",
221 "-mothed" , "one",
222 "-class" , "CLASS",
223 "-label" , "2,1,-1",
224 "-thread","10"};
225 GenerateArffFile genArff =new GenerateArffFile();
226 genArff.GenerFile(list, options);
227 CalcAttributeFromArffFile.calcAttribute(root + "不重合的部分.23.arff" , root + "calc.txt");
228 }catch(Exception e){
229 e.printStackTrace();
230 }
231
232 }
233 }
View Code
c) 指令行解析工具
1 package com.lvxinjian.alg.models.generatefile;
2
3 /**
4 * @Description : 抽取參數
5 * @author : Lv Xinjian
6 */
7 public class ParameterUtils {
8
9 static public void main(String [] args){
10 String option = "-min 1 -max 100 -w 1.2,1.3 -filter g";
11 try {
12 boolean r = getFlag("filter", option.split(" "));
13 System.out.println(r);
14
15 } catch (Exception e) {
16 e.printStackTrace();
17 }
18 }
19 /**
20 * @function 抽取字元類的布爾型參數
21 * @param flag 字元
22 * @param options 參數數組
23 * @return
24 * @throws Exception
25 */
26 public static boolean getFlag(char flag, String[] options) throws Exception
27 {
28 return getFlag("" + flag, options);
29 }
30 /**
31 * @function 抽取字元串類的布爾型參數
32 * @param flag 字元
33 * @param options 參數數組
34 * @return
35 * @throws Exception
36 */
37 public static boolean getFlag(String flag, String[] options) throws Exception
38 {
39 int pos = getOptionPos(flag, options);
40
41 if (pos > -1) {
42 options[pos] = "";
43 }
44 return (pos > -1);
45 }
46 /**
47 * @function 抽取字元串類的字元串型參數
48 * @param flag 字元串
49 * @param options 參數數組
50 * @return
51 * @throws Exception
52 */
53 public static String getOption(String flag, String[] options)throws Exception {
54 int i = getOptionPos(flag, options);
55 if (i > -1) {
56 if (options[i].equals("-" + flag)) {
57 if (i + 1 == options.length) {
58 throw new Exception("No value given for -" + flag
59 + " option.");
60 }
61 options[i] = "";
62 String newString = new String(options[(i + 1)]);
63 options[(i + 1)] = "";
64 return newString;
65 }
66 if (options[i].charAt(1) == '-') {
67 return "";
68 }
69 }
70
71 return "";
72 }
73 /**
74 * @function 抽取字元類的短整型參數
75 * @param flag 字元
76 * @param options 參數數組
77 * @return
78 * @throws Exception
79 */
80 public static int getOptionPos(char flag, String[] options)
81 {
82 return getOptionPos("" + flag, options);
83 }
84 /**
85 * @function 抽取字元串類的短整型參數
86 * @param flag 字元
87 * @param options 參數數組
88 * @return
89 * @throws Exception
90 */
91 public static int getOptionPos(String flag, String[] options) {
92 if (options == null) {
93 return -1;
94 }
95 for (int i = 0; i < options.length; ++i) {
96 if ((options[i].length() <= 0) || (options[i].charAt(0) != '-'))
97 continue;
98 try {
99 Double.valueOf(options[i]);
100 } catch (NumberFormatException e) {
101 if (options[i].equals("-" + flag)) {
102 return i;
103 }
104 if (options[i].charAt(1) == '-') {
105 return -1;
106 }
107 }
108 }
109
110 return -1;
111 }
112 /**
113 * @function 把資料轉換成字元串
114 * @param array
115 * @param splitPunc
116 * @return
117 */
118 public static String array2String(String [] array , String splitPunc){
119 try{
120 StringBuilder sb = new StringBuilder();
121 for(String para : array){
122 if(sb.length() != 0)
123 sb.append(splitPunc);
124 sb.append(para);
125 }
126 return sb.toString();
127 }catch(Exception e){
128 e.printStackTrace();
129 return null;
130 }
131 }
132 public static String [] String2Array(String para , String splitPunc){
133 try{
134 return para.split(splitPunc);
135 }catch(Exception e){
136 e.printStackTrace();
137 return null;
138 }
139 }
140 /**
141 * @替換字元串中的某個參數
142 * @param para 參數字元串
143 * @param splitPunc 參數分隔符
144 * @param key 參數名稱
145 * @param value 新參數值
146 * @return
147 */
148 public static String replacePara(String para , String splitPunc , String key , String value){
149 try{
150 String [] parameter = String2Array(para,splitPunc);
151 replacePara(parameter, key, value);
152 return array2String(parameter, splitPunc);
153 }catch(Exception e){
154 e.printStackTrace();
155 return null;
156 }
157 }
158 /**
159 * @function 替換一個參數
160 * @param para 參數數組
161 * @param key 參數名稱
162 * @param value 新參數值
163 * @return
164 */
165 public static String [] replacePara(String [] para , String key , String value){
166 try{
167 for(int i = 0 ; i < para.length ; i++){
168 String paraName = para[i];
169 if(paraName.contains(key)){
170 int nextIndex = i + 1;
171 if(nextIndex < para.length){
172 if(!para[nextIndex].contains("-"))
173 para[nextIndex] = value;
174 }
175 }
176 }
177 return para;
178 }catch(Exception e){
179 e.printStackTrace();
180 return null;
181 }
182 }
183 }
View Code
d) 生成arff檔案的輔助類
1 package com.lvxinjian.alg.models.generatefile;
2
3 import java.io.BufferedWriter;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.OutputStreamWriter;
7 import java.nio.charset.Charset;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.concurrent.Callable;
11
12 import weka.core.Attribute;
13 import weka.core.FastVector;
14 import weka.core.Instance;
15 import weka.core.Instances;
16 import weka.core.SparseInstance;
17
18 import com.iminer.alg.models.getInstance.InstanceFactory;
19 import com.iminer.tool.common.util.FileTool;
20
21
22 /**
23 * @Description : 生成arff檔案的工具類
24 * @author : Lv Xinjian
25 */
26 public class ArffFileUtils {
27
28 /**
29 * @function 生成特征向量
30 * @param lexDataPath 特征檔案路徑
31 * @param words 儲存特征詞
32 * @param classifyAttribute 類别特征
33 * @return 特征向量
34 * @throws IOException 檔案讀取異常
35 */
36 public static FastVector GetAttributes(String lexDataPath , List<String> words ,ClassifyAttribute classifyAttribute)
37 throws IOException {
38 HashMap<Integer, String> termId_term = FileTool.LoadIdStrFromFile(
39 lexDataPath, 0, 0, 1, "\t", Charset.forName("utf8"));
40 FastVector atts = new FastVector();
41 // - numeric
42 for (int tid = 0; tid < termId_term.size(); tid++) {
43 atts.addElement(new Attribute(termId_term.get(tid)));
44 words.add(termId_term.get(tid));
45 }
46 atts.addElement(new Attribute(classifyAttribute.getClassname(), classifyAttribute.getAttClassLabel()));
47
48 System.out.println("atribute size :" + atts.size());
49 return atts;
50 }
51 /**
52 * @function 儲存Arff檔案
53 * @param data arff格式的資料
54 * @param outputPath 資料儲存路徑
55 * @return
56 */
57 public static boolean savaInstances(Instances data , String outputPath)
58 {
59 try{
60 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
61 new FileOutputStream(outputPath), Charset.forName("utf-8")));
62
63 bw.write(data.toString());
64 bw.close();
65 }catch(Exception e){
66 e.printStackTrace();
67 return false;
68 }
69 return true;
70 }
71 }
72 /**
73 * @Description 分類器 類别特征
74 * @author Administrator
75 *
76 */
77 class ClassifyAttribute{
78 /**
79 * 分類特征名 , 預設為"CLASS"
80 */
81 private String classname = "CLASS";
82 private FastVector attClassLabel = new FastVector();
83 public ClassifyAttribute(String [] labels){
84 for(String label: labels){
85 this.attClassLabel.addElement(label);
86 }
87 }
88 public ClassifyAttribute(){}
89 /**
90 * @function 擷取類别特征名
91 * @return
92 */
93 public String getClassname() {
94 return classname;
95 }
96 /**
97 * @functoin 設定類别特征名
98 * @param classname
99 */
100 public void setClassname(String classname) {
101 this.classname = classname;
102 }
103 /**
104 * @function 擷取類别特征
105 * @return 類别特征
106 */
107 public FastVector getAttClassLabel() {
108 return attClassLabel;
109 }
110 /**
111 * @function 設定類别特征
112 * @param attClassLabel
113 */
114 public void setClassLabel(FastVector attClassLabel) {
115 this.attClassLabel = attClassLabel;
116 }
117 /**
118 * @function 設定類别特征分類标簽
119 * @param labels
120 */
121 public void setClassLabel(String [] labels) {
122 for(String label: labels){
123 this.attClassLabel.addElement(label);
124 }
125 }
126
127 }
128
129 /**
130 * @Description 把字元串轉化成 instance執行個體
131 * @author Administrator
132 *
133 */
134 class MyCallableClass implements Callable{
135 /**
136 * 輸入的文本
137 */
138 private String _text;
139 /**
140 * 文本的極性标簽
141 */
142 private double _labelVal;
143 /**
144 * 生成instance的方法
145 */
146 private String _getInstancesMothed = null;
147 /**
148 * 擷取instance方法 的 初始化詞表
149 */
150 private List<String> _set = null;
151 /**
152 * @function 構造函數,初始化參數
153 * @param labelVal 極性類别标簽
154 * @param text 輸入的文本内容
155 * @param set 擷取instance方法 的 初始化詞表
156 * @param getInstancesMothed 生成instance的方法
157 */
158 public MyCallableClass(final double labelVal, String text ,List<String> set ,String getInstancesMothed)
159 {
160 this._text = text;
161 this._labelVal = labelVal;
162 this._getInstancesMothed = getInstancesMothed;
163 this._set = set;
164
165 }
166 public Instance call() throws Exception{
167 double[] vals;
168 try {
169 //擷取instance的double 數組
170 vals = InstanceFactory.getInstance(this._getInstancesMothed, this._set ).getInstanceFromText(this._text );
171 if (vals != null) {
172 vals[vals.length - 1] = _labelVal;
173 SparseInstance si = new SparseInstance(1.0, vals);
174 return si;
175 }
176
177 } catch (Exception e) {
178 e.printStackTrace();
179 }
180 return null;
181
182 }
183 }
View Code
e) 以上代碼缺少了幾個類,但由于涉及到公司的保密制度,是以不友善上傳。如有疑問,可以給我留言。(其實就是一個生成instance的方法,不過我的方法中揉了些東西進去,方法本身很簡單,幾行代碼的事兒,有空我會補上)
四、小結
1、考慮到抽取instance可能會有不同的需求,是以用了一個工廠類,這樣友善 使用和新方法的添加。
2、考慮到效率問題,是以使用了多線程進行生成。
3、代碼的結構、風格以及變量命名是硬傷,希望多多批評、指點。