1、什麼是arff格式文件java
一、arff是Attribute-Relation File Format縮寫,從英文字面也能大概看出什麼意思。它是weka數據挖掘開源程序使用的一種文件模式。因爲weka是個很出色的數據挖掘開源項目,因此使用的比較廣,這也無形中推廣了它的數據存儲格式。數組
二、下面是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行,是關係名稱,這個本身隨便起,不過寫的最好要有意義。app
b) 第3~7行是特徵列表,其中第1列是特徵說明,不可缺乏,第2列是特徵名稱,第3列是特徵類型或特徵取值範圍。ide
c) @data(第9行)是數據域說明,在它下面的全是數據。其中每一行體表一條數據。函數
d) 例子中給出的數據域是最基本的表示方法,實際應用中,通常都是用稀疏表示法。工具
e) 此處對於arff文件格式不作進一步解釋,不懂的地方能夠給我留言。 ui
2、整體思路this
一、生成特徵文件spa
二、文件格式轉換
3、具體實現
一、特徵的生成
這裏假設咱們已經生成了特徵。(由於特徵選擇我會另寫一篇文章單獨介紹)
二、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 }
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 }
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 }
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 }
e) 以上代碼缺乏了幾個類,但因爲涉及到公司的保密制度,因此不方便上傳。若有疑問,能夠給我留言。(其實就是一個生成instance的方法,不過個人方法中揉了些東西進去,方法自己很簡單,幾行代碼的事兒,有空我會補上)
4、小結
一、考慮到抽取instance可能會有不一樣的需求,因此用了一個工廠類,這樣方便 使用和新方法的添加。
二、考慮到效率問題,因此使用了多線程進行生成。
三、代碼的結構、風格以及變量命名是硬傷,但願多多批評、指點。