上一篇介紹了arff格式,這是weka專有格式,一般情況需要我們從其他數據源抽取或者獲得。weka支持從cvs轉化,也可以從數據庫中抽取,界面如下圖
weka安裝目錄有一個data目錄,里面有一些測試數據,可以用於測試和學習。
導入了數據僅僅是一個開始,我們還需要對數據進行預處理。
數據預處理(data preprocessing)
數據預處理(data preprocessing)是指在主要的處理以前對數據進行的一些處理。
現實世界中數據大體上都是不完整,不一致的臟數據,無法直接進行數據挖掘,或挖掘結果差強人意。
為了提高數據挖掘的質量產生了數據預處理技術。
數據預處理有多種方法:數據清理,數據集成,數據變換,數據歸約等。這些數據處理技術在數據挖掘之前使用,大大提高了數據挖掘模式的質量,降低實際挖掘所需要的時間。
數據清理是使用比較頻繁的,主要有:
(1)空缺值處理
目前最常用的方法是使用最可能的值填充空缺值,比如可以用回歸、貝葉斯形式化方法工具或判定樹歸納等確定空缺值.這類方法依靠現有的數據信息來推測空缺值,使空缺值有更大的機會保持與其他屬性之間的聯系。
還可以用一個全局常量替換空缺值、使用屬性的平均值填充空缺值或將所有元組按某些屬性分類,然后用同一類中屬性的平均值填充空缺值.如果空缺值很多,這些方法可能誤導挖掘結果。
(2)噪聲數據處理
噪聲是一個測量變量中的隨機錯誤或偏差,包括錯誤的值或偏離期望的孤立點值。常用分箱、回歸、計算機檢查和人工檢查結合、聚類等方法進行噪音處理。
數據變化主要使用平滑聚集,數據概化,規范化等手段使數據換為較利於數據挖掘的格式。
數據歸約主要是為了壓縮數據量,源數據可以用來得到數據集的歸約表示,它接近於保持原數據的完整性,但數據量比原數據小得多.與非歸約數據相比,在歸約的數據上進行挖掘,所需的時間和內存資源更少,挖掘將更有效,並產生相同或幾乎相同的分析結果。常用維歸約、數據壓縮、數值歸約等方法實現。
Weka.Filters
weka.filters中包含了一些數據預處理的簡單實現(其實已經夠用了),主要分成兩大類,監督過濾(UnsupervisedFilter)和非監督過濾(UnsupervisedFilter)。
如果是使用GUI的話,點擊Filter的Choose就可以選擇
選擇完成后點擊選擇的Filter本身就可以修改相關參數。
完成參數修正后點擊Apply就Ok了。
我平時使用的比較多的還是非監督過濾,下面介紹一些比較常見。
先介紹weka.filters.unsupervised.attribute包下的,這是非監督方法對屬性進行預處理。
1.Add
為數據庫添加一個新的屬性,新的屬性將會包含所有缺失值。可選參數:
attributeIndex:屬性位置,從1開始算,last是最后一個,first是第一個
attributeName:屬性名稱
attributeType:屬性類型,一般是4選1
dateFormat:數據格式,參考ISO-8601
nominalLabels:名義標簽,多個值用逗號隔開
2.AddExpression
新增一個屬性,該屬性由現有屬性通過設定的表達式計算得出。支持+, -, *, /, ^, log, abs, cos, exp, sqrt, floor, ceil, rint, tan, sin。現有屬性由a+索引值構成。
3.AddID
字面意思,添加一個ID
4.AddNoise
只對名義屬性有效,依照一定比例修改值。
5.Center
將數值化屬性的平均化為0。
6.ChangeDateFormat
修改數據格式
7.Copy
復制制定屬性並命名為Copy Of XX
8.Discretize
簡單划分的離散化處理。參數:
attributeIndices:屬性范圍,如1-5,first-last
bins:桶的數量
9.FirstOrder
第n個值用n+1項值和n項值的差替換
10.MathExpression
功能和AddExpression類似,不過支持的運算更多,特別是MAX和MIN的支持特別有用。所有支持運算符如下:+, -, *, /, pow, log,abs, cos, exp, sqrt, tan, sin, ceil, floor, rint, (, ),A,MEAN, MAX, MIN, SD, COUNT, SUM, SUMSQUARED, ifelse
11.Reorder
重新排列屬性,輸入2-last,1可以讓第一項排到最后,如果輸入1,3,5的話…其他項就沒有了
12.Standardize
這個和Center功能大致相同,多了一個標准化單位變異數
13.StringToNominal
將String型轉化為Nominal型
14.SwapValues
交換值
然后是weka.filters.unsupervised.instance包下的
1.NonSparseToSparse
將所有輸入轉為稀疏格式
2.Normalize
規范化整個實例集
3.RemoveFolds
交叉驗證,不支持分層,如果需要的話使用監督學習中的方法
4.RemoveRange
移除制定范圍的實例,化為NaN
5.Resample
隨機抽樣,從現有樣本產生新的小樣本
6.SubsetByExpression
根據規則進行過濾,支持邏輯運算,向上取值,取絕對值等等
weka.filters.supervised包中的內容比較少,而且涉及到一些流程原理,這里就不介紹了,后面的文章會慢慢介紹到
調用Weka實現數據預處理
weka的使用並不僅僅極限於它自帶的GUI或者命令行,我們可以使用weka的java api,在weka的基礎架構和已經實現的算法基礎上進行開發。
新建一個java項目,添加對weka.jar的引用。這個包一般在安裝目錄下,我的3.6版本的大小為6316kb。
Instances是最主要的數據集容器,讀入arff文件初始化之,如下:
Instances instances=DataSource.read("data/cpu.arff");
System.out.println(instances.toSummaryString());
效果:
使用Filter的一般流程是:實例化過濾器,傳入過濾器參數,通過Filter.useFilter使用過濾器
舉個例子,我想為這個cpu數據庫加入一個ID,使用AddID過濾器。
先實例化AddID
AddID filter = new AddID();
該過濾器需要2個參數,一個是位置,一個是名稱。建立一個長為4的字符串數組,填充參數
String[] options = new String[4];
options[0] = "-C";
options[1] = "first";
options[2] = "-N";
options[3] = "ID";
filter.setOptions(options);
filter.setInputFormat(instances);
使用過濾器,然后輸出
Instances newInstances = Filter.useFilter(instances, filter);
System.out.println(newInstances.toSummaryString());
完整代碼:
Instances instances = DataSource.read("data/cpu.arff");
System.out.println(instances.toSummaryString());
AddID filter = new AddID();
String[] options = new String[4];
options[0] = "-C";
options[1] = "first";
options[2] = "-N";
options[3] = "ID";
filter.setOptions(options);
filter.setInputFormat(instances);
Instances newInstances = Filter.useFilter(instances, filter);
System.out.println(newInstances.toSummaryString());
效果:
再演示一個離散化過濾的使用和新數據的保存
Discretize discretize = new Discretize();
options = new String[6];
options[0] = "-B";
options[1] = "8";
options[2] = "-M";
options[3] = "-1.0";
options[4] = "-R";
options[5] = "2-last";
discretize.setOptions(options);
discretize.setInputFormat(newInstances);
Instances newInstances2 = Filter.useFilter(newInstances, discretize);
System.err.println(newInstances2.toSummaryString());
DataSink.write("data/newcpu.arff", newInstances2);
其實可以很明顯的看出,java調用weka api並不困難,關鍵還是對於數據挖掘、weka本身的了解和熟悉,對於使用哪種方法,需要什么參數要有一定概念。
weka的擴展,實現自己的過濾器
我使用的weka版本是3.6.6,要是版本不同的話可能有些細節有差異。
一切的開始是Filter類,然后是SimpleFilter,一般情況下我們繼承SimpleStreamFilter、SimpleBatchFilter。
這兩個的本質差別是一個是全部讀入,一個是數據流式處理,但是代碼可以完全一樣,主要是效率和使用空間上的區別。
舉個例子,我希望將所有屬性都進行向下取整,繼承SimpleStreamFilter,實現和重寫一下方法
public Capabilities getCapabilities()
public String globalInfo()
protected Instances determineOutputFormat(Instances inputFormat)
protected Instances process(Instances inst)
完整代碼:
@Override
public Capabilities getCapabilities() {
Capabilities capabilities = super.getCapabilities();
capabilities.enableAllAttributes();
capabilities.enableAllClasses();
capabilities.enable(Capability.NO_CLASS);
return capabilities;
}
public String globalInfo() {
return "A simple batch filter that adds an additional attribute 'bla' at the end "
+ "containing the index of the processed instance.";
}
protected Instances determineOutputFormat(Instances inputFormat) {
Instances result = new Instances(inputFormat, 0);
return result;
}
protected Instances process(Instances inst) {
Instances result = new Instances(determineOutputFormat(inst), 0);
for (int i = 0; i < inst.numInstances(); i++) {
double[] values = new double[result.numAttributes()];
for (int n = 0; n < inst.numAttributes(); n++)
values[n] = Math.floor(inst.instance(i).value(n));
result.add(new Instance(1, values));
}
return result;
}
效果: