最近這些天一直在用weka實(shí)現(xiàn)一個(gè)算法,也是從這次開始接觸weka,剛上手難免有些磕磕絆絆,這次實(shí)現(xiàn)也是遇到了各種各樣的問(wèn)題,其中的一個(gè)就 和weka中的Instances有關(guān),剛剛把程序跑起來(lái)了,因?yàn)閿?shù)據(jù)比較多,沒(méi)有個(gè)一天兩天估計(jì)是跑不完了,趁這個(gè)空閑時(shí)間,把我遇到的問(wèn)題及解決方法 記錄下來(lái),一是這樣可能會(huì)幫助到其他人,還有就是也方便我自己以后查閱,畢竟俗話說(shuō)得好,爛筆頭勝過(guò)好記性。
首先,weka是從事數(shù)據(jù)挖掘相關(guān)研究的人一定會(huì)接觸到的一個(gè)軟件,這個(gè)軟件由新西蘭waikato大學(xué)編寫,里面實(shí)現(xiàn)了種類繁多的算法,這里就不一一介紹,相信用過(guò)weka的人都知道我在說(shuō)什么。
這篇隨筆將會(huì)從三個(gè)具體的和屬性選擇相關(guān)的實(shí)際問(wèn)題出發(fā),給出具體的解決辦法及代碼。在討論實(shí)際問(wèn)題之前,先來(lái)說(shuō)說(shuō)為什么需要屬性刪除,也就是需求的問(wèn)題。
?
一、為什么需要屬性刪除
我們知道,在做交叉驗(yàn)證的時(shí)候,在訓(xùn)練集上做過(guò)什么處理,這些處理必須原封不動(dòng)地施加到測(cè)試集上,這樣分類得到的結(jié)果才是可信的。
大 數(shù)據(jù)時(shí)代已經(jīng)來(lái)臨,我們經(jīng)常會(huì)遇到那種實(shí)例多,維度高(屬性多)的大數(shù)據(jù),處理這樣的數(shù)據(jù)時(shí),從效果和效率兩方面來(lái)考慮,我們都需要先對(duì)數(shù)據(jù)進(jìn)行預(yù)處理, 最常見的一種預(yù)處理的方式就是屬性選擇,也就是我現(xiàn)在一直在做的東西,所謂屬性選擇,從字面上理解,就是從高維的數(shù)據(jù)中選擇出一部分的屬性,用這部分屬性 代替屬性總體,這樣一來(lái),很顯然能夠減少數(shù)據(jù)處理的時(shí)間,另外,如果算法選擇得當(dāng),我們能得到更好的結(jié)果,比如說(shuō),分類的精度更高。
如果我 們用屬性選擇后的訓(xùn)練集構(gòu)建了一個(gè)分類器,我們?cè)趺礈y(cè)試這個(gè)分類器的好壞呢,還是要靠實(shí)驗(yàn)說(shuō)話,在測(cè)試集上測(cè)試這個(gè)分類器的精度,前面說(shuō)過(guò),在分類的時(shí)候 在訓(xùn)練集上的處理必須原封不動(dòng)的施加到測(cè)試集上,訓(xùn)練集上屬性選擇了,測(cè)試集也要屬性選擇,并且選出來(lái)的屬性必須和訓(xùn)練集上選出來(lái)的屬性完全一致,為了得 到屬性選擇后的測(cè)試集,顯然,除了訓(xùn)練集上選擇出來(lái)的屬性,測(cè)試集上的其他屬性都要?jiǎng)h除。
?
二、三個(gè)具體的問(wèn)題
為了更好的描述這幾個(gè)問(wèn)題,我們定義一些變量,這些變量在三個(gè)問(wèn)題中通用。
train:訓(xùn)練集 test:測(cè)試集
trainInstance:在訓(xùn)練集上屬性選擇之后返回的訓(xùn)練集 testInstances:依據(jù)trainInstances在test上刪除相應(yīng)的屬性返回的測(cè)試集
trainIndex:在訓(xùn)練集上屬性選擇之后返回的訓(xùn)練集的下標(biāo)數(shù)組
1.屬性選擇算法返回的是trainInstances,這個(gè)時(shí)候,為了得到testInstances,可以使用下面這段代碼
public static Instances delAttr(Instances model, Instances origin){ boolean flag = false ; ArrayList <Integer> al = new ArrayList<Integer> (); for ( int q = 0; q < origin.numAttributes() - 1; q++ ){ String temp2 = origin.attribute(q).name(); for ( int x = 0; x < model.numAttributes() - 1; x++ ){ String temp1 = model.attribute(x).name(); if (temp1.equals(temp2)){ flag = true ; break ; } } if (flag) { flag = false ; continue ; } else // dataCopy.deleteAttributeAt(q); // you can not do like this al.add( new Integer(q)); } for ( int q = 0; q < al.size(); q++ ){ int deltemp = al.get(q) - q; // pay attention to this line origin.deleteAttributeAt(deltemp); } return origin; }
model相當(dāng)于trainInstances,origin相當(dāng)于test,返回的origin相當(dāng)于testInstances.
上面這段代碼的思想無(wú)非就是通過(guò)屬性的名字把需要?jiǎng)h除的屬性的下標(biāo)全部存放在一個(gè)ArrayList的數(shù)據(jù)結(jié)構(gòu)中,然后根據(jù)這個(gè)ArrayList刪除對(duì)應(yīng)的屬性,返回新構(gòu)造的Instances.
注意上面我寫的兩行注釋,因?yàn)槟銊h除一條屬性之后,排在這條屬性之后的所有屬性的index都是會(huì)變化的(-1),這就相當(dāng)于你從String刪除一個(gè)字符一樣,知道這一點(diǎn),你就知道為什么第二條注釋對(duì)應(yīng)的刪除方法可以正常工作了。
注意:如果你用的數(shù)據(jù)集中有兩個(gè)或多個(gè)屬性的名字完全一樣,這種方法會(huì)把擁有這個(gè)名字的所有屬性都保留,但是,有可能你真正需要的只是其中一個(gè)。
?
2.直接使用1返回的結(jié)果交給分類器,往往會(huì)報(bào)錯(cuò),其中的原因有點(diǎn)微妙,解釋如下:
假 設(shè)train有四個(gè)屬性,分別是{attr1,attr2,attr3,attr4},attr4是類標(biāo)簽,test也是這樣 在train上屬性選擇,返回trainInstances{attr3,attr2,attr4},把trainInstances和test交給1, 返回的是testInstances{attr2,attr3,attr4},把testInstances交給分類器,報(bào)錯(cuò),報(bào)錯(cuò)的原因是 trainInstances和testInstances的屬性錯(cuò)位了.
這種情況下,我們需要交換兩個(gè)屬性的位置,下面這段代碼解決了這個(gè)問(wèn)題
public static Instances sort(Instances model, Instances process){ Attribute attr; for ( int i = 0; i < model.numAttributes()-1; i++ ){ for ( int j = 0; j < process.numAttributes()-1; j++ ){ if (process.attribute(j).name().equals(model.attribute(i).name())){ if (j!= i){ attr = process.attribute(j); process.insertAttributeAt(attr, i); for ( int k = 0; k < process.numInstances(); k++ ){ process.instance(k).setValue(i, process.instance(k).stringValue(j +1 )); //pay attention to j+1 } break ; } } } } for ( int i = process.numAttributes() - 2; i > model.numAttributes()-2; i-- ){ process.deleteAttributeAt(i);; } return process; }
?
model相當(dāng)于trainInstances,process相當(dāng)于1中返回的testInstances,2中返回的process就是能和trainInstances的每個(gè)屬性對(duì)應(yīng)起來(lái)的testInstances。
這段代碼的思想是找到 process中每個(gè)屬性在model中的對(duì)應(yīng)位置,然后再process的對(duì)應(yīng)位置插入這個(gè)屬性,插入屬性的時(shí)候需要把屬性值復(fù)制過(guò)去。
注意因?yàn)槲矣玫降臄?shù)據(jù)都是nominal類型的,所以在復(fù)制屬性值的時(shí)候選的是stringValue這個(gè)方法,如果是numeric或者其他的類型需要選擇對(duì)應(yīng)的方法,這個(gè)在weka的Instance類中可以找到。
?
3.屬性選擇算法返回的是下標(biāo)數(shù)組trainIndex
3和1的不同之處在于省去了自己用屬性名去匹配的這個(gè)步驟,也因此,即使數(shù)據(jù)集中有多個(gè)屬性的名字完全一致,也沒(méi)有影響,因?yàn)榉祷氐氖窍聵?biāo)。
這種情況可以用下面的代碼:
public static Instances delAttr(ArrayList<Integer> al, Instances inst){ for ( int i = inst.numAttributes() - 1; i > -1; i--){ // delete from back to forward if (! al.contains(i)) inst.deleteAttributeAt(i); } return inst; }
?
注意:這段代碼我是從后往前刪的,這種刪除方法不會(huì)出現(xiàn)問(wèn)題,因?yàn)槊看蝿h除的都是需要?jiǎng)h除的屬性的最后一個(gè),對(duì)前面需要?jiǎng)h除的屬性的下標(biāo)不會(huì)有任何影響。
注意:拿到屬性下標(biāo)之后,先看看是不是排序過(guò),沒(méi)有排序的話,最好先排序一下,按從小到大的順序排,這樣雖然需要花費(fèi)一點(diǎn)額外的時(shí)間,有時(shí)卻能避免意想不到的錯(cuò)誤,具體原因我說(shuō)不清楚,但是我遇到過(guò)。
?
三、總結(jié)
很顯然,第三種情況最簡(jiǎn)單,代碼清晰,花費(fèi)的時(shí)間也相對(duì)較少,所以在屬性選擇之后能夠直接拿到下標(biāo)數(shù)組最好,否則將拿到的結(jié)果轉(zhuǎn)化成下標(biāo)數(shù)組也可以。
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
