Python讀寫arff文件開源工具(liac-arff)的增強
最近項目中需要用Python讀寫arff文件, 因此熟悉了下python讀寫arff的開源工具——liac-arff,并根據自己的需求增強了下它的功能。
Liac-arff是巴西Rio Grande do Sul聯邦大學的Connectionist Artificial Intelligence實驗室開發的。項目地址。在Github上,該項目的星星多達14個(囧)。看來,用Python處理arff文件的人蠻少的。不管人多還是人少,自己的項目有需求,還是學習下這個工具。
使用liac-arff, 我們可以用如下代碼讀入arff文件。
>>> import arff
>>> data = arff.load(open('wheater.arff', 'rb'))
>>> data
{
u'attributes': [
(u'outlook', [u'sunny', u'overcast', u'rainy']),
(u'temperature', u'REAL'),
(u'humidity', u'REAL'),
(u'windy', [u'TRUE', u'FALSE']),
(u'play', [u'yes', u'no'])],
u'data': [
[u'sunny', 85.0, 85.0, u'FALSE', u'no'],
[u'sunny', 80.0, 90.0, u'TRUE', u'no'],
[u'overcast', 83.0, 86.0, u'FALSE', u'yes'],
[u'rainy', 70.0, 96.0, u'FALSE', u'yes'],
[u'rainy', 68.0, 80.0, u'FALSE', u'yes'],
[u'rainy', 65.0, 70.0, u'TRUE', u'no'],
u'description': u'',
u'relation': u'weather'
}
我們也可以用如下代碼,將obj轉成可以寫入arff文件的字符串。
>>> print arff.dumps(data)
@RELATION weather@ATTRIBUTE outlook {sunny, overcast, rainy}
@ATTRIBUTE temperature REAL
@ATTRIBUTE humidity REAL
@ATTRIBUTE windy {TRUE, FALSE}
@ATTRIBUTE play {yes, no}@DATA
sunny,85.0,85.0,FALSE,no
sunny,80.0,90.0,TRUE,no
overcast,83.0,86.0,FALSE,yes
rainy,70.0,96.0,FALSE,yes
rainy,68.0,80.0,FALSE,yes
rainy,65.0,70.0,TRUE,no
%
%
%
除了load和dumps, liac-arff還提供了兩個接口loads和dump。下表總結了四個接口的用法。
liac-arff接口函數 | 用法 |
---|---|
load(fp) | 從fp讀入arff數據,并解析成python表示arff數據的對象obj. |
load(str) | 解析str得到obj |
dump(obj, fp) | 將obj存入fp |
dumps(obj) | 將obj編碼成字符串返回 |
干貨結束,開始夾帶私貨(狐貍尾巴露出來了,囧)。自己的項目需要能夠一次讀入一部分arff數據,而liac-arff并沒有提供這個功能。因此我fork了這個項目,并修改了一些代碼。 新項目的地址。 代碼的改動主要有兩項,1)增加ArffDecoder.iter_decode方法, 2)修改了ArffEncoder.iter_encode方法。這些代碼改動沒有影響原來接口。也就是說,load、loads、dump和dumps的用法沒有變化。
第一個改動是新加ArffDecoder.iter_decode(self, file, encode_nominal = False, obj = None, batch = 20). 其中file是要讀入的arff文件,encode_nominal我也不知道是什么鬼(這個參數是我從arffDecoder.decode參數表中抄 過來的), obj是python內部表示arff數據的對象,batch表示一次性讀入多少實例。
當我們第一次調用iter_decode方法時,obj需要置成None, 此時iter_decode方法會讀取arff文件,并解析arff信息(包括relations, attributes)和batch個實例。當不是第一次調用時,我們可以用之前調用返回結果作為obj, 此時iter_decode方法只解析batch個實例。當然不同調用之間,file必須統一。以下是一個示例,假設test.arff文件中的內容:
@RELATION weather
@ATTRIBUTE outlook {sunny, overcast, rainy}
@ATTRIBUTE temperature REAL
@ATTRIBUTE humidity REAL
@ATTRIBUTE windy {TRUE, FALSE}
@ATTRIBUTE play {yes, no}@DATA
sunny,85.0,85.0,FALSE,no
sunny,80.0,90.0,TRUE,no
overcast,83.0,86.0,FALSE,yes
我們運行如下代碼:
>>> f = open("test.arff");
>>> decoder = ArffDecoder();
>>> obj1 = decoder.iter_decode(f, obj = None, batch = 2);
>>> obj2 = decoder.iter_decode(f, obj = obj1, batch = 2);
此時的結果是,
>>> obj1
{
u'attributes': [
(u'outlook', [u'sunny', u'overcast', u'rainy']),
(u'temperature', u'REAL'),
(u'humidity', u'REAL'),
(u'windy', [u'TRUE', u'FALSE']),
(u'play', [u'yes', u'no'])],
u'data': [
[u'sunny', 85.0, 85.0, u'FALSE', u'no'],
[u'sunny', 80.0, 90.0, u'TRUE', u'no']],
u'description': u'',
u'relation': u'weather'
}
>>> obj2
{
u'attributes': [
(u'outlook', [u'sunny', u'overcast', u'rainy']),
(u'temperature', u'REAL'),
(u'humidity', u'REAL'),
(u'windy', [u'TRUE', u'FALSE']),
(u'play', [u'yes', u'no'])],
u'data': [
[u'overcast', 83.0, 86.0, u'FALSE', u'yes']],
u'description': u'',
u'relation': u'weather'
}
明顯地,obj1包含前面兩個實例,obj2包含最后一個實例。
第二個改動是修改了ArffEncoder.iter_encode(self, obj, is_first_call = True), 其中obj是python內部表示arff數據的對象,is_first_call是我加上的參數,指示是否為第一次調用。當 is_first_call=True,即第一次調用, iter_encode方法和原來的iter_encode一樣,將obj里的所有信息(relation,attributes等)和數據,都編碼成 arff文件內容; 當is_first_call = False,即后續調用中,iter_encode只將obj中的數據進行編碼。通過使用修改后的iter_encode,你在只有一部分數據時,就可以 將數據寫入文件,而不必等所有數據到位。當所有數據很多,難以全部放入內存時,這種做法將很有用。以下是一個示例,假設
>>> obj
{
u'attributes': [
(u'outlook', [u'sunny', u'overcast', u'rainy']),
(u'temperature', u'REAL'),
(u'humidity', u'REAL'),
(u'windy', [u'TRUE', u'FALSE']),
(u'play', [u'yes', u'no'])],
u'data': [
[u'sunny', 85.0, 85.0, u'FALSE', u'no'],
[u'sunny', 80.0, 90.0, u'TRUE', u'no']],
u'description': u'',
u'relation': u'weather'
}
如果is_first_call = True, 則iter_encode將編碼所有信息和數據。
>>> encoder = ArffEncoder();
>>> result = encoder.iter_encode(obj, is_first_call = True);
>>> for i in result:
. . . print i+u'\n',;
. . .
@RELATION weather@ATTRIBUTE outlook {sunny, overcast, rainy}
@ATTRIBUTE temperature REAL
@ATTRIBUTE humidity REAL
@ATTRIBUTE windy {TRUE, FALSE}
@ATTRIBUTE play {yes, no}@DATA
sunny,85.0,85.0,FALSE,no
sunny,80.0,90.0,TRUE,no
%
%
%
如果is_first_call = False, 則iter_encode只編碼數據。
來自:http://www.rustle.us/?p=603>>> encoder = ArffEncoder();
>>> result = encoder.iter_encode(obj, is_first_call = False);
>>> for i in result:
. . . print i+u'\n',;
. . .
sunny,85.0,85.0,FALSE,no
sunny,80.0,90.0,TRUE,no
%
%
%